F# is a general purpose functional/OO programming language. It’s free and open source, and runs on Linux, Mac, Windows and more.
It has a powerful type system that traps many errors at compile time, but it uses type inference so that it reads more like a dynamic language.
The syntax of F# is different from C-style languages:
- Curly braces are not used to delimit blocks of code. Instead, indentation is used (like Python).
- Whitespace is used to separate parameters rather than commas.
If you want to try out the code below, you can go to https://try.fsharp.org and paste it into an interactive REPL.
1// single line comments use a double slash
2(* multi line comments use (* . . . *) pair
3
4-end of multi line comment- *)
5
6// ================================================
7// Basic Syntax
8// ================================================
9
10// ------ "Variables" (but not really) ------
11// The "let" keyword defines an (immutable) value
12let myInt = 5
13let myFloat = 3.14
14let myString = "hello" // note that no types needed
15
16// Mutable variables
17let mutable a=3
18a <- 4 // a is now 4.
19
20// Somewhat mutable variables
21// Reference cells are storage locations that enable you to create mutable values with reference semantics.
22// See https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/reference-cells
23let xRef = ref 10
24printfn "%d" xRef.Value // 10
25xRef.Value <- 11
26printfn "%d" xRef.Value // 11
27
28let a=[ref 0; ref 1] // somewhat mutable list
29a[0].Value <- 2
30
31// ------ Lists ------
32let twoToFive = [2; 3; 4; 5] // Square brackets create a list with
33 // semicolon delimiters.
34let oneToFive = 1 :: twoToFive // :: creates list with new 1st element
35// The result is [1; 2; 3; 4; 5]
36let zeroToFive = [0; 1] @ twoToFive // @ concats two lists
37
38// IMPORTANT: commas are never used as delimiters, only semicolons!
39
40// ------ Functions ------
41// The "let" keyword also defines a named function.
42let square x = x * x // Note that no parens are used.
43square 3 // Now run the function. Again, no parens.
44
45let add x y = x + y // don't use add (x,y)! It means something
46 // completely different.
47add 2 3 // Now run the function.
48
49// to define a multiline function, just use indents. No semicolons needed.
50let evens list =
51 let isEven x = x % 2 = 0 // Define "isEven" as a sub function. Note
52 // that equality operator is single char "=".
53 List.filter isEven list // List.filter is a library function
54 // with two parameters: a boolean function
55 // and a list to work on
56
57evens oneToFive // Now run the function
58
59// You can use parens to clarify precedence. In this example,
60// do "map" first, with two args, then do "sum" on the result.
61// Without the parens, "List.map" would be passed as an arg to List.sum
62let sumOfSquaresTo100 =
63 List.sum ( List.map square [1..100] )
64
65// You can pipe the output of one operation to the next using "|>"
66// Piping data around is very common in F#, similar to UNIX pipes.
67
68// Here is the same sumOfSquares function written using pipes
69let sumOfSquaresTo100piped =
70 [1..100] |> List.map square |> List.sum // "square" was defined earlier
71
72// you can define lambdas (anonymous functions) using the "fun" keyword
73let sumOfSquaresTo100withFun =
74 [1..100] |> List.map (fun x -> x * x) |> List.sum
75
76// In F# there is no "return" keyword. A function always
77// returns the value of the last expression used.
78
79// ------ Pattern Matching ------
80// Match..with.. is a supercharged case/switch statement.
81let simplePatternMatch =
82 let x = "a"
83 match x with
84 | "a" -> printfn "x is a"
85 | "b" -> printfn "x is b"
86 | _ -> printfn "x is something else" // underscore matches anything
87
88// F# doesn't allow nulls by default -- you must use an Option type
89// and then pattern match.
90// Some(..) and None are roughly analogous to Nullable wrappers
91let validValue = Some(99)
92let invalidValue = None
93
94// In this example, match..with matches the "Some" and the "None",
95// and also unpacks the value in the "Some" at the same time.
96let optionPatternMatch input =
97 match input with
98 | Some i -> printfn "input is an int=%d" i
99 | None -> printfn "input is missing"
100
101optionPatternMatch validValue
102optionPatternMatch invalidValue
103
104// ------ Printing ------
105// The printf/printfn functions are similar to the
106// Console.Write/WriteLine functions in C#.
107printfn "Printing an int %i, a float %f, a bool %b" 1 2.0 true
108printfn "A string %s, and something generic %A" "hello" [1; 2; 3; 4]
109
110// There are also sprintf/sprintfn functions for formatting data
111// into a string, similar to String.Format in C#.
112
113// ================================================
114// More on functions
115// ================================================
116
117// F# is a true functional language -- functions are first
118// class entities and can be combined easily to make powerful
119// constructs
120
121// Modules are used to group functions together
122// Indentation is needed for each nested module.
123module FunctionExamples =
124
125 // define a simple adding function
126 let add x y = x + y
127
128 // basic usage of a function
129 let a = add 1 2
130 printfn "1 + 2 = %i" a
131
132 // partial application to "bake in" parameters
133 let add42 = add 42
134 let b = add42 1
135 printfn "42 + 1 = %i" b
136
137 // composition to combine functions
138 let add1 = add 1
139 let add2 = add 2
140 let add3 = add1 >> add2
141 let c = add3 7
142 printfn "3 + 7 = %i" c
143
144 // higher order functions
145 [1..10] |> List.map add3 |> printfn "new list is %A"
146
147 // lists of functions, and more
148 let add6 = [add1; add2; add3] |> List.reduce (>>)
149 let d = add6 7
150 printfn "1 + 2 + 3 + 7 = %i" d
151
152// ================================================
153// Lists and collection
154// ================================================
155
156// There are three types of ordered collection:
157// * Lists are most basic immutable collection.
158// * Arrays are mutable and more efficient when needed.
159// * Sequences are lazy and infinite (e.g. an enumerator).
160//
161// Other collections include immutable maps and sets
162// plus all the standard .NET collections
163
164module ListExamples =
165
166 // lists use square brackets
167 let list1 = ["a"; "b"]
168 let list2 = "c" :: list1 // :: is prepending
169 let list3 = list1 @ list2 // @ is concat
170
171 // list comprehensions (aka generators)
172 let squares = [for i in 1..10 do yield i * i]
173
174 // A prime number generator
175 // - this is using a short notation for the pattern matching syntax
176 // - (p::xs) is 'first :: tail' of the list, could also be written as p :: xs
177 // this means this matches 'p' (the first item in the list), and xs is the rest of the list
178 // this is called the 'cons pattern'
179 // - uses 'rec' keyword, which is necessary when using recursion
180 let rec sieve = function
181 | (p::xs) -> p :: sieve [ for x in xs do if x % p > 0 then yield x ]
182 | [] -> []
183 let primes = sieve [2..50]
184 printfn "%A" primes
185
186 // pattern matching for lists
187 let listMatcher aList =
188 match aList with
189 | [] -> printfn "the list is empty"
190 | [first] -> printfn "the list has one element %A " first
191 | [first; second] -> printfn "list is %A and %A" first second
192 | first :: _ -> printfn "the list has more than two elements, first element %A" first
193
194 listMatcher [1; 2; 3; 4]
195 listMatcher [1; 2]
196 listMatcher [1]
197 listMatcher []
198
199 // recursion using lists
200 let rec sum aList =
201 match aList with
202 | [] -> 0
203 | x::xs -> x + sum xs
204 sum [1..10]
205
206 // -----------------------------------------
207 // Standard library functions
208 // -----------------------------------------
209
210 // map
211 let add3 x = x + 3
212 [1..10] |> List.map add3
213
214 // filter
215 let even x = x % 2 = 0
216 [1..10] |> List.filter even
217
218 // many more -- see documentation
219
220module ArrayExamples =
221
222 // arrays use square brackets with bar
223 let array1 = [| "a"; "b" |]
224 let first = array1.[0] // indexed access using dot
225
226 // pattern matching for arrays is same as for lists
227 let arrayMatcher aList =
228 match aList with
229 | [| |] -> printfn "the array is empty"
230 | [| first |] -> printfn "the array has one element %A " first
231 | [| first; second |] -> printfn "array is %A and %A" first second
232 | _ -> printfn "the array has more than two elements"
233
234 arrayMatcher [| 1; 2; 3; 4 |]
235
236 // Standard library functions just as for List
237
238 [| 1..10 |]
239 |> Array.map (fun i -> i + 3)
240 |> Array.filter (fun i -> i % 2 = 0)
241 |> Array.iter (printfn "value is %i. ")
242
243
244module SequenceExamples =
245
246 // sequences use curly braces
247 let seq1 = seq { yield "a"; yield "b" }
248
249 // sequences can use yield and
250 // can contain subsequences
251 let strange = seq {
252 // "yield" adds one element
253 yield 1; yield 2;
254
255 // "yield!" adds a whole subsequence
256 yield! [5..10]
257 yield! seq {
258 for i in 1..10 do
259 if i % 2 = 0 then yield i }}
260 // test
261 strange |> Seq.toList
262
263
264 // Sequences can be created using "unfold"
265 // Here's the fibonacci series
266 let fib = Seq.unfold (fun (fst,snd) ->
267 Some(fst + snd, (snd, fst + snd))) (0,1)
268
269 // test
270 let fib10 = fib |> Seq.take 10 |> Seq.toList
271 printf "first 10 fibs are %A" fib10
272
273
274// ================================================
275// Data Types
276// ================================================
277
278module DataTypeExamples =
279
280 // All data is immutable by default
281
282 // Tuples are quick 'n easy anonymous types
283 // -- Use a comma to create a tuple
284 let twoTuple = 1, 2
285 let threeTuple = "a", 2, true
286
287 // Pattern match to unpack
288 let x, y = twoTuple // sets x = 1, y = 2
289
290 // ------------------------------------
291 // Record types have named fields
292 // ------------------------------------
293
294 // Use "type" with curly braces to define a record type
295 type Person = {First:string; Last:string}
296
297 // Use "let" with curly braces to create a record
298 let person1 = {First="John"; Last="Doe"}
299
300 // Pattern match to unpack
301 let {First = first} = person1 // sets first="John"
302
303 // ------------------------------------
304 // Union types (aka variants) have a set of choices
305 // Only one case can be valid at a time.
306 // ------------------------------------
307
308 // Use "type" with bar/pipe to define a union type
309 type Temp =
310 | DegreesC of float
311 | DegreesF of float
312
313 // Use one of the cases to create one
314 let temp1 = DegreesF 98.6
315 let temp2 = DegreesC 37.0
316
317 // Pattern match on all cases to unpack
318 let printTemp = function
319 | DegreesC t -> printfn "%f degC" t
320 | DegreesF t -> printfn "%f degF" t
321
322 printTemp temp1
323 printTemp temp2
324
325 // ------------------------------------
326 // Recursive types
327 // ------------------------------------
328
329 // Types can be combined recursively in complex ways
330 // without having to create subclasses
331 type Employee =
332 | Worker of Person
333 | Manager of Employee list
334
335 let jdoe = {First="John"; Last="Doe"}
336 let worker = Worker jdoe
337
338 // ------------------------------------
339 // Modeling with types
340 // ------------------------------------
341
342 // Union types are great for modeling state without using flags
343 type EmailAddress =
344 | ValidEmailAddress of string
345 | InvalidEmailAddress of string
346
347 let trySendEmail email =
348 match email with // use pattern matching
349 | ValidEmailAddress address -> () // send
350 | InvalidEmailAddress address -> () // don't send
351
352 // The combination of union types and record types together
353 // provide a great foundation for domain driven design.
354 // You can create hundreds of little types that accurately
355 // reflect the domain.
356
357 type CartItem = { ProductCode: string; Qty: int }
358 type Payment = Payment of float
359 type ActiveCartData = { UnpaidItems: CartItem list }
360 type PaidCartData = { PaidItems: CartItem list; Payment: Payment}
361
362 type ShoppingCart =
363 | EmptyCart // no data
364 | ActiveCart of ActiveCartData
365 | PaidCart of PaidCartData
366
367 // ------------------------------------
368 // Built in behavior for types
369 // ------------------------------------
370
371 // Core types have useful "out-of-the-box" behavior, no coding needed.
372 // * Immutability
373 // * Pretty printing when debugging
374 // * Equality and comparison
375 // * Serialization
376
377 // Pretty printing using %A
378 printfn "twoTuple=%A,\nPerson=%A,\nTemp=%A,\nEmployee=%A"
379 twoTuple person1 temp1 worker
380
381 // Equality and comparison built in.
382 // Here's an example with cards.
383 type Suit = Club | Diamond | Spade | Heart
384 type Rank = Two | Three | Four | Five | Six | Seven | Eight
385 | Nine | Ten | Jack | Queen | King | Ace
386
387 let hand = [ Club, Ace; Heart, Three; Heart, Ace;
388 Spade, Jack; Diamond, Two; Diamond, Ace ]
389
390 // sorting
391 List.sort hand |> printfn "sorted hand is (low to high) %A"
392 List.max hand |> printfn "high card is %A"
393 List.min hand |> printfn "low card is %A"
394
395
396// ================================================
397// Active patterns
398// ================================================
399
400module ActivePatternExamples =
401
402 // F# has a special type of pattern matching called "active patterns"
403 // where the pattern can be parsed or detected dynamically.
404
405 // "banana clips" are the syntax for active patterns
406
407 // You can use "elif" instead of "else if" in conditional expressions.
408 // They are equivalent in F#
409
410 // for example, define an "active" pattern to match character types...
411 let (|Digit|Letter|Whitespace|Other|) ch =
412 if System.Char.IsDigit(ch) then Digit
413 elif System.Char.IsLetter(ch) then Letter
414 elif System.Char.IsWhiteSpace(ch) then Whitespace
415 else Other
416
417 // ... and then use it to make parsing logic much clearer
418 let printChar ch =
419 match ch with
420 | Digit -> printfn "%c is a Digit" ch
421 | Letter -> printfn "%c is a Letter" ch
422 | Whitespace -> printfn "%c is a Whitespace" ch
423 | _ -> printfn "%c is something else" ch
424
425 // print a list
426 ['a'; 'b'; '1'; ' '; '-'; 'c'] |> List.iter printChar
427
428 // -----------------------------------
429 // FizzBuzz using active patterns
430 // -----------------------------------
431
432 // You can create partial matching patterns as well
433 // Just use underscore in the definition, and return Some if matched.
434 let (|MultOf3|_|) i = if i % 3 = 0 then Some MultOf3 else None
435 let (|MultOf5|_|) i = if i % 5 = 0 then Some MultOf5 else None
436
437 // the main function
438 let fizzBuzz i =
439 match i with
440 | MultOf3 & MultOf5 -> printf "FizzBuzz, "
441 | MultOf3 -> printf "Fizz, "
442 | MultOf5 -> printf "Buzz, "
443 | _ -> printf "%i, " i
444
445 // test
446 [1..20] |> List.iter fizzBuzz
447
448// ================================================
449// Conciseness
450// ================================================
451
452module AlgorithmExamples =
453
454 // F# has a high signal/noise ratio, so code reads
455 // almost like the actual algorithm
456
457 // ------ Example: define sumOfSquares function ------
458 let sumOfSquares n =
459 [1..n] // 1) take all the numbers from 1 to n
460 |> List.map square // 2) square each one
461 |> List.sum // 3) sum the results
462
463 // test
464 sumOfSquares 100 |> printfn "Sum of squares = %A"
465
466 // ------ Example: define a sort function ------
467 let rec sort list =
468 match list with
469 // If the list is empty
470 | [] ->
471 [] // return an empty list
472 // If the list is not empty
473 | firstElem::otherElements -> // take the first element
474 let smallerElements = // extract the smaller elements
475 otherElements // from the remaining ones
476 |> List.filter (fun e -> e < firstElem)
477 |> sort // and sort them
478 let largerElements = // extract the larger ones
479 otherElements // from the remaining ones
480 |> List.filter (fun e -> e >= firstElem)
481 |> sort // and sort them
482 // Combine the 3 parts into a new list and return it
483 List.concat [smallerElements; [firstElem]; largerElements]
484
485 // test
486 sort [1; 5; 23; 18; 9; 1; 3] |> printfn "Sorted = %A"
487
488// ================================================
489// Asynchronous Code
490// ================================================
491
492module AsyncExample =
493
494 // F# has built-in features to help with async code
495 // without encountering the "pyramid of doom"
496 //
497 // The following example downloads a set of web pages in parallel.
498
499 open System.Net
500 open System
501 open System.IO
502 open Microsoft.FSharp.Control.CommonExtensions
503
504 // Fetch the contents of a URL asynchronously
505 let fetchUrlAsync url =
506 async { // "async" keyword and curly braces
507 // creates an "async" object
508 let req = WebRequest.Create(Uri(url))
509 use! resp = req.AsyncGetResponse()
510 // use! is async assignment
511 use stream = resp.GetResponseStream()
512 // "use" triggers automatic close()
513 // on resource at end of scope
514 use reader = new IO.StreamReader(stream)
515 let html = reader.ReadToEnd()
516 printfn "finished downloading %s" url
517 }
518
519 // a list of sites to fetch
520 let sites = ["http://www.bing.com";
521 "http://www.google.com";
522 "http://www.microsoft.com";
523 "http://www.amazon.com";
524 "http://www.yahoo.com"]
525
526 // do it
527 sites
528 |> List.map fetchUrlAsync // make a list of async tasks
529 |> Async.Parallel // set up the tasks to run in parallel
530 |> Async.RunSynchronously // start them off
531
532// ================================================
533// .NET compatibility
534// ================================================
535
536module NetCompatibilityExamples =
537
538 // F# can do almost everything C# can do, and it integrates
539 // seamlessly with .NET or Mono libraries.
540
541 // ------- work with existing library functions -------
542
543 let (i1success, i1) = System.Int32.TryParse("123");
544 if i1success then printfn "parsed as %i" i1 else printfn "parse failed"
545
546 // ------- Implement interfaces on the fly! -------
547
548 // create a new object that implements IDisposable
549 let makeResource name =
550 { new System.IDisposable
551 with member this.Dispose() = printfn "%s disposed" name }
552
553 let useAndDisposeResources =
554 use r1 = makeResource "first resource"
555 printfn "using first resource"
556 for i in [1..3] do
557 let resourceName = sprintf "\tinner resource %d" i
558 use temp = makeResource resourceName
559 printfn "\tdo something with %s" resourceName
560 use r2 = makeResource "second resource"
561 printfn "using second resource"
562 printfn "done."
563
564 // ------- Object oriented code -------
565
566 // F# is also a fully fledged OO language.
567 // It supports classes, inheritance, virtual methods, etc.
568
569 // interface with generic type
570 type IEnumerator<'a> =
571 abstract member Current : 'a
572 abstract MoveNext : unit -> bool
573
574 // abstract base class with virtual methods
575 [<AbstractClass>]
576 type Shape() =
577 // readonly properties
578 abstract member Width : int with get
579 abstract member Height : int with get
580 // non-virtual method
581 member this.BoundingArea = this.Height * this.Width
582 // virtual method with base implementation
583 abstract member Print : unit -> unit
584 default this.Print () = printfn "I'm a shape"
585
586 // concrete class that inherits from base class and overrides
587 type Rectangle(x:int, y:int) =
588 inherit Shape()
589 override this.Width = x
590 override this.Height = y
591 override this.Print () = printfn "I'm a Rectangle"
592
593 // test
594 let r = Rectangle(2, 3)
595 printfn "The width is %i" r.Width
596 printfn "The area is %i" r.BoundingArea
597 r.Print()
598
599 // ------- extension methods -------
600
601 // Just as in C#, F# can extend existing classes with extension methods.
602 type System.String with
603 member this.StartsWithA = this.StartsWith "A"
604
605 // test
606 let s = "Alice"
607 printfn "'%s' starts with an 'A' = %A" s s.StartsWithA
608
609 // ------- events -------
610
611 type MyButton() =
612 let clickEvent = new Event<_>()
613
614 [<CLIEvent>]
615 member this.OnClick = clickEvent.Publish
616
617 member this.TestEvent(arg) =
618 clickEvent.Trigger(this, arg)
619
620 // test
621 let myButton = new MyButton()
622 myButton.OnClick.Add(fun (sender, arg) ->
623 printfn "Click event with arg=%O" arg)
624
625 myButton.TestEvent("Hello World!")
More Information ¶
For more demonstrations of F#, go to my why use F# series.
Read more about F# at fsharp.org and dotnet’s F# page.