Consuming input line-by-line until the end is often useful. In C# I would write the following loop:

while ((line = Console.ReadLine()) != null) {
    // ...

I wanted to do this using "idiomatic" F# (which to me includes avoiding let mutable) and I came up with this:

let rec readlines = seq {
    let line = Console.ReadLine()
    if line <> null then
        yield line
        yield! readlines

The last line shows the following warning

Warning FS0040: This and other recursive references to the object(s) being defined will be checked for initialization-soundness at runtime through the use of a delayed reference. This is because you are defining one or more recursive objects, rather than recursive functions. This warning may be suppressed by using '#nowarn "40"' or '--nowarn:40'.

The warning would suggest I am actually not writing idiomatic F# (as warnings usually point out problematic code). Is there a better way to write the function that avoids the warnings?

  • \$\begingroup\$ How are you invoking this function? The rules for code review don't allow me to post my version of this with the () addition in @MarkSeeman's answer. \$\endgroup\$ – octopusgrabbus Sep 22 '16 at 20:40

What you have there is a value, but you probably want it to be a function:

let rec readlines () = seq {
    let line = Console.ReadLine()
    if line <> null then
        yield line
        yield! readlines ()
  • \$\begingroup\$ was this meant to be yield line instead of yield v? There is a pending edit on this post by another user making the change to yield line and I want to make sure before accepting it. \$\endgroup\$ – Dan Pantry Jul 10 '15 at 16:59
  • \$\begingroup\$ In Visual Studio 2012, I'm getting a type constraint mismatch on the yield! readlines () line. Any thoughts as to why? \$\endgroup\$ – octopusgrabbus Sep 22 '16 at 17:34
  • \$\begingroup\$ @octopusgrabbus What does the error message say? \$\endgroup\$ – Mark Seemann Sep 22 '16 at 17:41
  • \$\begingroup\$ Must have been a weird alignment thing. The error is gone. Thanks for inquiring. \$\endgroup\$ – octopusgrabbus Sep 22 '16 at 17:44

Mark's response does answer as to the warning you received.

An alternate approach could be

Seq.initInfinite // (1)
    (fun _ -> Console.ReadLine())
|>  Seq.takeWhile (fun line -> line <> null) // (2)

Edit: Could be restructured and more succinct as

 fun _ -> Console.ReadLine()
 |>  Seq.initInfinite // (1)
 |>  Seq.takeWhile ((<>) null) // (2) (3)
  1. Seq.initInfinite<'T> Function (F#)
  2. Seq.takeWhile<'T> Function F#
  3. Partial Application
  • \$\begingroup\$ Thanks for a very helpful alternative. I was also searching for a non-recursive function example using the Seq methods. \$\endgroup\$ – octopusgrabbus Sep 22 '16 at 18:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.