Basic Discriminated Unions

Category:
Defining Types
Description: Discriminated unions give a way of building types from the disjoint union of two or more existing types. This sample shows how to build one such type and how to decompose its values.
Code:
type wheel = Wheel of float  // radius of wheel, inches
type cycle =
| Unicycle of wheel
| Bicycle of wheel * wheel

let veryBigWheel = Wheel(26.0)
let bigWheel = Wheel(13.0)
let smallWheel = Wheel(6.0)

let pennyFarthing = Bicycle(veryBigWheel, smallWheel)
let racer = Bicycle(bigWheel, bigWheel)
let kidsBike = Bicycle(smallWheel, smallWheel)

let UnionSample1() =
let show bike =
match bike with
| Unicycle (Wheel r) -> printfn "Unicycle, one wheel, radius = %f" r
| Bicycle (Wheel r1,Wheel r2) -> printfn "Bicycle, two wheels, front = %f, back = %f" r1 r2
show pennyFarthing;
show racer;
show kidsBike

Execution Result:
Bicycle, two wheels, front = 26.000000, back = 6.000000
Bicycle, two wheels, front = 13.000000, back = 13.000000
Bicycle, two wheels, front = 6.000000, back = 6.000000

Discriminated Unions as Trees

Category:
Defining Types
Description: Discriminated unions are excellent for representing tree structures.
Code:
type Tree<'a> =
| Node of 'a * Tree<'a> * Tree<'a>
| Tip

let UnionSample2() =
let tree = Node(4, Node(2, Node(1, Tip, Tip), Node(6, Tip, Tip)),
Node(6, Node(5, Tip, Tip), Node(7, Tip, Tip)))
// 4
// 2 6
// 1 3 5 7
// InOrder : Tree<'a> -> list<'a>
let rec InOrder t =
match t with
| Node(x,left,right) -> (InOrder left) @ [x] @ (InOrder right)
| Tip -> []

// Height : Tree<'a> -> int
let rec Height t =
match t with
| Node(x,left,right) -> 1 + (max (Height left) (Height right))
| Tip -> 0

printfn "The tree in order = %A" (InOrder tree)
printfn "\nThe height of the tree = %d" (Height tree)


Execution Result:
The tree in order = [1; 2; 6; 4; 5; 6; 7]

The height of the tree = 3

Basic Records

Category:
Defining Types
Description: Records are concrete type definitions that hold data in an unordered, named structure.
Code:
type Point = { x: float; y: float}
type Triangle = { p1: Point; p2: Point; p3: Point }
type Vector = { dx: float; dy: float}

let origin = { x = 0.0; y = 0.0 }
let onex = { x = 1.0; y = 0.0 }
let oney = { x = 0.0; y = 1.0 }
let diff p1 p2 = { dx = p2.x - p1.x; dy = p2.y - p1.y }
let sides tri =
diff tri.p2 tri.p1,
diff tri.p3 tri.p2,
diff tri.p1 tri.p3

let RecordSample1() =
let triangle1 = { p1=origin;p2=onex;p3=oney }
printfn "triangle1 = \n%A" triangle1;
printfn "sides(triangle1) = \n%A" (sides triangle1)

Execution Result:
triangle1 = 
{p1 = {x = 0.0;
y = 0.0;};
p2 = {x = 1.0;
y = 0.0;};
p3 = {x = 0.0;
y = 1.0;};}
sides(triangle1) =
({dx = -1.0;
dy = 0.0;}, {dx = 1.0;
dy = -1.0;}, {dx = 0.0;
dy = 1.0;})

More Basic Records

Category:
Defining Types
Description: Examples of cloning records using 'with' keyword and accessing record fields using . (dot).
Code:
type simpleRecord = {Name: string; Job: string; Wage: int}

let RecordSample2() =
let worker = { Name = "Bill"; Job = "Cashier"; Wage = 20 }

let worker2 = { worker with Name = "John"; Wage = 15 }

printfn "%s is a %s and makes $%d per hour" worker.Name worker.Job worker.Wage
printfn "\n%s is a %s and makes $%d per hour" worker2.Name worker2.Job worker2.Wage

Execution Result:
Bill is a Cashier and makes $20 per hour

John is a Cashier and makes $15 per hour

Records with Members

Category:
Defining Types
Description: F# allows values to be associated with type names, i.e. the type name can be used as a container for 'members' of that type. These are treated the same way as .NET object model members.
Code:
type PointWithMembers = 
{ x: float; y: float}
member p.VectorFromOrigin = { dx = p.x; dy = p.y }
static member Origin = { x = 0.0; y = 0.0 }
static member (+) ((p:PointWithMembers),(v:VectorWithMembers)) =
{ x = p.x + v.dx; y = p.y + v.dy }

