diff options
Diffstat (limited to 'fsharp.html.markdown')
-rw-r--r-- | fsharp.html.markdown | 416 |
1 files changed, 210 insertions, 206 deletions
diff --git a/fsharp.html.markdown b/fsharp.html.markdown index 49951c78..bbf477ba 100644 --- a/fsharp.html.markdown +++ b/fsharp.html.markdown @@ -5,7 +5,7 @@ contributors: filename: learnfsharp.fs --- -F# is a general purpose functional/OO programming language. It's free and open source, and runs on Linux, Mac, Windows and more. +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. @@ -31,14 +31,14 @@ If you want to try out the code below, you can go to [tryfsharp.org](http://www. // The "let" keyword defines an (immutable) value let myInt = 5 let myFloat = 3.14 -let myString = "hello" //note that no types needed +let myString = "hello" // note that no types needed // ------ Lists ------ -let twoToFive = [2;3;4;5] // Square brackets create a list with +let twoToFive = [2; 3; 4; 5] // Square brackets create a list with // semicolon delimiters. let oneToFive = 1 :: twoToFive // :: creates list with new 1st element -// The result is [1;2;3;4;5] -let zeroToFive = [0;1] @ twoToFive // @ concats two lists +// The result is [1; 2; 3; 4; 5] +let zeroToFive = [0; 1] @ twoToFive // @ concats two lists // IMPORTANT: commas are never used as delimiters, only semicolons! @@ -53,7 +53,7 @@ add 2 3 // Now run the function. // to define a multiline function, just use indents. No semicolons needed. let evens list = - let isEven x = x%2 = 0 // Define "isEven" as a sub function + let isEven x = x % 2 = 0 // Define "isEven" as a sub function List.filter isEven list // List.filter is a library function // with two parameters: a boolean function // and a list to work on @@ -75,7 +75,7 @@ let sumOfSquaresTo100piped = // you can define lambdas (anonymous functions) using the "fun" keyword let sumOfSquaresTo100withFun = - [1..100] |> List.map (fun x -> x*x) |> List.sum + [1..100] |> List.map (fun x -> x * x) |> List.sum // In F# there is no "return" keyword. A function always // returns the value of the last expression used. @@ -90,7 +90,7 @@ let simplePatternMatch = | _ -> printfn "x is something else" // underscore matches anything // F# doesn't allow nulls by default -- you must use an Option type -// and then pattern match. +// and then pattern match. // Some(..) and None are roughly analogous to Nullable wrappers let validValue = Some(99) let invalidValue = None @@ -109,103 +109,108 @@ optionPatternMatch invalidValue // The printf/printfn functions are similar to the // Console.Write/WriteLine functions in C#. printfn "Printing an int %i, a float %f, a bool %b" 1 2.0 true -printfn "A string %s, and something generic %A" "hello" [1;2;3;4] +printfn "A string %s, and something generic %A" "hello" [1; 2; 3; 4] // There are also sprintf/sprintfn functions for formatting data // into a string, similar to String.Format in C#. // ================================================ -// More on functions +// More on functions // ================================================ // F# is a true functional language -- functions are first -// class entities and can be combined easy to make powerful +// class entities and can be combined easily to make powerful // constructs // Modules are used to group functions together // Indentation is needed for each nested module. -module FunctionExamples = +module FunctionExamples = // define a simple adding function let add x y = x + y - + // basic usage of a function let a = add 1 2 - printfn "1+2 = %i" a - + printfn "1 + 2 = %i" a + // partial application to "bake in" parameters let add42 = add 42 let b = add42 1 - printfn "42+1 = %i" b - + printfn "42 + 1 = %i" b + // composition to combine functions let add1 = add 1 let add2 = add 2 let add3 = add1 >> add2 let c = add3 7 - printfn "3+7 = %i" c - + printfn "3 + 7 = %i" c + // higher order functions [1..10] |> List.map add3 |> printfn "new list is %A" - + // lists of functions, and more let add6 = [add1; add2; add3] |> List.reduce (>>) let d = add6 7 - printfn "1+2+3+7 = %i" d + printfn "1 + 2 + 3 + 7 = %i" d // ================================================ // 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). +// * 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 = +module ListExamples = - // lists use square brackets - let list1 = ["a";"b"] + // 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 + // list comprehensions (aka generators) + let squares = [for i in 1..10 do yield i * i] + + // A prime number generator + // - this is using a short notation for the pattern matching syntax + // - (p::xs) is 'first :: tail' of the list, could also be written as p :: xs + // this means this matches 'p' (the first item in the list), and xs is the rest of the list + // this is called the 'cons pattern' + // - uses 'rec' keyword, which is necessary when using recursion 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 - + printfn "%A" primes + // pattern matching for lists - let listMatcher aList = + 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" + | [] -> 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; 2; 3; 4] + listMatcher [1; 2] listMatcher [1] - listMatcher [] + listMatcher [] // recursion using lists - let rec sum aList = + let rec sum aList = match aList with | [] -> 0 | x::xs -> x + sum xs sum [1..10] - - // ----------------------------------------- - // Standard library functions + // ----------------------------------------- - + // Standard library functions + // ----------------------------------------- + // map let add3 x = x + 3 [1..10] |> List.map add3 @@ -213,102 +218,102 @@ module ListExamples = // filter let even x = x % 2 = 0 [1..10] |> List.filter even - + // many more -- see documentation - -module ArrayExamples = + +module ArrayExamples = // arrays use square brackets with bar - let array1 = [| "a";"b" |] + let array1 = [| "a"; "b" |] let first = array1.[0] // indexed access using dot - + // pattern matching for arrays is same as for lists - let arrayMatcher aList = + 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" + | [| |] -> 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 |] + 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) + + [| 1..10 |] + |> Array.map (fun i -> i + 3) + |> Array.filter (fun i -> i % 2 = 0) |> Array.iter (printfn "value is %i. ") - - -module SequenceExamples = + + +module SequenceExamples = // sequences use curly braces let seq1 = seq { yield "a"; yield "b" } - - // sequences can use yield and + + // sequences can use yield and // can contain subsequences let strange = seq { - // "yield! adds one element + // "yield" adds one element yield 1; yield 2; - + // "yield!" adds a whole subsequence - yield! [5..10] + yield! [5..10] yield! seq { - for i in 1..10 do - if i%2 = 0 then yield i }} - // test - strange |> Seq.toList - + 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 + // test let fib10 = fib |> Seq.take 10 |> Seq.toList - printf "first 10 fibs are %A" fib10 - - + printf "first 10 fibs are %A" fib10 + + // ================================================ -// Data Types +// Data Types // ================================================ -module DataTypeExamples = +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 - + let twoTuple = 1, 2 + let threeTuple = "a", 2, true + // Pattern match to unpack - let x,y = twoTuple //sets x=1 y=2 + 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} - - // Use "let" with curly braces to create a record + + // 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" + 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 = + 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 @@ -317,101 +322,104 @@ module DataTypeExamples = let printTemp = function | DegreesC t -> printfn "%f degC" t | DegreesF t -> printfn "%f degF" t - - printTemp temp1 + + printTemp temp1 printTemp temp2 - // ------------------------------------ + // ------------------------------------ // Recursive types - // ------------------------------------ + // ------------------------------------ - // Types can be combined recursively in complex ways + // Types can be combined recursively in complex ways // without having to create subclasses - type Employee = + type Employee = | Worker of Person | Manager of Employee list - let jdoe = {First="John";Last="Doe"} + 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 = + + // ------------------------------------ + // Modeling with types + // ------------------------------------ + + // Union types are great for modeling state without using flags + type EmailAddress = | ValidEmailAddress of string | InvalidEmailAddress of string let trySendEmail email = match email with // use pattern matching | ValidEmailAddress address -> () // send - | InvalidEmailAddress address -> () // dont send + | InvalidEmailAddress address -> () // don't send // The combination of union types and record types together // provide a great foundation for domain driven design. - // You can create hundreds of little types that accurately + // You can create hundreds of little types that accurately // reflect the domain. type CartItem = { ProductCode: string; Qty: int } type Payment = Payment of float type ActiveCartData = { UnpaidItems: CartItem list } type PaidCartData = { PaidItems: CartItem list; Payment: Payment} - - type ShoppingCart = + + type ShoppingCart = | EmptyCart // no data | ActiveCart of ActiveCartData - | PaidCart of PaidCartData + | PaidCart of PaidCartData - // ------------------------------------ + // ------------------------------------ // 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" + 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 + 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 ] + 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 // ================================================ -module ActivePatternExamples = +module ActivePatternExamples = - // F# has a special type of pattern matching called "active patterns" - // where the pattern can be parsed or detected dynamically. + // 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 - + + // You can use "elif" instead of "else if" in conditional expressions. + // They are equivalent in F# + // for example, define an "active" pattern to match character types... - let (|Digit|Letter|Whitespace|Other|) ch = + let (|Digit|Letter|Whitespace|Other|) ch = if System.Char.IsDigit(ch) then Digit - else if System.Char.IsLetter(ch) then Letter - else if System.Char.IsWhiteSpace(ch) then Whitespace - else Other + elif System.Char.IsLetter(ch) then Letter + elif System.Char.IsWhiteSpace(ch) then Whitespace + else Other // ... and then use it to make parsing logic much clearer - let printChar ch = + let printChar ch = match ch with | Digit -> printfn "%c is a Digit" ch | Letter -> printfn "%c is a Letter" ch @@ -419,57 +427,57 @@ module ActivePatternExamples = | _ -> printfn "%c is something else" ch // print a list - ['a';'b';'1';' ';'-';'c'] |> List.iter printChar + ['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. + // Just use underscore in the definition, 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 = + let fizzBuzz i = match i with - | MultOf3 & MultOf5 -> printf "FizzBuzz, " - | MultOf3 -> printf "Fizz, " - | MultOf5 -> printf "Buzz, " + | MultOf3 & MultOf5 -> printf "FizzBuzz, " + | MultOf3 -> printf "Fizz, " + | MultOf5 -> printf "Buzz, " | _ -> printf "%i, " i - + // test - [1..20] |> List.iter fizzBuzz - + [1..20] |> List.iter fizzBuzz + // ================================================ -// Conciseness +// Conciseness // ================================================ -module AlgorithmExamples = +module AlgorithmExamples = - // F# has a high signal/noise ratio, so code reads + // F# has a high signal/noise ratio, so code reads // almost like the actual algorithm // ------ Example: define sumOfSquares function ------ - let sumOfSquares n = + let sumOfSquares n = [1..n] // 1) take all the numbers from 1 to n |> List.map square // 2) square each one |> List.sum // 3) sum the results - // test - sumOfSquares 100 |> printfn "Sum of squares = %A" - - // ------ Example: define a sort function ------ + // test + sumOfSquares 100 |> printfn "Sum of squares = %A" + + // ------ Example: define a sort function ------ let rec sort list = match list with - // If the list is empty - | [] -> + // If the list is empty + | [] -> [] // return an empty list - // If the list is not empty - | firstElem::otherElements -> // take the first element - let smallerElements = // extract the smaller elements + // If the list is not empty + | firstElem::otherElements -> // take the first element + let smallerElements = // extract the smaller elements otherElements // from the remaining ones - |> List.filter (fun e -> e < firstElem) + |> List.filter (fun e -> e < firstElem) |> sort // and sort them let largerElements = // extract the larger ones otherElements // from the remaining ones @@ -479,13 +487,13 @@ module AlgorithmExamples = List.concat [smallerElements; [firstElem]; largerElements] // test - sort [1;5;23;18;9;1;3] |> printfn "Sorted = %A" + sort [1; 5; 23; 18; 9; 1; 3] |> printfn "Sorted = %A" // ================================================ // Asynchronous Code // ================================================ -module AsyncExample = +module AsyncExample = // F# has built-in features to help with async code // without encountering the "pyramid of doom" @@ -495,23 +503,23 @@ module AsyncExample = open System.Net open System open System.IO - open Microsoft.FSharp.Control.CommonExtensions + open Microsoft.FSharp.Control.CommonExtensions // Fetch the contents of a URL asynchronously - let fetchUrlAsync url = - async { // "async" keyword and curly braces + let fetchUrlAsync url = + async { // "async" keyword and curly braces // creates an "async" object - let req = WebRequest.Create(Uri(url)) - use! resp = req.AsyncGetResponse() + let req = WebRequest.Create(Uri(url)) + use! resp = req.AsyncGetResponse() // use! is async assignment - use stream = resp.GetResponseStream() + 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 + use reader = new IO.StreamReader(stream) + let html = reader.ReadToEnd() + printfn "finished downloading %s" url } - + // a list of sites to fetch let sites = ["http://www.bing.com"; "http://www.google.com"; @@ -520,90 +528,90 @@ module AsyncExample = "http://www.yahoo.com"] // do it - sites + sites |> List.map fetchUrlAsync // make a list of async tasks |> Async.Parallel // set up the tasks to run in parallel |> Async.RunSynchronously // start them off // ================================================ -// .NET compatability +// .NET compatibility // ================================================ -module NetCompatibilityExamples = +module NetCompatibilityExamples = // F# can do almost everything C# can do, and it integrates // seamlessly with .NET or Mono libraries. // ------- work with existing library functions ------- - - let (i1success,i1) = System.Int32.TryParse("123"); + + let (i1success, i1) = System.Int32.TryParse("123"); if i1success then printfn "parsed as %i" i1 else printfn "parse failed" // ------- Implement interfaces on the fly! ------- - + // create a new object that implements IDisposable - let makeResource name = - { new System.IDisposable + let makeResource name = + { new System.IDisposable with member this.Dispose() = printfn "%s disposed" name } - let useAndDisposeResources = + let useAndDisposeResources = use r1 = makeResource "first resource" - printfn "using first resource" + printfn "using first resource" for i in [1..3] do let resourceName = sprintf "\tinner resource %d" i - use temp = makeResource resourceName - printfn "\tdo something with %s" resourceName + use temp = makeResource resourceName + printfn "\tdo something with %s" resourceName use r2 = makeResource "second resource" - printfn "using second resource" - printfn "done." + printfn "using second resource" + printfn "done." // ------- Object oriented code ------- - + // F# is also a fully fledged OO language. // It supports classes, inheritance, virtual methods, etc. // interface with generic type - type IEnumerator<'a> = + type IEnumerator<'a> = abstract member Current : 'a - abstract MoveNext : unit -> bool + abstract MoveNext : unit -> bool // abstract base class with virtual methods [<AbstractClass>] - type Shape() = - //readonly properties + type Shape() = + // readonly properties abstract member Width : int with get abstract member Height : int with get - //non-virtual method + // non-virtual method member this.BoundingArea = this.Height * this.Width - //virtual method with base implementation - abstract member Print : unit -> unit + // virtual method with base implementation + abstract member Print : unit -> unit default this.Print () = printfn "I'm a shape" - // concrete class that inherits from base class and overrides - type Rectangle(x:int, y:int) = + // concrete class that inherits from base class and overrides + type Rectangle(x:int, y:int) = inherit Shape() override this.Width = x override this.Height = y override this.Print () = printfn "I'm a Rectangle" - //test - let r = Rectangle(2,3) + // test + let r = Rectangle(2, 3) printfn "The width is %i" r.Width printfn "The area is %i" r.BoundingArea - r.Print() + r.Print() // ------- extension methods ------- - - //Just as in C#, F# can extend existing classes with extension methods. + + // Just as in C#, F# can extend existing classes with extension methods. type System.String with member this.StartsWithA = this.StartsWith "A" - //test + // test let s = "Alice" - printfn "'%s' starts with an 'A' = %A" s s.StartsWithA - + printfn "'%s' starts with an 'A' = %A" s s.StartsWithA + // ------- events ------- - + type MyButton() = let clickEvent = new Event<_>() @@ -615,11 +623,11 @@ module NetCompatibilityExamples = // test let myButton = new MyButton() - myButton.OnClick.Add(fun (sender, arg) -> + myButton.OnClick.Add(fun (sender, arg) -> printfn "Click event with arg=%O" arg) myButton.TestEvent("Hello World!") - + ``` ## More Information @@ -627,7 +635,3 @@ module NetCompatibilityExamples = For more demonstrations of F#, go to the [Try F#](http://www.tryfsharp.org/Learn) site, or my [why use F#](http://fsharpforfunandprofit.com/why-use-fsharp/) series. Read more about F# at [fsharp.org](http://fsharp.org/). - - - - |