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
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.
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()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With