and VectorWithMembers =
{ dx: float; dy: float}
static member Zero = { dx = 0.0; dy = 0.0 }
static member OneX = { dx = 1.0; dy = 0.0 }
static member OneY = { dx = 0.0; dy = 1.0 }
static member (+) ((v1:VectorWithMembers),(v2:VectorWithMembers)) =
{ dx = v1.dx + v2.dx; dy = v1.dy + v2.dy }

let MemberSample1() =
printfn "Point.Origin = \n%A" PointWithMembers.Origin;
printfn "Point.Origin + Vector.OneX = \n%A" (PointWithMembers.Origin + VectorWithMembers.OneX);
printfn "Vector.OneX + Vector.OneY = \n%A" (VectorWithMembers.OneX + VectorWithMembers.OneY)

Execution Result:
Point.Origin = 
{x = 0.0;
y = 0.0;}
Point.Origin + Vector.OneX =
{x = 1.0;
y = 0.0;}
Vector.OneX + Vector.OneY =
{dx = 1.0;
dy = 1.0;}

Enumerations

Category:
Defining Types
Description:
Code:
type car = {Make: string; Model: string; Year: int}

let Pattern1() =
let car1 = {Make = "Porsche"; Model = "911"; Year = 2006}

let car2 = {Make = "BMW"; Model = "335i"; Year = 2008}

let car3 = {Make = "Subaru"; Model = "Impreza WRX"; Year = 2007}

let allCars = [ car1; car2; car3 ]

let subarus =
allCars
|> List.filter
(function
| {Make = "Subaru"} -> true
| _ -> false )

printfn "subarus = %A" subarus

Execution Result:
subarus = [{Make = "Subaru";
Model = "Impreza WRX";
Year = 2007;}]

Mutual Definitions

Category:
Defining Types
Description: Discriminated unions and records can be defined simultaneously to give mutually recursive collections of types.
Code:
// define mutually recursive collections with the 'and' keyword
type Airport =
{ Name: string;
Connections: connection list }
and connection =
| Connection of Airport
| None

let MutualDefinition() =
let LAX = { Name = "Los Angeles Intl Airport"; Connections = [connection.None] }
let DTW = { Name = "Detroit Metro Wayne County"; Connections = [connection.None] }
let SEA = { Name = "Seattle-Tacoma Intl Airport";
Connections = [connection.Connection DTW; connection.Connection LAX] }

printfn "%A" LAX
printfn "\n%A" DTW
printfn "\n%A" SEA

Execution Result:
{Name = "Los Angeles Intl Airport";
Connections = [None];}

{Name = "Detroit Metro Wayne County";
Connections = [None];}

{Name = "Seattle-Tacoma Intl Airport";
Connections =
[Connection {Name = "Detroit Metro Wayne County";
Connections = [None];};
Connection {Name = "Los Angeles Intl Airport";
Connections = [None];}];}

Enumerations

Category:
Defining Types
Description: Enums are lightweight types, similar to discriminated unions. Unlike discriminated unions, each data tag must be given a constant value of the same type
Code:
type FaceCard =
| Jack = 11
| Queen = 12
| King = 13
| Ace = 1

let EnumSample() =
let isAce card =
match card with
| FaceCard.Ace -> true
| _ -> false

printfn "Checking if a Jack is an Ace.....%A" (isAce FaceCard.Jack)

Execution Result:
Checking if a Jack is an Ace.....false

Structs

Category:
Defining Types
Description: Structs are lightweight data structures, stored on the Stack. They use value equality comparison.
Code:
[]
type StructBox (width: float, height: float, depth: float) =
member box.Width = width
member box.Height = height
member box.Depth = depth

type StructVector2D (dx: float, dy: float) =
struct
member v.DX = dx
member v.DY = dy
member v.Length = sqrt (v.DX * v.DX + v.DY * v.DY)
end

let structSample() =
// a simple struct - must use [] attribute tag or 'struct' 'end' keywords to define as struct
// []
// type StructBox (width: float, height: float, depth: float) =
// member box.Width = width
// member box.Height = height
// member box.Depth = depth

// type StructVector2D (dx: float, dy: float) =
// struct
// member v.DX = dx
// member v.DY = dy
// member v.Length = sqrt (v.DX * v.DX + v.DY * v.DY)
// end

let b1 = StructBox(2.0, 3.0, 6.0)
let v1 = StructVector2D(3.0, 4.0)
let v2 = StructVector2D(3.0, 4.0)

printfn "b1.Width = %A" b1.Width
printfn"\nv1.Length = %A" v1.Length
printfn"\nv1 = v2?\t%A" (v1=v2)

Execution Result:
b1.Width = 2.0

v1.Length = 5.0

v1 = v2? true

Constructing Classes

