Microsoft F# manual says that verbose syntax is always enabled in F#. If I understand that correctly, it means that F# code, written using verbose syntax, has to produce the same result whether lightweight syntax is enabled or not.
However, the code snippet below prints the number 6 ten times
#light "off"
let f x = for i = 1 to 10 do printfn "%d" x done in f 6
while the following generates the error: Unexpected keyword 'in' in binding. Expected incomplete structured construct at or before this point or other token.
let f x = for i = 1 to 10 do printfn "%d" x done in f 6
What is the structural difference between these two snippets and why is the error generated when lightweight syntax is enabled?
This verbose code:
#light "off"
let f x = for i = 1 to 10 do printfn "%d" x done in f 6
is equivalent to this lightweight code:
let f x =
for i = 1 to 10 do
printfn "%d" x
f 6
in the lightweight mode the indentation defines the blocks, in verbose mode do ... done is the inner block of the for and let ... in ... is the block of the expression f 6.
Regarding why it complains when lightweight is on there are 2 possibilities:
It could be a bug in the parser. Very few people use verbose and it may have gone unnoticed.
It's also possible that both versions are no fully compatible. The documentation does say:
even if you enable lightweight syntax, you can still use verbose
syntax for some constructs.
Notice how it says "some" and not "all".
Related
I'm creating a tiny function that will generate an alphanumeric string randomly like so
open System
let randomStr (len : int) : string =
let rand = new Random()
let ch = Array.concat [| [|'a' .. 'z'|];[|'A' .. 'Z'|];[|'0' .. '9'|] |]
let sz = Array.length ch in
System.String(Array.init len (fun _ -> ch.[rand.Next sz]))
let e = randomStr 5
printf "%s" e
I seem to keep receiving the following error and I have no clue as to why
error FS0010: Unexpected keyword 'let' or 'use' in expression. Expected 'in' or other token.
I suspect you have lightweight syntax turned off via #light "off". I've reproduced the same error outside of Visual Studio here. Try removing the #light directive. There might also be some setting in VS2017 that disables lightweight syntax, but I don't have a copy handy that I can try. Lightweight syntax is now considered the norm in F#, so there's rarely any need for the old verbose style.
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"
When quoting
<# 1 + 1 #>
I want "1 + 1"
instead of
"Call (None, Int32 op_Addition[Int32,Int32,Int32](Int32, Int32),
[Value (1), Value (1)])"
You'll have to write it yourself. See the F# quotations visualizer code as a guide for transforming the quotations abstract syntax tree.
I have implemented a quotation decompiler as part of a larger open source project Unquote. It can decompile many simple F# quoted expressions as single-line non-light syntax strings (see the project's home page for a list of decompiler features). For example,
> decompile <# (11 + 3) / 2 = String.length ("hello world".Substring(4, 5)) #>;;
val it : string =
"(11 + 3) / 2 = String.length ("hello world".Substring(4, 5))"
#Kurt Schelfthout is correct about the many challenges faced when decompiling F# Quotations into human readable form. But from my work so far, I believe that it is possible to write a quotation decompiler which can generate correct F# code. Take match expressions and computation expressions for example, the Unquote decompiler can produce correct F# code in the following simple cases:
> decompile <# match true with | true -> "hi" | _ -> "bye" #>;;
val it : string =
"let matchValue = true in if matchValue then "hi" else "bye""
> decompile <# seq {yield 1; yield 2} #>;;
val it : string =
"seq (Seq.delay (fun unitVar -> Seq.append (Seq.singleton 1) (Seq.delay (fun unitVar -> Seq.singleton 2))))"
Infix and prefix operators are not too hard (as you can see in the first example), but source structure such as new lines and indentation is an interesting topic (though not terribly difficult, I think). However, single-line non-light syntax is sufficient for Unquote's requirements.
There is none, and it's not quite that easy, except in very simple cases. One of the main problems, for example, is the match construct. It is syntactic sugar for a whole bunch of if and switch statements (try printing a quotation with a match in, you'll see). Another one of those biggies are computation expressions, but I guess you could skip those at first.
Then there is a the rabbit hole of ambiguities you'll have to resolve, with conventions like the pipe operator starts a new line, let starts a new line, indentation, infix, prefix, special cases like the (::) operator and so forth.
All in all, doable, but not trivial. Sort of like decompiling.
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
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 10 years ago.
Looks like here in StackOveflow there is a group of F# enthusiasts.
I'd like to know better this language, so, apart from the functional programming theory, can you point me to the better starting points to start using the F# language? I mean, tutorials, how-tos, but first of all working samples to have the chance to start doing something and enjoy the language.
Thanks a lot
Andrea
Not to whore myself horribly but I wrote a couple F# overview posts on my blog here and here. Chris Smith (guy on the F# team at MS) has an article called 'F# in 20 minutes' - part 1 and part 2.
Note you have to be careful as the latest CTP of F# (version 1.9.6.0) has some seriously breaking changes compared to previous versions, so some examples/tutorials out there might not work without modification.
Here's a quick run-down of some cool stuff, maybe I can give you a few hints here myself which are clearly very brief and probably not great but hopefully gives you something to play with!:-
First note - most examples on the internet will assume 'lightweight syntax' is turned on. To achieve this use the following line of code:-
#light
This prevents you from having to insert certain keywords that are present for OCaml compatibility and also having to terminate each line with semicolons. Note that using this syntax means indentation defines scope. This will become clear in later examples, all of which rely on lightweight syntax being switched on.
If you're using the interactive mode you have to terminate all statements with double semi-colons, for example:-
> #light;;
> let f x y = x + y;;
val f : int -> int -> int
> f 1 2;;
val it : int = 3
Note that interactive mode returns a 'val' result after each line. This gives important information about the definitions we are making, for example 'val f : int -> int -> int' indicates that a function which takes two ints returns an int.
Note that only in interactive do we need to terminate lines with semi-colons, when actually defining F# code we are free of that :-)
You define functions using the 'let' keyword. This is probably the most important keyword in all of F# and you'll be using it a lot. For example:-
let sumStuff x y = x + y
let sumStuffTuple (x, y) = x + y
We can call these functions thus:-
sumStuff 1 2
3
sumStuffTuple (1, 2)
3
Note there are two different ways of defining functions here - you can either separate parameters by whitespace or specify parameters in 'tuples' (i.e. values in parentheses separated by commas). The difference is that we can use 'partial function application' to obtain functions which take less than the required parameters using the first approach, and not with the second. E.g.:-
let sumStuff1 = sumStuff 1
sumStuff 2
3
Note we are obtaining a function from the expression 'sumStuff 1'. When we can pass around functions just as easily as data that is referred to as the language having 'first class functions', this is a fundamental part of any functional language such as F#.
Pattern matching is pretty darn cool, it's basically like a switch statement on steroids (yeah I nicked that phrase from another F#-ist :-). You can do stuff like:-
let someThing x =
match x with
| 0 -> "zero"
| 1 -> "one"
| 2 -> "two"
| x when x < 0 -> "negative = " + x.ToString()
| _ when x%2 = 0 -> "greater than two but even"
| _ -> "greater than two but odd"
Note we use the '_' symbol when we want to match on something but the expression we are returning does not depend on the input.
We can abbreviate pattern matching using if, elif, and else statements as required:-
let negEvenOdd x = if x < 0 then "neg" elif x % 2 = 0 then "even" else "odd"
F# lists (which are implemented as linked lists underneath) can be manipulated thus:-
let l1 = [1;2;3]
l1.[0]
1
let l2 = [1 .. 10]
List.length l2
10
let squares = [for i in 1..10 -> i * i]
squares
[1; 4; 9; 16; 25; 36; 49; 64; 81; 100]
let square x = x * x;;
let squares2 = List.map square [1..10]
squares2
[1; 4; 9; 16; 25; 36; 49; 64; 81; 100]
let evenSquares = List.filter (fun x -> x % 2 = 0) squares
evenSqares
[4; 16; 36; 64; 100]
Note the List.map function 'maps' the square function on to the list from 1 to 10, i.e. applies the function to each element. List.filter 'filters' a list by only returning values in the list that pass the predicate function provided. Also note the 'fun x -> f' syntax - this is the F# lambda.
Note that throughout we have not defined any types - the F# compiler/interpreter 'infers' types, i.e. works out what you want from usage. For example:-
let f x = "hi " + x
Here the compiler/interpreter will determine x is a string since you're performing an operation which requires x to be a string. It also determines the return type will be string as well.
When there is ambiguity the compiler makes assumptions, for example:-
let f x y = x + y
Here x and y could be a number of types, but the compiler defaults to int. If you want to define types you can using type annotation:-
let f (x:string) y = x + y
Also note that we have had to enclose x:string in parentheses, we often have to do this to separate parts of a function definition.
Two really useful and heavily used operators in F# are the pipe forward and function composition operators |> and >> respectively.
We define |> thus:-
let (|>) x f = f x
Note that you can define operators in F#, this is pretty cool :-).
This allows you to write things in a clearer way, e.g.:-
[1..10] |> List.map (fun x -> x * x) |> List.filter (fun x -> x % 2 = 0)
Will allow you to obtain the first 10 even squares. That is clearer than:-
List.filter (fun x -> x % 2 = 0) (List.map (fun x -> x * x) [1..10])
Well, at least I think so :-)
Function composition defined by the >> operator is defined as follows:-
let (>>) f g x = g(f(x))
I.e. you forward-pipe an operation only the parameter of the first function remains unspecified. This is useful as you can do the following:-
let mapFilter = List.map (fun x -> x * x) >> List.filter (fun x -> x % 2 = 0)
Here mapFilter will accept a list an input and return the list filtered as before. It's an abbreviated version of:-
let mapFilter = l |> List.map (fun x -> x * x) |> List.filter (fun x -> x % 2 = 0)
If we want to write recursive functions we have to define the function as recursive by placing 'rec' after the let. Examples below.
Some cool stuff:-
Factorial
let rec fact x = if x <= 1 then 1 else x * fact (x-1)
nth Fibonacci Number
let rec fib n = if n <= 1 then n else fib (n-1) + fib (n-2)
FizzBuzz
let (/%) x y = x % y = 0
let fb = function
| x when x /% 15 -> "FizzBuzz"
| x when x /% 3 -> "Fizz"
| x when x /% 5 -> "Buzz"
| x -> x.ToString()
[1..100] |> List.map (fb >> printfn "%s")
Anyway that's a very brief overview, hopefully it helps a little!!
Without doubt, you should purchase Don Syme's excellent book "Expert F#". The book is very well written and is suitable for both beginners and experts alike. In it, you'll find both introductory material and much more challenging material too. At nearly 600 pages it is good value for money.
I found that it taught me a lot of useful techniques for writing more functional C# as well as providing all the reference material I needed to get started writing Windows hosted F# applications.
The book is published by Apress and has an accompanying web site at:
http://www.expert-fsharp.com/default.aspx
#kronoz - well thanks a lot for your long answer, that's a really good place to start from. I'll follow your advices, and look for the book #vecstasy mentioned.
now, let me go coding :-)
let thanksalot = "thanks a lot"
printfn "%s" (thanksalot);;
I've been reading Real World Functional Programming
With examples in F# and C# by:Tomas Petricek
So far I find it very good at teaching F# concepts by showing the implementations in C# on the side. Great for OO Programmers.
The first chapter of my book F# for Scientists is freely available here. We have a series of free F# toy programs here. The first article from our F#.NET Journal is freely available here.
Check out the F# Developer Center. There is also hubFS, a forum dedicated to F#.
If you have the current CTP release in Visual Studio it lets you create a F# Tutorial project, which gives you a Tutorial.fs, exactly containing what it's name suggests.
That tutorial also points to a larger collection of F# examples at Microsoft.
Also, there is an F# samples project going on at CodePlex.
Hope this helps,
Michiel