Okay so i'm currently making mastermind in console in fsharp, and im trying to figure out how to ask the user if they want to play again.
let main() =
choosePuzzleMaker()
puzzleGuess()
c <- guess b [([],(0,0))]
while a <> c && d <> 8 do
c <- guess b [(c, validate a c)]
d <- d+1
if d <> 8 then
printfn "GZ! FUCKING MASTERMIND! You completed in %A turns and the code was %A" d a
else
printfn "That didn't go well...?"
printfn "Game Over!"
PlayAgain()
main()
where i tried defining PlayAgain() as:
let rec PlayAgain() =
printfn "Do you want to play again? Please type:
1: Yes
2: No\n"
match System.Console.ReadLine() with
| "1"|"yes"|"Yes" -> printfn "Alright!!!"
choosePuzzleMaker()
| "2"|"no"|"No" -> printfn "The game is over!"
| _ -> printfn "Invalid option! Please try again!"
(PlayAgain())
However, that didn't work so my quesiton is:
How would you make the console take a response yes/no and make the program begin again?
It looks like your problem is a simple indentation mistake. F#, like Python, defines code blocks by indentation. Let me show you an example:
// Some variables
let x = 5
let y = 3
let z = 1
let add1_wrong x =
printfn "Adding 1 to %d produces..." x
printfn "The wrong answer: %d" (x + 1) // Oops! This is wrong
let add1_correct x =
printfn "Adding 1 to %d produces..." x
printfn "The right answer: %d" (x + 1) // This is correct
add1_wrong x
add1_wrong y
add1_wrong z
add1_correct x
add1_correct y
add1_correct z
Try running that in F# Interactive and you'll get the following output:
The wrong answer: 6
Adding 1 to 5 produces...
Adding 1 to 3 produces...
Adding 1 to 1 produces...
Adding 1 to 5 produces...
The right answer: 6
Adding 1 to 3 produces...
The right answer: 4
Adding 1 to 1 produces...
The right answer: 2
Notice how "The wrong answer: 6" was printed right away, before you ever called the add1_wrong function? The way the code is written, it looks like the author intended to put the printfn "The wrong answer" line inside the add1_wrong function, but he made an indentation mistake and put it outside the function instead. So it gets run at the same time as the rest of the code that sets the x, y, and z variables and calls add1_wrong and add1_right.
If you don't yet understand what's going on in that sample code, stop reading now and keep reading it until you understand it. (Or ask a followup question if you still don't understand it after two or three readthroughs, because that means that I haven't explained it very well). It's important that you see the indentation mistake in my sample code before proceeding, because the code you posted has the same mistake in it. Actually, you have two indentation mistakes, but only one of them is causing the problem you've asked us about.
Here's your main() function, exactly as you typed it in this question, with the two indentation mistakes in it:
let main() =
choosePuzzleMaker()
puzzleGuess()
c <- guess b [([],(0,0))]
while a <> c && d <> 8 do
c <- guess b [(c, validate a c)]
d <- d+1
if d <> 8 then
printfn "GZ! FUCKING MASTERMIND! You completed in %A turns and the code was %A" d a
else
printfn "That didn't go well...?"
printfn "Game Over!"
PlayAgain()
main()
And now, here's the same function, with both indentation mistakes solved:
let main() =
choosePuzzleMaker()
puzzleGuess()
c <- guess b [([],(0,0))]
while a <> c && d <> 8 do
c <- guess b [(c, validate a c)]
d <- d+1
if d <> 8 then
printfn "GZ! FUCKING MASTERMIND! You completed in %A turns and the code was %A" d a
else
printfn "That didn't go well...?"
printfn "Game Over!"
PlayAgain()
main()
The c <- guess b [([],(0,0))] line has been indented to match the other lines, and the PlayAgain() call at the end has been indented to be inside your main() function, instead of outside it the way you originally wrote it.
This is what Mark Seemann meant when he wrote in the comments that "the program only calls PlayAgain once". The way you wrote it, the PlayAgain function is not called at the end of main(). Instead, you were calling it once, then calling main() once, and then exiting your program.
By the way, there are lots of other things that I (and other experienced F# programmers) would suggest doing differently in your code -- for example, the names a, b, c and d aren't good names, because they don't give you a clue about what's supposed to be in those names. I'd suggest renaming them as follows:
a should be called correctAnswer
b should be called... actually, I haven't a clue what b is. I know it's passed into the guess function, but I have no idea how it gets used. And that, by the way, is why it's a bad variable name. Even if I don't see any code that uses it, the name alone should give me a clue as to how it's intended to be used.
c should be called thisGuess
d should be called rounds or numberOfGuesses.
Hope that helps you figure out (and fix) your mistake. Let us know if you need further help.
UPDATE: To answer your latest comment, there's a simple solution to your problem, and a clever solution. I'll show you both, because the clever solution will teach you a very valuable programming technique that's used all the time in functional programming languages like F#.
First, the simple solution. In F#, if you need to have two functions that call each other, that's called mutual recursion, and there are two keywords that you'd use to support it: the rec and and keywords. It looks like this:
// Note that these two functions would form an infinite loop!
let rec f x =
g (x + 1)
and g x =
f (x * 2)
The rec keyword tells the F# compiler "The function I'm defining is going to be calling itself, directly or indirectly, at some point -- so please make its name available within the function itself." The and keyword creates a group of functions that all have their names available to each other.
So one way you could solve this is to do the following:
let rec PlayAgain() =
// ...
and main() =
// ...
That would work, but I recommend a second solution. One of the key ideas in functional programming is treating functions as "things" that you can manipulate. That is, you can store functions in lists or arrays, pass them as parameters to other functions, and so on. Which brings us to a very powerful technique for taking a function like your PlayAgain function, and making it more general and re-useable. If a function like PlayAgain has the general structure "Do some calculations or make a decision. Then, depending on what the results were, either do A or B next" -- then what you do is make A and B parameters of the function! In other words, you turn it from a function that takes no parameters into a function that takes one or two parameters, where the parameters are the "what to do next" functions. (Normally you'd take two parameters in a function that decides between two scenarios. But in the case of your PlayAgain function, one of the two "what to do next" steps is "do nothing", so it makes sense to have it take only one parameter). This is known as continuation-passing style -- "continuation" is the traditional functional-programming terminology for any "what to do next" step.
Here's what that would look like:
let rec PlayAgain whatToDoNext =
printfn "Do you want to play again? Please type:
1: Yes
2: No\n"
match System.Console.ReadLine() with
| "1"|"yes"|"Yes" -> printfn "Alright!!!"
whatToDoNext()
| "2"|"no"|"No" -> printfn "The game is over!"
| _ -> printfn "Invalid option! Please try again!"
(PlayAgain())
That's it! All I did was give PlayAgain a parameter, and then call that parameter in the appropriate place. Now we rewrite your main() function as follows (changing just the last line, and using let rec so that the name main will be available inside the main() function):
let rec main() =
choosePuzzleMaker()
puzzleGuess()
c <- guess b [([],(0,0))]
while a <> c && d <> 8 do
c <- guess b [(c, validate a c)]
d <- d+1
if d <> 8 then
printfn "GZ! FUCKING MASTERMIND! You completed in %A turns and the code was %A" d a
else
printfn "That didn't go well...?"
printfn "Game Over!"
PlayAgain main
And with that, you've avoided the use of and, and you've discovered a powerful new programming technique. I strongly recommend that F# beginners avoid using the and keyword if possible, because it tends to add unnecessary complication to reading the code later. And, as in this case, it can often be avoided by simply making the "what to do next" step a parameter, which also means that the PlayAgain function will be more easily reused as-is in later programs.
Related
So, I'm an experienced OOP programmer (primarily C++), just now starting to dip my toes in with functional programming. From my understanding, in a purely functional paradigm, functions shouldn't have conditionals and should be broken down as much as possible using currying. Could someone provide me the "pure" functional version of the following example? Preferably using every strict technique that would be part of the functional paradigm:
let rec greatestCommonFactor a b =
if a = 0 then b
elif a < b then greatestCommonFactor a (b - a)
else greatestCommonFactor (a - b) b
The example function that you have supplied is already purely functional. When we talk about a function purity, what we are actually talking about is the property of functions being referentially transparent.
An expression is referentially transparent if it can be replaced with its value without altering the effect of the program. To give a simple example, imagine the function:
let add2 x = x + 2
Now, anywhere that the value add2 2 appears in our program, we can substitute the value 4 without altering the behaviour of the program.
Imagine now that we add some additional behaviour into the function that prints to the console:
let add2Print x =
printfn "%d" x
x + 2
Although the result of the function is the same as before, we can no longer perform value substitution with the value 4 without changing the behaviour of our program because our function has the additional side-effect of printing to the console.
This function is no longer referentially transparent and therefore not a pure function.
let rec greatestCommonFactor a b =
if a = 0 then b
elif a < b then greatestCommonFactor a (b - a)
else greatestCommonFactor (a - b) b
Looking at this function that you have supplied, no side-effects are involved in its execution. We will always get the same output value for given inputs a and b, therefore this is already a pure function.
To be clear, there is absolutely no issue with functions containing conditionals in functional programming. Often, however, we make use of pattern matching rather than if/elif/else expressions but in the example you have described, this is purely stylistic. An alternative expression of your function using pattern matching would be:
let rec greatestCommonFactor a b =
match a with
|0 -> b
|a' when a' < b -> greatestCommonFactor a' (b - a')
|a' -> greatestCommonFactor (a' - b) b
I'm trying to do the Tower of Hanoi but I don't know how to add a count incrementer. Here's my code:
open System
let disks = Int32.Parse(Console.ReadLine())
let rec hanoi num start finish =
match num with
| 0 -> [ ]
| _ -> let temp = (6 - start - finish)
(hanoi (num-1) start temp) # [ start, finish ] # (hanoi (num-1) temp finish)
[<EntryPoint>]
let main args =
(hanoi disks 1 2) |> List.iter (fun pair -> match pair with
| a, b -> printf ": %A %A\n" a b)
0
I'm trying to get it to print out something like this
1: 1 3
2: 1 2
3: 3 2
etc...
I'm aware that there is no formatting set for the
1:
2:
3:
part. I know that the proper formatting is
"%A: %A %A\n" *with some counter here* a b
however I don't know how to do this. I've looked for an answer on the web but I have not found anything. If anyone could please help me, that would be greatly appreciated.
Thank you in advance
s952163's comment is the correct answer here, but here's a bit more explanation to go with it.
List.iteri looks very similar to List.iter, except your function will then have two arguments - the counter and the list element. Here, that would look like
hanoi disks 1 2 |> List.iteri (fun i (a, b) -> printfn "%d: %d %d" i a b)
note: I've also included a couple of ways to simplify that line of code, by
removing the unnecessary brackets around the hanoi function - the pipe operator |> has very low precedence, so brackets are not usually required to separate its arguments
using printfn instead of printf "...\n" - the former is preferred because it will use the correct form of the line ending. On Windows, this is actually "\r\n" (though when you're writing to the console, it doesn't matter)
removing the pattern match from the lambda function - you're not actually pattern matching, because the tuple (a, b) is a type itself. You can get the arguments directly in the function call, and save yourself some typing.
I'm starting out to learn Haskell. Even though I'm a dunce extraordinaire, I am intent on making this work. The error I received is listed as the title. This is the code that I wrote to try to implement the behavior of replicating a list (n) times and concatenating its new length as a new list. Now I have a basic understanding of how parsing works in Haskell, below my original code I will give example of some modified code to see if my understanding on parsing is adequate. My question for now is how I can properly indent or structure my block in order to not receive this error (is that specific enough :O) -- is there a piece of information I'm missing when it comes to creating instances and formatting? PLEASE DO NOT TELL ME OR OFFER SUGGESTIONS IF YOU NOTICE THAT MY CURRENT INSTANCE OR MAIN FUNCTION ARE SYNTACTICALLY WRONG. I want to figure it out and will deal with that GHC error when I get to it. (I hope that's the proper way to learn). BUT if I could ask for anyone's help in getting past this first obstacle in understanding proper formatting, I'd be grateful.
module Main where
import Data.List
n :: Int
x :: [Char]
instance Data stutter n x where
x = []
n = replicate >>= x : (n:xs)
stutter >>= main = concat [x:xs]
let stutter 6 "Iwannabehere" -- <-- parse error occurs here!!!
--Modified code with appropriate brackets, at least where I think they go.
module Main where
import Data.List
n :: Int
x :: [Char]
instance Data stutter n x where{
;x = []
;n = replicate >>= x : (n:xs)
;stutter >>= main = concat [x:xs]
;
};let stutter 6 "Iwannabehere" -- there should be no bracket of any kind at the end of this
I placed the 'let' expression on the outside of the block, I don't believe it goes inside and I also receive a parsing error if I do that. Not correct but I thought I'd ask anyway.
I'm not sure what the instance Data stutter n x is supposed to be, the instance XYZ where syntax is used solely for typeclasses, but you have a couple syntax errors here.
First of all, while GHC says that the error is on let stutter 6 "Iwannabehere", your first error occurs before that with stutter >>= main = concat [x:xs]. A single = sign is reserved for assignments, which are merely definitions. You can have assignments at the top level, inside a where block, or inside a let block (the where includes typeclass instance definitions). You can't have an assignment be part of an expression like x >>= y = z.
Your next syntax error is the let itself. let blocks can not appear at the top level, they only appear within another definition. You use let in GHCi but the reasons for that are outside the scope of this answer. Suffice to say that entering expression in GHCi is not equivalent to the top level of a source file.
Next, if you were to use a let block somewhere, it can only contain definitions. The syntax looks more like
let <name> [<args>] = <definition>
[<name> [<args>] = <definition>]
in <expression>
And this whole block makes an expression. For example, you could write
def f(x, y, z):
w = x + y + z
u = x - y - z
return w * u
in Python, and this would be equivalent to the Haskell function definition
f x y z = let w = x + y + z
u = x - y - z
in w * u
It only defines local variables. There is another form when you're using it inside do blocks where you can exclude the in <expression> part, such as
main = do
name <- getLine
let message = if length name > 5 then "short name" else "long name"
goodbye n = putStrLn ("Goodbye, " ++ n)
putStrLn message
goodbye name
Note that there is no need to use in here. You can if you want, it just means you have to start a new do block:
main = do
name <- getLine
let message = ...
goodbye n = ...
in do
putStrLn message
goodbye name
And this isn't as pretty.
Hopefully this points you more towards correct syntax, but it looks like you have some misunderstandings about how Haskell works. Have you looked at Learn You a Haskell? It's a pretty gentle and fun introduction to the language that can really help you learn the syntax and core ideas.
Your parse error is from the let keyword. Remove it and no error related to that will occur. let x = y is only relevant in GHCi and do-blocks, neither of which is relevant at this point. Essentially, just replace it with this line:
theWordIGet = stutter 6 "Iwannabehere"
Secondly, instance keyword in Haskell has absolutley nothing to do with what you want to do at this stage. This is not how Haskell functions are defined, which is what I'm guessing you want to do. This is what you're wanting to do to create a stutter function, assuming it simply repeats a string n times.
stutter :: Int -> String -> String
stutter n x = concat (replicate n x)
You'll also want to remove the type declarations for the (out-of-scope) values n and x: they're not objects, they're arguments for a function, which has its own signature determining the types of n and x within a function call.
Lastly, I imagine you will want to print the value of stutter 6 "Iwannabehere" when the program is executed. To do that, just add this:
main :: IO ()
main = print (stutter 6 "Iwannabehere")
In conclusion, I implore you to start from scratch and read 'Learn You a Haskell' online here, because you're going off in entirely the wrong direction - the program you've quoted is a jumble of expressions that could have a meaning, but are in the wrong place entirely. The book will show you the syntax of Haskell much better that I can write about in this one answer, and will explain fully how to make your program behave in the way you expect.
I' working on a basic 2D CAD engine and the pipeline operator significantly improved my code. Basically several functions start with a point (x,y) in space and compute a final position after a number of move operations:
let finalPosition =
startingPosition
|> moveByLengthAndAngle x1 a1
|> moveByXandY x2 y2
|> moveByXandAngle x3 a3
|> moveByLengthAndAngle x4 a4
// etc...
This is incredibly easy to read and I'd like to keep it that way. The various x1, a1, etc. obviously have a meaning name in the real code.
Now the new requirement is to introduce exception handling. A big try/with around the whole operation chain is not enough because I'd like to know which line caused the exception. I need to know which argument is invalid, so that the user knows what parameter must be changed.
For example if the first line (moveByLengthAndAngle x1 a1) raises an exception, I'd like to tell something like "Hey, -90 is an invalid value for a1! a1 must be between 45 and 90!". Given that many operations of the same type can be used in the sequence it's not enough to define a different exception type for each operation (in this example I wouldn't be able to tell if the error was the first or the last move).
The obvious solution would be to split the chain in single let statements, each within its respective try/with. This however would make my beautiful and readable code a bit messy, not so readable anymore.
Is there a way to satisfy this requirement without sacrificing the readability and elegance of the current code?
(note. right now every moveBy function raises an exception in case of errors, but I'm free to change for ex. to return an option, a bigger tuple, or just anything else if needed).
The solution that Rick described is only going to handle exceptions that are raised when evaluating the arguments of the functions in the pipeline. However, it will not handle the exceptions that are raised by the pipelined functions (as described in answer to your other question).
For example, let's say you have these simple functions:
let times2 n = n * 2
let plus a b = a + b
let fail n = failwith "inside fail"
10 // This will handle exception that happens when evaluating arguments
|> try plus (failwith "evaluating args") with _ -> 0
|> times2
|> try fail with _ -> 0 // This will not handle the exception from 'fail'!
To solve this, you can write a function that wraps any other function in an exception handler. The idea that your protect function will take a function (such as times2 or fail) and will return a new function that takes the input from the pipeline (number) and passes it to the function (times2 or fail), but will do this inside exception handler:
let protect msg f =
fun n ->
try
f n
with _ ->
// Report error and return 0 to the pipeline (do something smarter here!)
printfn "Error %s" msg
0
Now you can protect each function in the pipeline and it will also handle exceptions that happen when evaluating these functions:
let n =
10 |> protect "Times" times2
|> protect "Fail" fail
|> protect "Plus" (plus 5)
How about folding over Choices? Let's say that instead of pipelining the actions, you represent them like this:
let startingPosition = 0. ,0.
let moveByLengthAndAngle l a (x,y) = x,y // too lazy to do the math
let moveByXandY dx dy (x,y) =
//failwith "oops"
x+dx, y+dy
let moveByXandAngle dx a (x,y) = x+dx, y
let actions =
[
moveByLengthAndAngle 0. 0., "failed first moveByLengthAndAngle"
moveByXandY 1. 2., "failed moveByXandY"
moveByXandY 3. 4., "failed moveByXandY"
moveByXandAngle 3. 4., "failed moveByXandAngle"
moveByLengthAndAngle 4. 5., "failed second moveByLengthAndAngle"
]
i.e. actions is of type ((float * float -> float * float) * string) list.
Now, using FSharpx we lift the actions to Choice and fold/bind (not sure how to call it this is similar to foldM in Haskell) over the actions:
let folder position (f,message) =
Choice.bind (Choice.protect f >> Choice.mapSecond (konst message)) position
let finalPosition = List.fold folder (Choice1Of2 startingPosition) actions
finalPosition is of type Choice<float * float, string> , i.e. it's either the final result of all those functions, or an error (as defined in the table above).
Explanation for this last snippet:
Choice.protect is similar to Tomas' protect, except that when it finds an exception, it returns the exception wrapped in a Choice2Of2. When there's no exception, it returns the result wrapped in a Choice1Of2.
Choice.mapSecond changes this potential exception in Choice2Of2 with the error message defined in the table of actions. Instead of (konst message) this could also be a function that builds the error message using the exception.
Choice.bind runs this "protected" action against the current position. It will not run the actual action if the current position is in error (i.e. a Choice2Of2).
Finally, the fold applies all actions threading along / accumulating the resulting Choice (either the current position or an error).
So now we just have to pattern match to handle each case (correct result or error):
match finalPosition with
| Choice1Of2 (x,y) ->
printfn "final position: %f,%f" x y
| Choice2Of2 error ->
printfn "error: %s" error
If you uncomment failwith "oops" above, finalPosition will be a Choice2Of2 "failed moveByXandY"
There's a lot of ways to approach this, the easiest would be to just wrap each call in a try-with block:
let finalPosition =
startingPosition
|> (fun p -> try moveByLengthAndAngle x1 a1 p with ex -> failwith "failed moveByLengthAndAngle")
|> (fun p -> try moveByXandY x2 y2 p with ex -> failwith "failed moveByXandY")
|> (fun p -> try moveByXandAngle x3 a3 p with ex -> failwith "failed moveByXandAngle")
|> (fun p -> try moveByLengthAndAngle x4 a4 p with ex -> failwith "failed moveByLengthAndAngle")
// etc...
Behold the power of expression oriented programming :).
Unfortunately, if you're pipelining over a sequence it becomes much more difficult as:
What happens in the pipeline (for Seqs) is composition, not execution.
Exception handling inside an IEnumerable is undefined and so depends on the implementation of the Enumerator.
The only safe way is to make sure the internals of each sequence operation are wrapped.
Edit: Wow, I can't believe I messed that up. It's fixed now but I do think that the two other solutions are cleaner.
I am unclear why
Now the new requirement is to introduce exception handling. A big
try/with around the whole operation chain is not enough because I'd
like to know which line caused the exception. I need to know which
argument is invalid, so that the user knows what parameter must be
changed.
the debugger isn't sufficient for this. This sounds like a design-time bug in the user's code; each of these methods might throw ArgumentException and nothing would handle it (it would crash the app), and the programmer would debug and see the method/stack that threw the exception, and the exception text would have the argument name.
(Or maybe this is FSI/scripting typically?)
Why not just put the exception handling in the function calls and throw them. Wouldn't this break the code. Then in your function that calls this, catch the error and display to user.
I'm trying to learn F# by going through some of the Euler problems and I found an issue I haven't been able to figure out. This is my naive solution.
let compute =
let mutable f = false
let mutable nr = 0
while f = false do
nr <- nr + 20
f = checkMod nr
nr
When i do this I get the error message warning FS0020: This expression should have type 'unit', but has type 'bool' on the expression "nr <- nr +20". I've tried rewriting and moving the expressions around and I always get that error on the line below the while statement.
I'm writing this using VS2010 Beta.
Since I can imagine this weg page becoming the 'canonical' place to look up information about warning FS0020, here's my quick summary of the three commonest cases in which you get the warning, and how to fix them.
Intentionally discarding the result of a function that is called only for its side-effects:
// you are calling a function for its side-effects, intend to ignore result
let Example1Orig() =
let sb = new System.Text.StringBuilder()
sb.Append("hi") // warning FS0020
sb.Append(" there") // warning FS0020
sb.ToString()
let Example1Fixed() =
let sb = new System.Text.StringBuilder()
sb.Append("hi") |> ignore
sb.Append(" there") |> ignore
sb.ToString()
Warning is useful, pointing out an error (function has no effects):
// the warning is telling you useful info
// (e.g. function does not have an effect, rather returns a value)
let Example2Orig() =
let l = [1;2;3]
List.map (fun x -> x * 2) l // warning FS0020
printfn "doubled list is %A" l
let Example2Fixed() =
let l = [1;2;3]
let result = List.map (fun x -> x * 2) l
printfn "doubled list is %A" result
Confusing assignment operator and equality comparison operator:
// '=' versus '<-'
let Example3Orig() =
let mutable x = 3
x = x + 1 // warning FS0020
printfn "%d" x
let Example3Fixed() =
let mutable x = 3
x <- x + 1
printfn "%d" x
The following line:
f = checkMod nr
is an equality check, not an assignment as I believe you are intending. Change it to:
f <- checkMod nr
and all should work fine. I'm not sure why you've used the correct syntax on the previous line and not that line...
Also, the line while f = false do should really be simplified to while not f do; equality checks on booleans are rather convoluted.
As I side note, I feel a need to point out that you are effectively trying to use F# as an imperative language. Use of mutable variables and while loops are strongly discouraged in functional languages (including F#), especially when a purely functional (and simpler) solution exists, as in this situation. I recommend you read up a bit on programming in the functional style. Of course, just getting to grips with the syntax is a useful thing in itself.
If you're trying to adopt the functional style, try to avoid mutable values.
For example like this:
let nr =
let rec compute nr =
if checkMod nr then nr else compute (nr + 20)
compute 0
while expressions in F# take a little getting used to if you're coming from an imperative language. Each line in a while expression must evaluate to unit (think void from C++/C#). The overall expression then also evaluates to unit.
In the example:
nr <- nr + 20
evaluates to unit whereas
f = checkMod nr
evaluates to a bool as Noldorin noted. This results in a warning message being reported. You can actually turn the warning off if you so desire. Just put the following at the top of your file:
#nowarn "0020"
I've been programming in an imperative style for a long time, so getting used to the functional programming mindset took a while.
In your example, you're trying to find the first multiple of 20 that passes your checkMod test. That's the what part. For the functional how part, I recommend browsing through the methods available to sequences. What you need is the first element of a sequence (multiples of 20) passing your test, like this:
let multi20 = Seq.initInfinite (fun i -> i*20)
let compute = multi20 |> Seq.find checkMod
The first let generates an infinite list of twentyples (I made that one up). The second let finds the first number in said list that passes your test. Your task is to make sure that there actually is a number that will pass the test, but that's of course also true for the imperative code.
If you want to condense the two above lines into one, you can also write
let computeCryptic = Seq.initInfinite ((*) 20) |> Seq.find checkMod
but I find that pulling stunts like that in code can lead to headaches when trying to read it a few weeks later.
In the same spirit as Brian's post, here is another way to get warning FS0020: In a nutshell, I accidentally tupled the function arguments.
Being an F# newbie, I had a difficult time debugging the code below, which for the second line (let gdp...) gave the warning FS0020: This expression should have type 'unit', but has type '(string -> ^a -> unit) * string * float'. It turns out that line was not the problem at all; instead, it was the printfn line that was messed up. Removing the comma separators from the argument list fixed it.
for country in wb.Regions.``Arab World``.Countries do
let gdp = country.Indicators.``GDP per capita (current US$)``.[2010]
let gdpThous = gdp / 1.0e3
printfn "%s, %s (%.2f)" country.Name, country.CapitalCity, gdpThous