From 142b6b1f18b16d5235d9ab11a76e3c98f9672f76 Mon Sep 17 00:00:00 2001 From: Scott Wlaschin Date: Sat, 29 Jun 2013 15:54:14 +0100 Subject: [PATCH] Updated F# code with new list and collection examples, and extra examples for data types. --- fsharp.html.markdown | 251 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 226 insertions(+), 25 deletions(-) diff --git a/fsharp.html.markdown b/fsharp.html.markdown index 5c54130d..1deaf437 100644 --- a/fsharp.html.markdown +++ b/fsharp.html.markdown @@ -6,11 +6,11 @@ author_url: http://fsharpforfunandprofit.com/ 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 read more like a dynamic language. +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 similar to Python: +The syntax of F# is different from C-style languages: -* Curly braces are not used to delimit blocks of code. Instead, indentation is used. +* 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 [tryfsharp.org](http://www.tryfsharp.org/Create) and paste it into an interactive REPL. @@ -123,7 +123,7 @@ printfn "A string %s, and something generic %A" "hello" [1;2;3;4] // Modules are used to group functions together // Indentation is needed for each nested module. -module Addition = +module FunctionExamples = // define a simple adding function let add x y = x + y @@ -132,12 +132,12 @@ module Addition = let a = add 1 2 printfn "1+2 = %i" a - // partial application + // partial application to "bake in" parameters let add42 = add 42 let b = add42 1 printfn "42+1 = %i" b - // composition + // composition to combine functions let add1 = add 1 let add2 = add 2 let add3 = add1 >> add2 @@ -153,30 +153,190 @@ module Addition = printfn "1+2+3+7 = %i" d // ================================================ -// Data Types +// Lists and collection // ================================================ +// There are three types of ordered collection: +// * Lists are most basic immutable collection. +// * Arrays are mutable and more efficient when needed. +// * Sequences are lazy and infinite (e.g. an enumerator). +// +// Other collections include immutable maps and sets +// plus all the standard .NET collections + +module ListExamples = + + // lists use square brackets + let list1 = ["a";"b"] + let list2 = "c" :: list1 // :: is prepending + let list3 = list1 @ list2 // @ is concat + + // list comprehensions (aka generators) + let squares = [for i in 1..10 do yield i*i] + + // prime number generator + let rec sieve = function + | (p::xs) -> p :: sieve [ for x in xs do if x % p > 0 then yield x ] + | [] -> [] + let primes = sieve [2..50] + printfn "%A" primes + + // pattern matching for lists + let listMatcher aList = + match aList with + | [] -> printfn "the list is empty" + | [first] -> printfn "the list has one element %A " first + | [first; second] -> printfn "list is %A and %A" first second + | _ -> printfn "the list has more than two elements" + + listMatcher [1;2;3;4] + listMatcher [1;2] + listMatcher [1] + listMatcher [] + + // recursion using lists + let rec sum aList = + match aList with + | [] -> 0 + | x::xs -> x + sum xs + sum [1..10] + + // ----------------------------------------- + // Standard library functions + // ----------------------------------------- + + // map + let add3 x = x + 3 + [1..10] |> List.map add3 + + // filter + let even x = x % 2 = 0 + [1..10] |> List.filter even + + // many more -- see documentation + +module ArrayExamples = + + // arrays use square brackets with bar + let array1 = [| "a";"b" |] + let first = array1.[0] // indexed access using dot + + // pattern matching for arrays is same as for lists + let arrayMatcher aList = + match aList with + | [| |] -> printfn "the array is empty" + | [| first |] -> printfn "the array has one element %A " first + | [| first; second |] -> printfn "array is %A and %A" first second + | _ -> printfn "the array has more than two elements" + + arrayMatcher [| 1;2;3;4 |] + + // Standard library functions just as for List + + [| 1..10 |] + |> Array.map (fun i -> i+3) + |> Array.filter (fun i -> i%2 = 0) + |> Array.iter (printfn "value is %i. ") + + +module SequenceExamples = + + // sequences use curly braces + let seq1 = seq { yield "a"; yield "b" } + + // sequences can use yield and + // can contain subsequences + let strange = seq { + // "yield! adds one element + yield 1; yield 2; + + // "yield!" adds a whole subsequence + yield! [5..10] + yield! seq { + for i in 1..10 do + if i%2 = 0 then yield i }} + // test + strange |> Seq.toList + + + // Sequences can be created using "unfold" + // Here's the fibonacci series + let fib = Seq.unfold (fun (fst,snd) -> + Some(fst + snd, (snd, fst + snd))) (0,1) + + // test + let fib10 = fib |> Seq.take 10 |> Seq.toList + printf "first 10 fibs are %A" fib10 + + +// ================================================ +// Data Types +// ================================================ module DataTypeExamples = // All data is immutable by default // Tuples are quick 'n easy anonymous types + // -- Use a comma to create a tuple let twoTuple = 1,2 let threeTuple = "a",2,true + + // Pattern match to unpack + let x,y = twoTuple //sets x=1 y=2 - // Record types have named fields + // ------------------------------------ + // Record types have named fields + // ------------------------------------ + + // Use "type" with curly braces to define a record type type Person = {First:string; Last:string} - let person1 = {First="john"; Last="Doe"} + + // Use "let" with curly braces to create a record + let person1 = {First="John"; Last="Doe"} + // Pattern match to unpack + let {First=first} = person1 //sets first="john" + + // ------------------------------------ // Union types (aka variants) have a set of choices // Only case can be valid at a time. + // ------------------------------------ + + // Use "type" with bar/pipe to define a union type type Temp = | DegreesC of float | DegreesF of float + + // Use one of the cases to create one let temp1 = DegreesF 98.6 let temp2 = DegreesC 37.0 + // Pattern match on all cases to unpack + let printTemp = function + | DegreesC t -> printfn "%f degC" t + | DegreesF t -> printfn "%f degF" t + + printTemp temp1 + printTemp temp2 + + // ------------------------------------ + // Recursive types + // ------------------------------------ + + // Types can be combined recursively in complex ways + // without having to create subclasses + type Employee = + | Worker of Person + | Manager of Employee list + + let jdoe = {First="John";Last="Doe"} + let worker = Worker jdoe + + // ------------------------------------ + // Modelling with types + // ------------------------------------ + // Union types are great for modelling state without using flags type EmailAddress = | ValidEmailAddress of string @@ -186,15 +346,6 @@ module DataTypeExamples = match email with // use pattern matching | ValidEmailAddress address -> () // send | InvalidEmailAddress address -> () // dont send - - // Types can be combined recursively in complex ways - // without having to create subclasses - type Employee = - | Worker of Person - | Manager of Employee list - - let jdoe = {First="John";Last="Doe"} - let worker = Worker jdoe // The combination of union types and record types together // provide a great foundation for domain driven design. @@ -211,10 +362,35 @@ module DataTypeExamples = | ActiveCart of ActiveCartData | PaidCart of PaidCartData - // All complex types have pretty printing built in for free + // ------------------------------------ + // Built in behavior for types + // ------------------------------------ + + // Core types have useful "out-of-the-box" behavior, no coding needed. + // * Immutability + // * Pretty printing when debugging + // * Equality and comparison + // * Serialization + + // Pretty printing using %A printfn "twoTuple=%A,\nPerson=%A,\nTemp=%A,\nEmployee=%A" twoTuple person1 temp1 worker - + + // Equality and comparison built in. + // Here's an example with cards. + type Suit = Club | Diamond | Spade | Heart + type Rank = Two | Three | Four | Five | Six | Seven | Eight + | Nine | Ten | Jack | Queen | King | Ace + + let hand = [ Club,Ace; Heart,Three; Heart,Ace; + Spade,Jack; Diamond,Two; Diamond,Ace ] + + // sorting + List.sort hand |> printfn "sorted hand is (low to high) %A" + List.max hand |> printfn "high card is %A" + List.min hand |> printfn "low card is %A" + + // ================================================ // Active patterns // ================================================ @@ -224,6 +400,8 @@ module ActivePatternExamples = // F# has a special type of pattern matching called "active patterns" // where the pattern can be parsed or detected dynamically. + // "banana clips" are the syntax for active patterns + // for example, define an "active" pattern to match character types... let (|Digit|Letter|Whitespace|Other|) ch = if System.Char.IsDigit(ch) then Digit @@ -242,7 +420,26 @@ module ActivePatternExamples = // print a list ['a';'b';'1';' ';'-';'c'] |> List.iter printChar - + // ----------------------------------- + // FizzBuzz using active patterns + // ----------------------------------- + + // You can create partial matching patterns as well + // Just use undercore in the defintion, and return Some if matched. + let (|MultOf3|_|) i = if i % 3 = 0 then Some MultOf3 else None + let (|MultOf5|_|) i = if i % 5 = 0 then Some MultOf5 else None + + // the main function + let fizzBuzz i = + match i with + | MultOf3 & MultOf5 -> printf "FizzBuzz, " + | MultOf3 -> printf "Fizz, " + | MultOf5 -> printf "Buzz, " + | _ -> printf "%i, " i + + // test + [1..20] |> List.iter fizzBuzz + // ================================================ // Conciseness // ================================================ @@ -289,7 +486,7 @@ module AlgorithmExamples = module AsyncExample = - // F# has some built-in features to help with async code + // F# has built-in features to help with async code // without encountering the "pyramid of doom" // // The following example downloads a set of web pages in parallel. @@ -301,10 +498,14 @@ module AsyncExample = // Fetch the contents of a URL asynchronously let fetchUrlAsync url = - async { + async { // "async" keyword and curly braces + // creates an "async" object let req = WebRequest.Create(Uri(url)) - use! resp = req.AsyncGetResponse() + use! resp = req.AsyncGetResponse() + // use! is async assignment use stream = resp.GetResponseStream() + // "use" triggers automatic close() + // on resource at end of scope use reader = new IO.StreamReader(stream) let html = reader.ReadToEnd() printfn "finished downloading %s" url @@ -360,7 +561,7 @@ module NetCompatibilityExamples = // F# is also a fully fledged OO language. // It supports classes, inheritance, virtual methods, etc. - // interface + // interface with generic type type IEnumerator<'a> = abstract member Current : 'a abstract MoveNext : unit -> bool