Category:
Defining Types
Description: Classes are custom types containing data (known as 'fields'), methods, and properties ( methods without parameters). Classes are reference types and are stored on the Heap. Classes must be constructed and instantiated to use. Unlike C#, no default constructor is provided, one must be defined.
Code:
type Circle (r: float) =
let area = System.Math.PI * r * r
do printfn "Initialized a circle with radius %f\n" r

member this.R = r
member this.Area = area

new () = new Circle(1.0)

new(text: string) =
if text = null then
raise <| new System.Exception("text")

let (successR, r) = System.Double.TryParse(text)
if not successR then
raise <| new System.Exception("text")
new Circle(r: float)


let ClassesSupport() =
let c1 = new Circle()
let area1 = c1.Area

let c2 = new Circle(6.0)
let area2 = c2.Area

let c3 = new Circle("3")
let area3 = c3.Area

printfn "area1 = %f\n" area1
printfn "area2 = %f\n" area2
printfn "area3 = %f\n" area3

Execution Result:
Initialized a circle with radius 1.000000

Initialized a circle with radius 6.000000

Initialized a circle with radius 3.000000

area1 = 3.141593

area2 = 113.097336

area3 = 28.274334


Classes with methods and properties

Category:
Defining Types
Description: Classes can contain methods of general form and properties.
Code:
type Circle2 (r : float) =    
let mutable radius = r
let area = System.Math.PI * r * r

// Read/Write property with get and set
member c.R with get() = radius and set(inp) = radius <- inp
// Read only property with get
member c.Area with get() = area

new () = Circle2(1.0)

new(text: string) =
if text = null then
raise <| new System.Exception("text")

let (successR, r) = System.Double.TryParse(text)
if not successR then
raise <| new System.Exception("text")
new Circle2(r: float)

// method with signature: member CalculateCircumference : unit -> float
member c.CalculateCircumference() = System.Math.PI * 2.0 * c.R

let ClassesSupport2() =
let c1 = Circle2(2.0)

printfn "c1.Area = %f\tc1.R = %f\n" c1.Area c1.R
c1.R <- 5.0
printfn "c1.Area = %f\tc1.R = %f\n" c1.Area c1.R

c1.CalculateCircumference()

Execution Result:
c1.Area = 12.566371	c1.R = 2.000000

c1.Area = 12.566371 c1.R = 5.000000


define class

Category:
Defining Types
Description: define a class with multiple constructors
Code:
type Complex(real : float, img  : float) =
new (r : float) = Complex (r, 0.0)
new() = Complex(0.0, 0.0)
member this.Real = real
member this.Img = img
member this.Multiply(other : Complex) =
Complex(this.Real*other.Real - this.Img*other.Img,
this.Real*other.Img + this.Img*other.Real)
member this.Add(r : float) =
new Complex(real + r, img)
member this.Add(r : float, i : float) =
new Complex(real + r, img + i)
let ClassSample1() =
let a = Complex()
let b = Complex(1.2,2.5)
let c = Complex(1.0)
printfn "a=(%f,%f), b=(%f,%f) c=(%f,%f)" a.Real a.Img b.Real b.Img c.Img c.Real

Execution Result:
a=(0.000000,0.000000), b=(1.200000,2.500000) c=(0.000000,1.000000)

Define a class with mutable variable

Category:
Defining Types
Description: Define a class with mutable variable
Code:
type BuilderState =
| None
| Active
| Completed

type Builder() =
let mutable state = None
member this.CurrentState
with get() = state

member this.Begin() =
state <- Active

member this.End() =
// complete the build process...
state <- Completed

let ClassSample2() =
let b = Builder()
printfn "current state = %A" b.CurrentState
b.Begin();
printfn "current state = %A" b.CurrentState
b.End();
printfn "current state = %A" b.CurrentState

Execution Result:
current state = None
current state = Active
current state = Completed

Define a mutable record

Category:
Defining Types
Description: Define a mutable record
Code:
type Employee = 
{ Name : string;
StartDate : string;
mutable Salary : float }

let TypeRecordSample1() =
let steve = { Name = "Steve"; StartDate = "01 Jan 2000"; Salary = 30000.0 }
let joe = { Name = "Joe"; StartDate = "25 Dec 2001"; Salary = 45000.0 }

let giveRaise (emp : Employee, increase : float ) =
emp.Salary <- (1.0 + increase)*emp.Salary
printfn "%s: Salary=%f" emp.Name emp.Salary

giveRaise (steve, 0.05)
giveRaise (joe, 0.10)

//--------------------------------------------------------

Execution Result:
Steve: Salary=31500.000000
Joe: Salary=49500.000000

Last edited Sep 12, 2011 at 11:17 PM by ttliu2000, version 1

Comments

No comments yet.