summaryrefslogtreecommitdiffhomepage
path: root/fsharp.html.markdown
diff options
context:
space:
mode:
Diffstat (limited to 'fsharp.html.markdown')
-rw-r--r--fsharp.html.markdown416
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/).
-
-
-
-