Given this this operator for evaluation of side effects in pipelines
let inline (|>!) a f = f a ; a
and this code snippet
if 1 = 1 then
"same"
else
"different"
|>! printfn "The numbers are %s."
|> printfn "Yes, they are %s."
This never prints The numbers are same but it does print Yes, they are same.
Why is the side effect operator |>! ignored here, but the |> taken into account, despite having the same indent?
Would I have to define the side effect operator differently?
written like this it works as expected.
if 1 = 1 then "same"
else "different"
|>! printfn "The numbers are %s."
|> printfn "Yes, they are %s."
Is it only unintuitive for me that the code actually behaves as if written
if 1 = 1 then
"same"
else
"different"
|>! printfn "The numbers are %s." // with indent here
|> printfn "Yes, they are %s."
Edit:
see also https://github.com/fsharp/fslang-suggestions/issues/806 for an answer.
This is not a bug, and it doesn't happen specifically for operators longer than two characters. This is a funny consequence of F#'s permissive offset rules.
When aligning lines of the same nestedness level, they have to be at the same indentation, like this:
let foo =
bar
baz
qux
But this is not allowed:
let foo =
bar
baz // Indented too much to the left
qux
And neither is this:
let foo =
bar
baz // Indented too little
qux
When dealing with constructs that create nested blocks, such as if/then, this rule is used to determine when the block is over: it's when the indentation alignment is broken.
let x =
if 1 = 1 then
bar
baz
qux
But there is an exception to this rule: if the line starts with an operator, then it is allowed to be shifted to the left by at most the operator size plus 1 characters, and it will still be considered to be at "current" indentation.
For example, this works:
let x =
1
+ 2
+ 3
But when you have operators of different sizes, it gets tricky. This works:
let x =
1
+ 2
+ 3
<> 6
But this doesn't:
let x =
1
+ 2
+ 3
<> 6
👆 this doesn't work because 2 and 3 are shifted to the left by more than the operator size plus one character.
So this is what happens in your case:
The first printfn is considered to be part of the else block, because it happens to align exactly with "different", but shifted to the left by operator size plus one.
The second printfn, however, is shifted to the left by more than operator size plus one, so it's no longer part of the else.
But it still happens to be correct syntax, because now it can be part of the surrounding block, with the whole result of if/then/else piped into it.
Normally you'd get a syntax error, like in my example with 1+2+3 <> 6 above, but in this case the syntax happens to align just right (or, perhaps, just wrong).
You can verify this by removing the extra space in front of the second printfn:
if 1 = 1 then
"same"
else
"different"
|>! printfn "The numbers are %s."
|> printfn "Yes, they are %s."
Now the second printfn will be part of the else, and you'll get an error: types string and unit don't match. This is because then returns a string, but else now returns a unit. This can be fixed by amending the then part:
if 1 = 1 then
()
else
"different"
|>! printfn "The numbers are %s."
|> printfn "Yes, they are %s."
Now this compiles and doesn't print anything. And if you replace 1 = 1 with 1 = 2, it will correctly print "different" twice.
Finally, if you want the whole if/then/else block to be piped through both printfn calls, you have to break the alignment of "different" with the first printfn somehow. One way to do it you have offered yourself: putting "different" on the same line with else. Another way would be to indent "different" a bit further, so it no longer aligns with the first printfn:
if 1 = 1 then
"same"
else
"different"
|>! printfn "The numbers are %s."
|> printfn "Yes, they are %s."
Related
Given this this operator for evaluation of side effects in pipelines
let inline (|>!) a f = f a ; a
and this code snippet
if 1 = 1 then
"same"
else
"different"
|>! printfn "The numbers are %s."
|> printfn "Yes, they are %s."
This never prints The numbers are same but it does print Yes, they are same.
Why is the side effect operator |>! ignored here, but the |> taken into account, despite having the same indent?
Would I have to define the side effect operator differently?
written like this it works as expected.
if 1 = 1 then "same"
else "different"
|>! printfn "The numbers are %s."
|> printfn "Yes, they are %s."
Is it only unintuitive for me that the code actually behaves as if written
if 1 = 1 then
"same"
else
"different"
|>! printfn "The numbers are %s." // with indent here
|> printfn "Yes, they are %s."
Edit:
see also https://github.com/fsharp/fslang-suggestions/issues/806 for an answer.
This is not a bug, and it doesn't happen specifically for operators longer than two characters. This is a funny consequence of F#'s permissive offset rules.
When aligning lines of the same nestedness level, they have to be at the same indentation, like this:
let foo =
bar
baz
qux
But this is not allowed:
let foo =
bar
baz // Indented too much to the left
qux
And neither is this:
let foo =
bar
baz // Indented too little
qux
When dealing with constructs that create nested blocks, such as if/then, this rule is used to determine when the block is over: it's when the indentation alignment is broken.
let x =
if 1 = 1 then
bar
baz
qux
But there is an exception to this rule: if the line starts with an operator, then it is allowed to be shifted to the left by at most the operator size plus 1 characters, and it will still be considered to be at "current" indentation.
For example, this works:
let x =
1
+ 2
+ 3
But when you have operators of different sizes, it gets tricky. This works:
let x =
1
+ 2
+ 3
<> 6
But this doesn't:
let x =
1
+ 2
+ 3
<> 6
👆 this doesn't work because 2 and 3 are shifted to the left by more than the operator size plus one character.
So this is what happens in your case:
The first printfn is considered to be part of the else block, because it happens to align exactly with "different", but shifted to the left by operator size plus one.
The second printfn, however, is shifted to the left by more than operator size plus one, so it's no longer part of the else.
But it still happens to be correct syntax, because now it can be part of the surrounding block, with the whole result of if/then/else piped into it.
Normally you'd get a syntax error, like in my example with 1+2+3 <> 6 above, but in this case the syntax happens to align just right (or, perhaps, just wrong).
You can verify this by removing the extra space in front of the second printfn:
if 1 = 1 then
"same"
else
"different"
|>! printfn "The numbers are %s."
|> printfn "Yes, they are %s."
Now the second printfn will be part of the else, and you'll get an error: types string and unit don't match. This is because then returns a string, but else now returns a unit. This can be fixed by amending the then part:
if 1 = 1 then
()
else
"different"
|>! printfn "The numbers are %s."
|> printfn "Yes, they are %s."
Now this compiles and doesn't print anything. And if you replace 1 = 1 with 1 = 2, it will correctly print "different" twice.
Finally, if you want the whole if/then/else block to be piped through both printfn calls, you have to break the alignment of "different" with the first printfn somehow. One way to do it you have offered yourself: putting "different" on the same line with else. Another way would be to indent "different" a bit further, so it no longer aligns with the first printfn:
if 1 = 1 then
"same"
else
"different"
|>! printfn "The numbers are %s."
|> printfn "Yes, they are %s."
F# uses the offside rule for ensuring correct indentation. If this rule isn't followed can it result in the incorrectness of the program or is it purely a styling guide?
For example given the code for pattern matching:
let f x = match x with
| 1 -> "pattern 1"
| 2 -> "pattern 2"
| _ -> "anything"
Note two of the lines are offside. I have ran the program by complying with the offside rule and not complying with it and I get the same output but just wanted another opinion.
Also what about for if statements?
let test x y =
if x = y
then "equals"
else "is greater than"
let x = test 1 2
printf "%s" x
Note the compiler does warn when the offside rule isn't not being complied with:
warning FS0058: Possible incorrect indentation: this token is offside of context started at position (1:13). Try indenting this token further or using standard formatting conventions.
The accepted answer will need to address the issue for both the pattern matching example and the if statement.
The behavior of Lightweight syntax is described in great detail in the F# spec, section 15.1.4 deals specifically with Offside lines. The description there is technical and concerned with lexical analysis for the F# parser implementation. Nonetheless, it has a couple of useful examples.
// The "|" markers in patterns must align.
// The first "|" should always be inserted.
let f () =
match 1+1 with
| 2 -> printf "ok"
| _ -> failwith "no!" <--syntax error
whereas in your example a warning is generated instead. So you should be able to trust the compiled program to work as intended.
For if/then/else/elif clauses a special dispensation is given in 15.1.9, while their branches are falling under permitted undentations, 15.1.10, so that this is possible:
let test x y =
if x = y
then
"equals"
else "is greater than"
Disclaimer/edit: this is a fairly simple question, but I'm asking it since I'm (still) often confused by evaluation order in F#, esp. with respect to newlines vs. spaces. Trial and error always gets me where I want to, but I don't think you can really understand a language if you have to resort to trial and error.
If I write:
let res x =
x * 1 = x
|> ignore
all's fine, but if I write:
let res x =
x * 1 = x && x * -1 = -x
|> ignore
then the compiler complains (it says it expects bool -> bool, not bool -> unit). I would have expected the newline to act as a separator here.
Adding parentheses helps, and putting it on one line shows that it is evaluated as (X && (Y |> Z)), where X and Y are boolean expressions and Z is any function.
Is this true? And is there a simpler way to find this out? Or better, when is whitespace a significant operator and when not?
To give another example:
let v = x |> fun x -> float x |> fun y -> true
Why is y here of type float and not of type int -> float? This may be obvious, and I sure have programmed thousands of lines that way, it even feels natural, but why?
If the only answer to give is "operator precedence and -> comes before |>", so be it. But I guess/hope there's some more academical or otherwise formal though behind all this (and I still find it odd that && has lower prio than |>, again, I don't understand why and in that case it feels counter-intuitive).
Looking at https://learn.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/symbol-and-operator-reference/#operator-precedence it seems that && has higher precedence than |> so your guess is correct and you need to add parenthesis:
let res x =
(x * 1 = x && x * -1 = -x)
|> ignore
It looks like a new line doesn't act as a separator unless it has a let binding or fall in one of the rules listed here
For example if you take this expression:
let res =
5 + 1
.GetType()
You also get an error, because it applies the . operator to 1 not to the whole expression, so the precedence rules still holds, regardless of the newline.
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.
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