summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorScott Wlaschin <swlaschin@gmail.com>2013-06-29 15:54:14 +0100
committerScott Wlaschin <swlaschin@gmail.com>2013-06-29 15:54:14 +0100
commit142b6b1f18b16d5235d9ab11a76e3c98f9672f76 (patch)
treea5bb23f75143977bf7d5fd10f0602f3cf1ebdf34
parenta6bcf5f8d7fe60af918eea947bee7f5950a2061d (diff)
Updated F# code with new list and collection examples, and extra examples for data types.
-rw-r--r--fsharp.html.markdown251
1 files 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,40 +153,177 @@ 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
- // Union types are great for modelling state without using flags
- type EmailAddress =
- | ValidEmailAddress of string
- | InvalidEmailAddress of string
+ // 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
+ // ------------------------------------
- let trySendEmail email =
- 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 =
@@ -195,6 +332,20 @@ module DataTypeExamples =
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
+ | InvalidEmailAddress of string
+
+ let trySendEmail email =
+ match email with // use pattern matching
+ | ValidEmailAddress address -> () // send
+ | InvalidEmailAddress address -> () // dont send
// 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