Reading input from the console in F# In the process of learning F#, I wrote this code that reads lines of input from the console and stores them in a list.

As far as I can tell the code works correctly, but since I'm new to functional programming, I'm not sure if it is written as well as it could have been.


Should I have read characters iteratively instead of recursively?
Is there a better alternative to using a StringBuilder object?
Does my code follow proper functional style?




open System

/// Appends a character to the string builder and returns the new builder.
/// Characters 10 and 13 (newline and carriage return) are ignored.
/// Returns the updated StringBuilder.
let appendChar char (builder : Text.StringBuilder) =
    match char with
    | 10 | 13 -&gt; builder
    | n -&gt;
        let c = Convert.ToChar(n)
        builder.Append(c)

/// Finishes building a string by clearing the StringBuilder
/// and appending the string to the end of the list of strings.
/// Empty strings are ignored.
/// Returns a tuple containing the lines and the new StringBuilder.
let finishString lines (builder : Text.StringBuilder) = 
    let s = builder.ToString()
    let l = builder.Length
    let newBuilder = builder.Clear()
    match l with
    | 0 -&gt; (lines, newBuilder)
    | _ -&gt; (lines @ [s], newBuilder)

/// Handles a character by appending it to the builder and taking an appropriate action.
/// If char is a newline, finish the string.
/// Returns a tuple containing lines and the new builder.
let handleChar char lines builder =
    let newBuilder = appendChar char builder
    match char with
    | 10 -&gt; finishString lines newBuilder
    | c -&gt; (lines, newBuilder)

/// Gets all the lines from standard input until end of input (Ctrl-Z).
/// Empty lines are ignored.
/// Returns a list of strings read.
let rec getLines lines builder =
    match Console.Read() with
    | -1 -&gt; lines
    | c -&gt;
        let tuple = handleChar c lines builder
        let newLines = fst tuple
        let newbuilder = snd tuple
        getLines newLines newbuilder

[&lt;EntryPoint&gt;]
let main argv = 
    Text.StringBuilder()
    |&gt; getLines []
    |&gt; ... and so on
Some small comments

    let tuple = handleChar c lines builder
    let newLines = fst tuple
    let newbuilder = snd tuple
    getLines newLines newbuilder


can become

    let newlines,newbuilder = handleChar c lines builder
    getLines newLines newbuilder


although, if you aren't using getLines anywhere else, there is a good argument for making it

handleChar c lines builder |&gt; getLines


by makeing getLines take its argument in tupled form.

The magic number 10 appears a few times, so I would add a literal like

[&lt;Literal&gt;]
let newline = 10


and then you can pattern match against it.

Also,

Lines @ [s]


is bad as @ is very slow.  You are better to use

s::Lines


and then reverse the list at the end.

Your format for reading the characters recursively should be fine as it will get optimised as a tail-call.

Of course, your entire program could be replaced by:

let rec procinput lines= 
    match Console.ReadLine() with
    | null -&gt; List.rev lines
    | "" -&gt; procinput lines
    | s -&gt; procinput (s::lines)