Reading input in F#
This article was originally published at tech.blinemedical.com
I’ve been playing with F# lately, much to the the chagrin of Sam, but I still think it’s fun as an excersize in thinking differently. I also find its terseness lets you prototype ideas quickly, encouraging you to experiment and tweak your code. However, I’m much more used to imperative programming, so when I started writing an F# program that needed user input I hit a small roadblock: how do I get input, validate, and ask for it again if I want to stay purely functional and leverage immutable values?
In an imperative language you might write something like this:
String path = null;
while(true)
{
path = Console.ReadLine();
if (File.Exists(path))
{
break;
}
Console.WriteLine("File doesn't exist");
}
You can’t declare path
inside the while loop or its loses its scope. If you need to use path
outside of the while loop, then it might seem like you have to let path be mutable. But, what if we did this:
private String GetPath()
{
while(true)
{
var path = Console.ReadLine();
if (File.Exists(path))
{
return path;
}
Console.WriteLine("File doesn't exist");
}
}
Now we don’t ever update any variables. We only ever use direct assignment. This sounds pretty functional to me. But, we still can’t directly translate into F#. Remembering that in F# the last statement is the return value, what does this return?
let falseItem =
while true do
false
This is actually an infinite loop; the while loop won’t ever return false
. In F#, a while loop can’t return from it’s body, since the body expression return type has to be of type unit. If you imagine the while loop as a function that takes a predicate and a lambda for the body then this makes sense. The whileLoop
function will execute the body as long as the predicate returns true. So, in psuedocode, it kind of looks like this
whileLoop(predicate, body) = {
while predicate() do {
body()
}
}
Now what? Well, turning this while loop into a recursive structure with immutable types is actually pretty easy:
let rec documentPath =
fun () -\>
Console.Write("File path: ")
let path = Console.ReadLine()
if not(File.Exists path) then
Console.WriteLine("File does not exist")
documentPath()
else path
The trick here is to define documentPath
as a recursive function. Either the function returns a valid path, or it calls itself executing the next “step” in our while loop. Also, since we don’t need to do any work after the recursive function call, F# can optimize this to use tail call optimization. The documentPath
variable is of type unit -> string
meaning it’s a function that takes a unit type and returns a string. To actually get the path, we execute documentPath()
, where ()
is the unit type.
Now we have a function that uses immutable types, but continuously reads in user input and won’t return until the input is valid.
Though, if you really want to use imperative style loop breaks, you can, but it’s not trivial.