2013-06-29 03:26:34 +04:00
- - -
language : F #
2013-07-04 09:59:13 +04:00
contributors :
- [ " Scott Wlaschin " , " http://fsharpforfunandprofit.com/ " ]
2013-06-30 07:19:14 +04:00
filename : learnfsharp . fs
2013-06-29 03:26:34 +04:00
- - -
2015-10-08 06:11:24 +03:00
F # is a general purpose functional / OO programming language . It's free and open source , and runs on Linux , Mac , Windows and more .
2013-06-29 03:26:34 +04:00
2013-06-29 18:54:14 +04:00
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 .
2013-06-29 03:26:34 +04:00
2013-06-29 18:54:14 +04:00
The syntax of F # is different from C - style languages :
2013-06-29 03:26:34 +04:00
2013-06-29 18:54:14 +04:00
* Curly braces are not used to delimit blocks of code . Instead , indentation is used ( like Python ) .
2013-06-29 03:26:34 +04:00
* 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.
2015-10-28 02:54:39 +03:00
` ` ` csharp
2013-06-29 03:26:34 +04:00
// single line comments use a double slash
(* multi line comments use (* . . . *) pair
- end of multi line comment - * )
// ================================================
// Basic Syntax
// ================================================
// ------ "Variables" (but not really) ------
// The "let" keyword defines an (immutable) value
let myInt = 5
let myFloat = 3 . 14
2015-10-31 07:40:37 +03:00
let myString = " hello " // note that no types needed
2013-06-29 03:26:34 +04:00
// ------ Lists ------
2015-10-31 07:40:37 +03:00
let twoToFive = [ 2 ; 3 ; 4 ; 5 ] // Square brackets create a list with
2013-06-29 03:26:34 +04:00
// semicolon delimiters.
let oneToFive = 1 :: twoToFive // :: creates list with new 1st element
2015-10-31 07:40:37 +03:00
// The result is [1; 2; 3; 4; 5]
let zeroToFive = [ 0 ; 1 ] @ twoToFive // @ concats two lists
2013-06-29 03:26:34 +04:00
// IMPORTANT: commas are never used as delimiters, only semicolons!
// ------ Functions ------
// The "let" keyword also defines a named function.
let square x = x * x // Note that no parens are used.
square 3 // Now run the function. Again, no parens.
let add x y = x + y // don't use add (x,y)! It means something
// completely different.
add 2 3 // Now run the function.
// to define a multiline function, just use indents. No semicolons needed.
let evens list =
2015-10-31 07:40:37 +03:00
let isEven x = x % 2 = 0 // Define "isEven" as a sub function
2013-06-29 03:26:34 +04:00
List . filter isEven list // List.filter is a library function
// with two parameters: a boolean function
// and a list to work on
evens oneToFive // Now run the function
// You can use parens to clarify precedence. In this example,
// do "map" first, with two args, then do "sum" on the result.
// Without the parens, "List.map" would be passed as an arg to List.sum
let sumOfSquaresTo100 =
List . sum ( List . map square [ 1 .. 100 ] )
// You can pipe the output of one operation to the next using "|>"
// Piping data around is very common in F#, similar to UNIX pipes.
// Here is the same sumOfSquares function written using pipes
let sumOfSquaresTo100piped =
[ 1 .. 100 ] | > List . map square | > List . sum // "square" was defined earlier
// you can define lambdas (anonymous functions) using the "fun" keyword
let sumOfSquaresTo100withFun =
2015-10-31 07:40:37 +03:00
[ 1 .. 100 ] | > List . map ( fun x -> x * x ) | > List . sum
2013-06-29 03:26:34 +04:00
// In F# there is no "return" keyword. A function always
// returns the value of the last expression used.
// ------ Pattern Matching ------
// Match..with.. is a supercharged case/switch statement.
let simplePatternMatch =
let x = " a "
match x with
| " a " -> printfn " x is a "
| " b " -> printfn " x is b "
| _ -> printfn " x is something else " // underscore matches anything
// F# doesn't allow nulls by default -- you must use an Option type
2015-10-08 06:11:24 +03:00
// and then pattern match.
2013-06-29 03:26:34 +04:00
// Some(..) and None are roughly analogous to Nullable wrappers
let validValue = Some ( 99 )
let invalidValue = None
// In this example, match..with matches the "Some" and the "None",
// and also unpacks the value in the "Some" at the same time.
let optionPatternMatch input =
match input with
| Some i -> printfn " input is an int=%d " i
| None -> printfn " input is missing "
optionPatternMatch validValue
optionPatternMatch invalidValue
// ------ Printing ------
// 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
2015-10-31 07:40:37 +03:00
printfn " A string %s, and something generic %A " " hello " [ 1 ; 2 ; 3 ; 4 ]
2013-06-29 03:26:34 +04:00
// There are also sprintf/sprintfn functions for formatting data
// into a string, similar to String.Format in C#.
// ================================================
2015-10-08 06:11:24 +03:00
// More on functions
2013-06-29 03:26:34 +04:00
// ================================================
// F# is a true functional language -- functions are first
2015-10-08 16:00:59 +03:00
// class entities and can be combined easily to make powerful
2013-06-29 03:26:34 +04:00
// constructs
// Modules are used to group functions together
// Indentation is needed for each nested module.
2015-10-08 06:11:24 +03:00
module FunctionExamples =
2013-06-29 03:26:34 +04:00
// define a simple adding function
let add x y = x + y
2015-10-08 06:11:24 +03:00
2013-06-29 03:26:34 +04:00
// basic usage of a function
let a = add 1 2
2015-10-31 07:40:37 +03:00
printfn " 1 + 2 = %i " a
2015-10-08 06:11:24 +03:00
2013-06-29 18:54:14 +04:00
// partial application to "bake in" parameters
2013-06-29 03:26:34 +04:00
let add42 = add 42
let b = add42 1
2015-10-31 07:40:37 +03:00
printfn " 42 + 1 = %i " b
2015-10-08 06:11:24 +03:00
2013-06-29 18:54:14 +04:00
// composition to combine functions
2013-06-29 03:26:34 +04:00
let add1 = add 1
let add2 = add 2
let add3 = add1 > > add2
let c = add3 7
2015-10-31 07:40:37 +03:00
printfn " 3 + 7 = %i " c
2015-10-08 06:11:24 +03:00
2013-06-29 03:26:34 +04:00
// higher order functions
[ 1 .. 10 ] | > List . map add3 | > printfn " new list is %A "
2015-10-08 06:11:24 +03:00
2013-06-29 03:26:34 +04:00
// lists of functions, and more
let add6 = [ add1 ; add2 ; add3 ] | > List . reduce ( > > )
let d = add6 7
2015-10-31 07:40:37 +03:00
printfn " 1 + 2 + 3 + 7 = %i " d
2013-06-29 03:26:34 +04:00
// ================================================
2013-06-29 18:54:14 +04:00
// Lists and collection
2013-06-29 03:26:34 +04:00
// ================================================
2013-06-29 18:54:14 +04:00
// There are three types of ordered collection:
2015-10-08 06:11:24 +03:00
// * Lists are most basic immutable collection.
// * Arrays are mutable and more efficient when needed.
// * Sequences are lazy and infinite (e.g. an enumerator).
2013-06-29 18:54:14 +04:00
//
// Other collections include immutable maps and sets
// plus all the standard .NET collections
2015-10-08 06:11:24 +03:00
module ListExamples =
2013-06-29 18:54:14 +04:00
2015-10-08 06:11:24 +03:00
// lists use square brackets
2015-10-31 07:40:37 +03:00
let list1 = [ " a " ; " b " ]
2013-06-29 18:54:14 +04:00
let list2 = " c " :: list1 // :: is prepending
let list3 = list1 @ list2 // @ is concat
2015-10-08 06:11:24 +03:00
2013-06-29 18:54:14 +04:00
// list comprehensions (aka generators)
2015-10-31 07:40:37 +03:00
let squares = [ for i in 1 .. 10 do yield i * i ]
2013-06-29 18:54:14 +04:00
2016-08-04 15:41:16 +03:00
// 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
2013-06-29 18:54:14 +04:00
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 ]
2015-10-08 06:11:24 +03:00
printfn " %A " primes
2013-06-29 18:54:14 +04:00
// pattern matching for lists
2015-10-08 06:11:24 +03:00
let listMatcher aList =
2013-06-29 18:54:14 +04:00
match aList with
2015-10-08 06:11:24 +03:00
| [] -> 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 "
2013-06-29 18:54:14 +04:00
2015-10-31 07:40:37 +03:00
listMatcher [ 1 ; 2 ; 3 ; 4 ]
listMatcher [ 1 ; 2 ]
2013-06-29 18:54:14 +04:00
listMatcher [ 1 ]
2015-10-08 06:11:24 +03:00
listMatcher []
2013-06-29 18:54:14 +04:00
// recursion using lists
2015-10-08 06:11:24 +03:00
let rec sum aList =
2013-06-29 18:54:14 +04:00
match aList with
| [] -> 0
| x :: xs -> x + sum xs
sum [ 1 .. 10 ]
2015-10-08 06:11:24 +03:00
// -----------------------------------------
// Standard library functions
2013-06-29 18:54:14 +04:00
// -----------------------------------------
2015-10-08 06:11:24 +03:00
2013-06-29 18:54:14 +04:00
// map
let add3 x = x + 3
[ 1 .. 10 ] | > List . map add3
// filter
let even x = x % 2 = 0
[ 1 .. 10 ] | > List . filter even
2015-10-08 06:11:24 +03:00
2013-06-29 18:54:14 +04:00
// many more -- see documentation
2015-10-08 06:11:24 +03:00
module ArrayExamples =
2013-06-29 18:54:14 +04:00
// arrays use square brackets with bar
2015-10-31 07:40:37 +03:00
let array1 = [| " a " ; " b " |]
2013-06-29 18:54:14 +04:00
let first = array1 . [ 0 ] // indexed access using dot
2015-10-08 06:11:24 +03:00
2013-06-29 18:54:14 +04:00
// pattern matching for arrays is same as for lists
2015-10-08 06:11:24 +03:00
let arrayMatcher aList =
2013-06-29 18:54:14 +04:00
match aList with
2015-10-08 06:11:24 +03:00
| [| |] -> 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 "
2013-06-29 18:54:14 +04:00
2015-10-31 07:40:37 +03:00
arrayMatcher [| 1 ; 2 ; 3 ; 4 |]
2013-06-29 18:54:14 +04:00
// Standard library functions just as for List
2015-10-08 06:11:24 +03:00
[| 1 .. 10 |]
2015-10-31 07:40:37 +03:00
| > Array . map ( fun i -> i + 3 )
| > Array . filter ( fun i -> i % 2 = 0 )
2013-06-29 18:54:14 +04:00
| > Array . iter ( printfn " value is %i. " )
2015-10-08 06:11:24 +03:00
module SequenceExamples =
2013-06-29 18:54:14 +04:00
// sequences use curly braces
let seq1 = seq { yield " a " ; yield " b " }
2015-10-08 06:11:24 +03:00
// sequences can use yield and
2013-06-29 18:54:14 +04:00
// can contain subsequences
let strange = seq {
2015-10-27 06:21:02 +03:00
// "yield" adds one element
2013-06-29 18:54:14 +04:00
yield 1 ; yield 2 ;
2015-10-08 06:11:24 +03:00
2013-06-29 18:54:14 +04:00
// "yield!" adds a whole subsequence
2015-10-08 06:11:24 +03:00
yield ! [ 5 .. 10 ]
2013-06-29 18:54:14 +04:00
yield ! seq {
2015-10-08 06:11:24 +03:00
for i in 1 .. 10 do
2015-10-31 07:40:37 +03:00
if i % 2 = 0 then yield i } }
2015-10-08 06:11:24 +03:00
// test
strange | > Seq . toList
2013-06-29 18:54:14 +04:00
// 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 )
2015-10-08 06:11:24 +03:00
// test
2013-06-29 18:54:14 +04:00
let fib10 = fib | > Seq . take 10 | > Seq . toList
2015-10-08 06:11:24 +03:00
printf " first 10 fibs are %A " fib10
2013-06-29 18:54:14 +04:00
// ================================================
2015-10-08 06:11:24 +03:00
// Data Types
2013-06-29 18:54:14 +04:00
// ================================================
2013-06-29 03:26:34 +04:00
2015-10-08 06:11:24 +03:00
module DataTypeExamples =
2013-06-29 03:26:34 +04:00
// All data is immutable by default
// Tuples are quick 'n easy anonymous types
2013-06-29 18:54:14 +04:00
// -- Use a comma to create a tuple
2015-10-31 07:40:37 +03:00
let twoTuple = 1 , 2
let threeTuple = " a " , 2 , true
2015-10-08 06:11:24 +03:00
2013-06-29 18:54:14 +04:00
// Pattern match to unpack
2015-10-31 07:40:37 +03:00
let x , y = twoTuple // sets x = 1, y = 2
2013-06-29 03:26:34 +04:00
2015-10-08 06:11:24 +03:00
// ------------------------------------
// Record types have named fields
// ------------------------------------
2013-06-29 18:54:14 +04:00
// Use "type" with curly braces to define a record type
2013-06-29 03:26:34 +04:00
type Person = { First : string ; Last : string }
2015-10-08 06:11:24 +03:00
// Use "let" with curly braces to create a record
2013-06-29 18:54:14 +04:00
let person1 = { First = " John " ; Last = " Doe " }
2013-06-29 03:26:34 +04:00
2013-06-29 18:54:14 +04:00
// Pattern match to unpack
2015-10-31 07:40:37 +03:00
let { First = first } = person1 // sets first="John"
2013-06-29 18:54:14 +04:00
2015-10-08 06:11:24 +03:00
// ------------------------------------
2013-06-29 03:26:34 +04:00
// Union types (aka variants) have a set of choices
2018-06-27 00:43:39 +03:00
// Only one case can be valid at a time.
2015-10-08 06:11:24 +03:00
// ------------------------------------
2013-06-29 18:54:14 +04:00
// Use "type" with bar/pipe to define a union type
2015-10-08 06:11:24 +03:00
type Temp =
2013-06-29 03:26:34 +04:00
| DegreesC of float
| DegreesF of float
2015-10-08 06:11:24 +03:00
2013-06-29 18:54:14 +04:00
// Use one of the cases to create one
2013-06-29 03:26:34 +04:00
let temp1 = DegreesF 98 . 6
let temp2 = DegreesC 37 . 0
2013-06-29 18:54:14 +04:00
// Pattern match on all cases to unpack
let printTemp = function
| DegreesC t -> printfn " %f degC " t
| DegreesF t -> printfn " %f degF " t
2015-10-08 06:11:24 +03:00
printTemp temp1
2013-06-29 18:54:14 +04:00
printTemp temp2
2015-10-08 06:11:24 +03:00
// ------------------------------------
2013-06-29 18:54:14 +04:00
// Recursive types
2015-10-08 06:11:24 +03:00
// ------------------------------------
2013-06-29 03:26:34 +04:00
2015-10-08 06:11:24 +03:00
// Types can be combined recursively in complex ways
2013-06-29 03:26:34 +04:00
// without having to create subclasses
2015-10-08 06:11:24 +03:00
type Employee =
2013-06-29 03:26:34 +04:00
| Worker of Person
| Manager of Employee list
2015-10-31 07:40:37 +03:00
let jdoe = { First = " John " ; Last = " Doe " }
2013-06-29 03:26:34 +04:00
let worker = Worker jdoe
2015-10-08 06:11:24 +03:00
// ------------------------------------
2015-10-24 09:17:35 +03:00
// Modeling with types
2015-10-08 06:11:24 +03:00
// ------------------------------------
2015-10-24 09:17:35 +03:00
// Union types are great for modeling state without using flags
2015-10-08 06:11:24 +03:00
type EmailAddress =
2013-06-29 18:54:14 +04:00
| ValidEmailAddress of string
| InvalidEmailAddress of string
let trySendEmail email =
match email with // use pattern matching
| ValidEmailAddress address -> () // send
2016-03-15 17:28:11 +03:00
| InvalidEmailAddress address -> () // don't send
2013-06-29 03:26:34 +04:00
// The combination of union types and record types together
// provide a great foundation for domain driven design.
2015-10-08 06:11:24 +03:00
// You can create hundreds of little types that accurately
2013-06-29 03:26:34 +04:00
// 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 }
2015-10-08 06:11:24 +03:00
type ShoppingCart =
2013-06-29 03:26:34 +04:00
| EmptyCart // no data
| ActiveCart of ActiveCartData
2015-10-08 06:11:24 +03:00
| PaidCart of PaidCartData
2013-06-29 03:26:34 +04:00
2015-10-08 06:11:24 +03:00
// ------------------------------------
2013-06-29 18:54:14 +04:00
// Built in behavior for types
2015-10-08 06:11:24 +03:00
// ------------------------------------
2013-06-29 18:54:14 +04:00
// Core types have useful "out-of-the-box" behavior, no coding needed.
// * Immutability
// * Pretty printing when debugging
// * Equality and comparison
// * Serialization
2015-10-08 06:11:24 +03:00
2013-06-29 18:54:14 +04:00
// Pretty printing using %A
2015-10-08 06:11:24 +03:00
printfn " twoTuple=%A, \n Person=%A, \n Temp=%A, \n Employee=%A "
2013-06-29 03:26:34 +04:00
twoTuple person1 temp1 worker
2013-06-29 18:54:14 +04:00
// Equality and comparison built in.
// Here's an example with cards.
type Suit = Club | Diamond | Spade | Heart
2015-10-08 06:11:24 +03:00
type Rank = Two | Three | Four | Five | Six | Seven | Eight
| Nine | Ten | Jack | Queen | King | Ace
2013-06-29 18:54:14 +04:00
2015-10-31 07:40:37 +03:00
let hand = [ Club , Ace ; Heart , Three ; Heart , Ace ;
Spade , Jack ; Diamond , Two ; Diamond , Ace ]
2013-06-29 18:54:14 +04:00
// 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 "
2015-10-08 06:11:24 +03:00
2013-06-29 03:26:34 +04:00
// ================================================
// Active patterns
// ================================================
2015-10-08 06:11:24 +03:00
module ActivePatternExamples =
2013-06-29 03:26:34 +04:00
2015-10-08 06:11:24 +03:00
// F# has a special type of pattern matching called "active patterns"
// where the pattern can be parsed or detected dynamically.
2013-06-29 03:26:34 +04:00
2013-06-29 18:54:14 +04:00
// "banana clips" are the syntax for active patterns
2015-10-08 06:11:24 +03:00
2016-11-12 20:29:18 +03:00
// You can use "elif" instead of "else if" in conditional expressions.
// They are equivalent in F#
2013-06-29 03:26:34 +04:00
// for example, define an "active" pattern to match character types...
2015-10-08 06:11:24 +03:00
let ( | Digit | Letter | Whitespace | Other | ) ch =
2013-06-29 03:26:34 +04:00
if System . Char . IsDigit ( ch ) then Digit
2016-11-12 20:29:18 +03:00
elif System . Char . IsLetter ( ch ) then Letter
elif System . Char . IsWhiteSpace ( ch ) then Whitespace
2015-10-08 06:11:24 +03:00
else Other
2013-06-29 03:26:34 +04:00
// ... and then use it to make parsing logic much clearer
2015-10-08 06:11:24 +03:00
let printChar ch =
2013-06-29 03:26:34 +04:00
match ch with
| Digit -> printfn " %c is a Digit " ch
| Letter -> printfn " %c is a Letter " ch
| Whitespace -> printfn " %c is a Whitespace " ch
| _ -> printfn " %c is something else " ch
// print a list
2015-10-31 07:40:37 +03:00
[ 'a' ; 'b' ; '1' ; ' ' ; '-' ; 'c' ] | > List . iter printChar
2013-06-29 03:26:34 +04:00
2013-06-29 18:54:14 +04:00
// -----------------------------------
// FizzBuzz using active patterns
// -----------------------------------
2015-10-08 06:11:24 +03:00
2013-06-29 18:54:14 +04:00
// You can create partial matching patterns as well
2016-03-15 17:28:11 +03:00
// Just use underscore in the definition, and return Some if matched.
2013-06-29 18:54:14 +04:00
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
2015-10-08 06:11:24 +03:00
let fizzBuzz i =
2013-06-29 18:54:14 +04:00
match i with
2015-10-08 06:11:24 +03:00
| MultOf3 & MultOf5 -> printf " FizzBuzz, "
| MultOf3 -> printf " Fizz, "
| MultOf5 -> printf " Buzz, "
2013-06-29 18:54:14 +04:00
| _ -> printf " %i, " i
2015-10-08 06:11:24 +03:00
2013-06-29 18:54:14 +04:00
// test
2015-10-08 06:11:24 +03:00
[ 1 .. 20 ] | > List . iter fizzBuzz
2013-06-29 03:26:34 +04:00
// ================================================
2015-10-08 06:11:24 +03:00
// Conciseness
2013-06-29 03:26:34 +04:00
// ================================================
2015-10-08 06:11:24 +03:00
module AlgorithmExamples =
2013-06-29 03:26:34 +04:00
2015-10-08 06:11:24 +03:00
// F# has a high signal/noise ratio, so code reads
2013-06-29 03:26:34 +04:00
// almost like the actual algorithm
// ------ Example: define sumOfSquares function ------
2015-10-08 06:11:24 +03:00
let sumOfSquares n =
2013-06-29 03:26:34 +04:00
[ 1 .. n ] // 1) take all the numbers from 1 to n
| > List . map square // 2) square each one
| > List . sum // 3) sum the results
2015-10-08 06:11:24 +03:00
// test
sumOfSquares 100 | > printfn " Sum of squares = %A "
// ------ Example: define a sort function ------
2013-06-29 03:26:34 +04:00
let rec sort list =
match list with
2015-10-08 06:11:24 +03:00
// If the list is empty
| [] ->
2013-06-29 03:26:34 +04:00
[] // return an empty list
2015-10-08 06:11:24 +03:00
// If the list is not empty
| firstElem :: otherElements -> // take the first element
let smallerElements = // extract the smaller elements
2013-06-29 03:26:34 +04:00
otherElements // from the remaining ones
2015-10-08 06:11:24 +03:00
| > List . filter ( fun e -> e < firstElem )
2013-06-29 03:26:34 +04:00
| > sort // and sort them
let largerElements = // extract the larger ones
otherElements // from the remaining ones
| > List . filter ( fun e -> e > = firstElem )
| > sort // and sort them
// Combine the 3 parts into a new list and return it
List . concat [ smallerElements ; [ firstElem ] ; largerElements ]
// test
2015-10-31 07:40:37 +03:00
sort [ 1 ; 5 ; 23 ; 18 ; 9 ; 1 ; 3 ] | > printfn " Sorted = %A "
2013-06-29 03:26:34 +04:00
// ================================================
// Asynchronous Code
// ================================================
2015-10-08 06:11:24 +03:00
module AsyncExample =
2013-06-29 03:26:34 +04:00
2013-06-29 18:54:14 +04:00
// F# has built-in features to help with async code
2013-06-29 03:26:34 +04:00
// without encountering the "pyramid of doom"
//
// The following example downloads a set of web pages in parallel.
open System.Net
open System
open System.IO
2015-10-08 06:11:24 +03:00
open Microsoft.FSharp.Control.CommonExtensions
2013-06-29 03:26:34 +04:00
// Fetch the contents of a URL asynchronously
2015-10-08 06:11:24 +03:00
let fetchUrlAsync url =
async { // "async" keyword and curly braces
2013-06-29 18:54:14 +04:00
// creates an "async" object
2015-10-08 06:11:24 +03:00
let req = WebRequest . Create ( Uri ( url ) )
use ! resp = req . AsyncGetResponse ()
2013-06-29 18:54:14 +04:00
// use! is async assignment
2015-10-08 06:11:24 +03:00
use stream = resp . GetResponseStream ()
2013-06-29 18:54:14 +04:00
// "use" triggers automatic close()
// on resource at end of scope
2015-10-08 06:11:24 +03:00
use reader = new IO . StreamReader ( stream )
let html = reader . ReadToEnd ()
printfn " finished downloading %s " url
2013-06-29 03:26:34 +04:00
}
2015-10-08 06:11:24 +03:00
2013-06-29 03:26:34 +04:00
// a list of sites to fetch
let sites = [ " http://www.bing.com " ;
" http://www.google.com " ;
" http://www.microsoft.com " ;
" http://www.amazon.com " ;
" http://www.yahoo.com " ]
// do it
2015-10-08 06:11:24 +03:00
sites
2013-06-29 03:26:34 +04:00
| > List . map fetchUrlAsync // make a list of async tasks
| > Async . Parallel // set up the tasks to run in parallel
| > Async . RunSynchronously // start them off
// ================================================
2015-10-24 09:17:35 +03:00
// .NET compatibility
2013-06-29 03:26:34 +04:00
// ================================================
2015-10-08 06:11:24 +03:00
module NetCompatibilityExamples =
2013-06-29 03:26:34 +04:00
// F# can do almost everything C# can do, and it integrates
// seamlessly with .NET or Mono libraries.
// ------- work with existing library functions -------
2015-10-08 06:11:24 +03:00
2015-10-31 07:40:37 +03:00
let ( i1success , i1 ) = System . Int32 . TryParse ( " 123 " ) ;
2013-06-29 03:26:34 +04:00
if i1success then printfn " parsed as %i " i1 else printfn " parse failed "
// ------- Implement interfaces on the fly! -------
2015-10-08 06:11:24 +03:00
2013-06-29 03:26:34 +04:00
// create a new object that implements IDisposable
2015-10-08 06:11:24 +03:00
let makeResource name =
{ new System . IDisposable
2013-06-29 03:26:34 +04:00
with member this . Dispose () = printfn " %s disposed " name }
2015-10-08 06:11:24 +03:00
let useAndDisposeResources =
2013-06-29 03:26:34 +04:00
use r1 = makeResource " first resource "
2015-10-08 06:11:24 +03:00
printfn " using first resource "
2013-06-29 03:26:34 +04:00
for i in [ 1 .. 3 ] do
let resourceName = sprintf " \t inner resource %d " i
2015-10-08 06:11:24 +03:00
use temp = makeResource resourceName
printfn " \t do something with %s " resourceName
2013-06-29 03:26:34 +04:00
use r2 = makeResource " second resource "
2015-10-08 06:11:24 +03:00
printfn " using second resource "
printfn " done. "
2013-06-29 03:26:34 +04:00
// ------- Object oriented code -------
2015-10-08 06:11:24 +03:00
2013-06-29 03:26:34 +04:00
// F# is also a fully fledged OO language.
// It supports classes, inheritance, virtual methods, etc.
2013-06-29 18:54:14 +04:00
// interface with generic type
2015-10-08 06:11:24 +03:00
type IEnumerator < ' a > =
2013-06-29 03:26:34 +04:00
abstract member Current : ' a
2015-10-08 06:11:24 +03:00
abstract MoveNext : unit -> bool
2013-06-29 03:26:34 +04:00
// abstract base class with virtual methods
[< AbstractClass >]
2015-10-08 06:11:24 +03:00
type Shape () =
2015-10-31 07:40:37 +03:00
// readonly properties
2013-06-29 03:26:34 +04:00
abstract member Width : int with get
abstract member Height : int with get
2015-10-31 07:40:37 +03:00
// non-virtual method
2013-06-29 03:26:34 +04:00
member this . BoundingArea = this . Height * this . Width
2015-10-31 07:40:37 +03:00
// virtual method with base implementation
2015-10-08 06:11:24 +03:00
abstract member Print : unit -> unit
2013-06-29 03:26:34 +04:00
default this . Print () = printfn " I'm a shape "
2015-10-08 06:11:24 +03:00
// concrete class that inherits from base class and overrides
type Rectangle ( x : int , y : int ) =
2013-06-29 03:26:34 +04:00
inherit Shape ()
override this . Width = x
override this . Height = y
override this . Print () = printfn " I'm a Rectangle "
2015-10-31 07:40:37 +03:00
// test
let r = Rectangle ( 2 , 3 )
2013-06-29 03:26:34 +04:00
printfn " The width is %i " r . Width
printfn " The area is %i " r . BoundingArea
2015-10-08 06:11:24 +03:00
r . Print ()
2013-06-29 03:26:34 +04:00
// ------- extension methods -------
2015-10-08 06:11:24 +03:00
2015-10-31 07:40:37 +03:00
// Just as in C#, F# can extend existing classes with extension methods.
2013-06-29 03:26:34 +04:00
type System . String with
member this . StartsWithA = this . StartsWith " A "
2015-10-31 07:40:37 +03:00
// test
2013-06-29 03:26:34 +04:00
let s = " Alice "
2015-10-08 06:11:24 +03:00
printfn " '%s' starts with an 'A' = %A " s s . StartsWithA
2013-06-29 03:26:34 +04:00
// ------- events -------
2015-10-08 06:11:24 +03:00
2013-06-29 03:26:34 +04:00
type MyButton () =
let clickEvent = new Event < _ > ()
[< CLIEvent >]
member this . OnClick = clickEvent . Publish
member this . TestEvent ( arg ) =
clickEvent . Trigger ( this , arg )
// test
let myButton = new MyButton ()
2015-10-08 06:11:24 +03:00
myButton . OnClick . Add ( fun ( sender , arg ) ->
2013-06-29 03:26:34 +04:00
printfn " Click event with arg=%O " arg )
myButton . TestEvent ( " Hello World! " )
2015-10-08 06:11:24 +03:00
2013-06-29 03:26:34 +04:00
` ` `
# # More Information
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/).