A functional take on console program loop in F#

Often when learning a new technology I start with a simple console application in which the program is run in a loop it continues to prompt you for more input until you give some command like quit or exit or whatever you choose:

 Enter input: someInput
someOutput

Enter input: otherInput

someoutPut

Enter input: quit

Thanks! Come again!

The code for this is in an imperative language is usually something like:

    1: while(true)
    2: {
    3:     Console.Write("\nEnter input:");
    4:     var line = Console.ReadLine();
    5:     if(line == "quit") break;
    6:     
    7:     doSomething(line);
    8: }
    9:  
   10: Console.WriteLine("Thanks! Come Again");

 

While reading some F# samples, I saw some code doing essentially the same thing which looked something like:

    1: Console.Write "\nEnter input:"
    2: let mutable input = Console.ReadLine()
    3: while input <> "quit"
    4:     do
    5:     
    6:     if input <> "quit" 
    7:     then
    8:         doSomething input
    9:         Console.Write "\nEnter input:" 
   10:         input <- Console.ReadLine()

 

Now this just feels dirty! In a functional language explicit loop constructs like a while loop feel wrong.  I different way of doing this which is more functional is to create an infinite list of actions.  Where each action represents one iteration of any of the above loops. Then you just execute each action until some condition is met (like seeing the input “quit”).

    1: let action = fun _ ->
    2:     Console.Write "\nEnter input: "
    3:     Console.ReadLine()
    4:  
    5: let readlines = Seq.init_infinite (fun _ -> action())
    6:     
    7: let run item = if item = "quit" 
    8:                 then Some(item) 
    9:                 else
   10:                     parse item 
   11:                     None
   12:  
   13: Seq.first run readlines |> ignore
   14: Console.WriteLine "Thanks! Come Again"

This code makes use of the Seq.init_infinite which create a infinite sequence and Seq.first which enumerates the sequence until the passed in function returns Some.