The Record type is the simplest concrete data structure in F# and is immutable by default. A record is defined using the type keyword, followed by the identifier for the record, an equals sign, braces and a list of labels for the record.
1 #light
2 #r "FSharp.PowerPack.dll"
3
4 (*
5 Example 1: A record type describing a
6 programming language
7 *)
8
9 // define the record
10 type Language =
11 { Name: string; // label:type
12 Company: string;
13 Age: int
14 }
15
16 // create a record and fill in fields
17 let rec1 =
18 { Name = "F#";
19 Company = "Microsoft";
20 Age = 2
21 }
22 // the signature of rec1 is as follows:
23 > val rec1 : Language
Cloning Records
F# provides a nice way to create a new record based on an existing record, but allows you to change just the necessary fields. You can think of it as the BasedOn command used in XAML Styles, where you can define a new style based on an existing one, just modifying the parts of it as needed. For instance, let's say that for some reason, as F# ages, we want to create a record to indicate this change.
24 // Example 2: Cloning records
25 // clone the original rec1 record
26 // and modify the Age field
27 // Note that this reduces the work needed
28 // to be done to create rec2
29 let rec2 = {rec1 with Age = 3}
30
31 // notice that rec2 retains the information
32 // from rec1 while only changing the Age field
33 > val it : Language =
34 { Name = "F#";
35 Company = "Microsoft";
36 Age = 3;}
Mutating Records
Recall that record types are immutable so once a record is created, it cannot be changed. However, if you want a record to be modified without creating a new instance of it, you can mark the necessary fields that is to be modified with the mutable keyword. In addition, the left operator <- and familiar dot notation can be used to modify a record's field. Only fields that have been marked as mutable can be changed, and any attempt to modify a non-mutable field results in a compiler error.
37 //Example 3: Mutating a record
38 // define the record
39 type Language2 =
40 { Name : string; // label:type of label
41 Company : string;
42 mutable Age : int
43 }
44
45 let rec3 =
46 { Name = "F#";
47 Company = "Microsoft";
48 Age = 2
49 }
50
51 rec3;;
52 > val it : Language2 = {Name = "F#";
53 Company = "Microsoft";
54 Age = 2;}
55
56 // modify the Age field
57 // can use dot notation and get intellisense
58 rec3.Age <- 4
59 > val it : unit = ()
60
61 // show the mutated record
62 rec3;;
63 > val it : Language2 =
64 { Name = "F#";
65 Company = "Microsoft";
66 Age = 4;}
67
68 // attempt to modify a non-mutable field
69 rec3.Name <- "Microsoft"
70 // will get the error:
71 error FS0005: This field is not mutable
Methods, Properties, Indexers, Operator Overloading
As a type in F#, records lend themselves to a variety of augmentations available to types in the .NET world. Let me illustrate how some of these are implemented in F#.
1 #light
2 #r "FSharp.PowerPack.dll"
3
4 type Complex =
5 {
6 // create mutable fields so that they can
7 // be modified
8 mutable re : float;
9 mutable im : float
10 } with
11
12 //[1] Magnitude property
13 member c.Magnitude =
14 sqrt(c.re * c.re + c.im + c.im)
15
16 //[2] static Unit property
17 // create a zero based complex number
18 static member Zero = {re = 0.; im = 0.}
19
20 //[3] Indexer + Get/Set
21 // allows you to index into the complex type
22 // where c.[0] will get/set the real part and
23 // c.[1] will get/set the imaginary part
24 // this also demos the get/set methods used to
25 // update properties
26 member c.Item
27 with get(index) =
28 match index with
29 | 0 -> c.re
30 | 1 -> c.im
31 | _ -> invalid_arg "Complex.get"
32 and set index value =
33 match index with
34 | 0 -> c.re <- value
35 | 1 -> c.im <- value
36 | _ -> invalid_arg "Complex.set"
37
38 //[4] Operator Overloading
39 static member (+) (c1:Complex, c2:Complex) =
40 { re = c1.re + c2.re ;
41 im = c1.im + c2.im }
42
43 //[5] Display method
44 // convert re and im to strings
45 // and print to screen
46 member c.Display() =
47 string c.re + "," + string c.im + "i"
48
49 // the signature of the Complex type is:
50 >
51 type Complex =
52 {mutable re: float;
53 mutable im: float;}
54 with
55 member Display : unit -> string
56 member Item : index:int -> float with get
57 member Magnitude : float
58 static member Zero : Complex
59 static member ( + ) :
60 c1:Complex * c2:Complex -> Complex
61 member Item : index:int -> float with set
62 end
63
64
65 // let's take a look at how we use all
66 // these features
67 let d = {re = 3.; im = 4.} // create complex
68 let e = {re = 2.; im = 4.} // numbers to use
69
70 //[1] Magnitude of d
71 d.Magnitude;;
72 > val it : float = 4.123105626
73
74 //[2] Zero complex number
75 let f = Complex.Zero
76 f;;
77 > val it : Complex = {re = 0.0;
78 im = 0.0;}
79
80 //[3] Indexer
81 // get the real part of e
82 e.[0];; // get
83 > val it : float = 2.0
84 e.[0] <- 4.;; // set
85 e;;
86 > val it : Complex = {re = 4.0;
87 im = 4.0;}
88
89 //[4] Operator overloading
90 let c2 = d + e
91 c2;;
92 > val it : Complex = {re = 7.0;
93 im = 8.0;}
94
95 //[5] Display method
96 d.Display();;
97 > val it : string = "3,4i"
NOTE: When working with Indexers, you need to explicitly use the Item member name or the compiler will throw an exception.
No comments:
Post a Comment