12
\$\begingroup\$

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?

\$\endgroup\$
1
  • \$\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\$ Commented Sep 22, 2016 at 20:40

2 Answers 2

11
\$\begingroup\$

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 ()
}
\$\endgroup\$
4
  • \$\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
    Commented Jul 10, 2015 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\$ Commented Sep 22, 2016 at 17:34
  • \$\begingroup\$ @octopusgrabbus What does the error message say? \$\endgroup\$ Commented Sep 22, 2016 at 17:41
  • \$\begingroup\$ Must have been a weird alignment thing. The error is gone. Thanks for inquiring. \$\endgroup\$ Commented Sep 22, 2016 at 17:44
8
\$\begingroup\$

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
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Thanks for a very helpful alternative. I was also searching for a non-recursive function example using the Seq methods. \$\endgroup\$ Commented Sep 22, 2016 at 18:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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