learnxinyminutes-docs/fsharp.html.markdown

638 lines
21 KiB
FSharp
Raw Normal View History

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
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
The syntax of F# is different from C-style languages:
2013-06-29 03:26:34 +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.
```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
// 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
// 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
// ================================================
// Lists and collection
2013-06-29 03:26:34 +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).
//
// Other collections include immutable maps and sets
// plus all the standard .NET collections
2015-10-08 06:11:24 +03:00
module ListExamples =
2015-10-08 06:11:24 +03:00
// lists use square brackets
2015-10-31 07:40:37 +03:00
let list1 = ["a"; "b"]
let list2 = "c" :: list1 // :: is prepending
let list3 = list1 @ list2 // @ is concat
2015-10-08 06:11:24 +03:00
// list comprehensions (aka generators)
2015-10-31 07:40:37 +03:00
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]
2015-10-08 06:11:24 +03:00
printfn "%A" primes
// pattern matching for lists
2015-10-08 06:11:24 +03:00
let listMatcher aList =
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"
2015-10-31 07:40:37 +03:00
listMatcher [1; 2; 3; 4]
listMatcher [1; 2]
listMatcher [1]
2015-10-08 06:11:24 +03:00
listMatcher []
// recursion using lists
2015-10-08 06:11:24 +03:00
let rec sum aList =
match aList with
| [] -> 0
| x::xs -> x + sum xs
sum [1..10]
2015-10-08 06:11:24 +03:00
// -----------------------------------------
// Standard library functions
// -----------------------------------------
2015-10-08 06:11:24 +03: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
// many more -- see documentation
2015-10-08 06:11:24 +03:00
module ArrayExamples =
// arrays use square brackets with bar
2015-10-31 07:40:37 +03:00
let array1 = [| "a"; "b" |]
let first = array1.[0] // indexed access using dot
2015-10-08 06:11:24 +03:00
// pattern matching for arrays is same as for lists
2015-10-08 06:11:24 +03:00
let arrayMatcher aList =
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"
2015-10-31 07:40:37 +03:00
arrayMatcher [| 1; 2; 3; 4 |]
// 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)
|> Array.iter (printfn "value is %i. ")
2015-10-08 06:11:24 +03:00
module SequenceExamples =
// sequences use curly braces
let seq1 = seq { yield "a"; yield "b" }
2015-10-08 06:11:24 +03:00
// sequences can use yield and
// can contain subsequences
let strange = seq {
// "yield" adds one element
yield 1; yield 2;
2015-10-08 06:11:24 +03:00
// "yield!" adds a whole subsequence
2015-10-08 06:11:24 +03:00
yield! [5..10]
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
// 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
let fib10 = fib |> Seq.take 10 |> Seq.toList
2015-10-08 06:11:24 +03:00
printf "first 10 fibs are %A" fib10
// ================================================
2015-10-08 06:11:24 +03:00
// Data Types
// ================================================
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
// -- 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
// 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
// ------------------------------------
// 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
let person1 = {First="John"; Last="Doe"}
2013-06-29 03:26:34 +04:00
// Pattern match to unpack
2015-10-31 07:40:37 +03:00
let {First = first} = person1 // sets first="John"
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
// Only one case can be valid at a time.
2015-10-08 06:11:24 +03: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
// 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
// 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
printTemp temp2
2015-10-08 06:11:24 +03: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
// ------------------------------------
// Modeling with types
2015-10-08 06:11:24 +03:00
// ------------------------------------
// Union types are great for modeling state without using flags
2015-10-08 06:11:24 +03:00
type EmailAddress =
| ValidEmailAddress of string
| InvalidEmailAddress of string
let trySendEmail email =
match email with // use pattern matching
| ValidEmailAddress address -> () // send
| 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
// ------------------------------------
// Built in behavior for types
2015-10-08 06:11:24 +03: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
// Pretty printing using %A
2015-10-08 06:11:24 +03:00
printfn "twoTuple=%A,\nPerson=%A,\nTemp=%A,\nEmployee=%A"
2013-06-29 03:26:34 +04:00
twoTuple person1 temp1 worker
// 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
2015-10-31 07:40:37 +03:00
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"
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
// "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
// -----------------------------------
// FizzBuzz using active patterns
// -----------------------------------
2015-10-08 06:11:24 +03:00
// You can create partial matching patterns as well
// 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
2015-10-08 06:11:24 +03:00
let fizzBuzz i =
match i with
2015-10-08 06:11:24 +03:00
| MultOf3 & MultOf5 -> printf "FizzBuzz, "
| MultOf3 -> printf "Fizz, "
| MultOf5 -> printf "Buzz, "
| _ -> printf "%i, " i
2015-10-08 06:11:24 +03: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
// 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
// creates an "async" object
2015-10-08 06:11:24 +03:00
let req = WebRequest.Create(Uri(url))
use! resp = req.AsyncGetResponse()
// use! is async assignment
2015-10-08 06:11:24 +03:00
use stream = resp.GetResponseStream()
// "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
// ================================================
// .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 "\tinner resource %d" i
2015-10-08 06:11:24 +03:00
use temp = makeResource resourceName
printfn "\tdo 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.
// 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/).