Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# strange printfn problem

Tags:

f#

I was playing around with F# (Visual Studio 2010 beta 1), and I wrote a little console script that asked the user to input 2 numbers and an operator and then executed it. It works fine, apart from a tiny, but annoying thing: sometimes my printfn instructions are ignored. I placed breakpoints in the code to see that's indeed the case.

The code snippet:

let convert (source : string) =
    try System.Int32.Parse(source)
    with :? System.FormatException ->
        printfn "'%s' is not a number!" source;
        waitForExitKey();
        exit 1

let read =
    printfn "Please enter a number.";
    System.Console.ReadLine

let num1 : int = read() |> convert // the printfn in the read function is run...
let num2 : int = read() |> convert // ... but here is ignored

This is not the complete source of course, but I think that'll be enough. If you need the complete source just let me know.

So my question is pretty simple: what causes this issue with printfn? Am I doing something wrong?

Thanks in advance, ShdNx

like image 869
ShdNx Avatar asked Jun 24 '09 14:06

ShdNx


2 Answers

This page has a partial explanation of what's going on, but the short and sweet version is that F# will execute any value on declaration if it doesn't take parameters.

let read =
    printfn "Please enter a number."
    System.Console.ReadLine

Since read doesn't take any parameters, its executed immediately on declaration and binds the return value of the function to the identifier read.

Incidentally, your return value happens to be a function with the type (unit -> string). This results because F# automatically curries functions if they aren't passed all of their parameters. ReadLine expects one unit parameter, but since it isn't passed on, you actually bind read to the ReadLine function itself.

The solution is as follows:

let read() = // read takes one unit parameter
    printfn "Please enter a number."
    System.Console.ReadLine() // pass paramter to ReadLine method

Since read takes one parameter, its re-evaluated each time its called. Additionally, we're passing a parameter to ReadLine, otherwise we'll just return the ReadLine function as a value.

like image 167
Juliet Avatar answered Sep 29 '22 11:09

Juliet


I understand that this can be confusing. In your example, printfn runs earlier than you think. It will actually execute even without the call to read(), i.e., comment out the last two lines and you will still see a message being printed.

I think your intention is something like this:

let read() =
    printfn "Please enter a number.";
    System.Console.ReadLine()

This will create a "reusable" function instead of binding a function to an identifier as in your original example.

As a sidenote, the use of semi-colons here is optional so you can just write:

let read() =
    printfn "Please enter a number."
    System.Console.ReadLine()
like image 42
Ray Vernagus Avatar answered Sep 29 '22 13:09

Ray Vernagus