Check out the formatting tips on the right for help formatting and making links.
Use the template below:
On the F# email list Alex Peake asked "Since records can contain fields that are functions, can these functions refer to other fields within that record?". This is an important topic, since it's closely related to other topics such as how to use records to build objects, and how to use lexical scoping to manage private state and private functionality when building these objects.
The usual way to build rich object-like records is to first use a series of private "let" and "let rec" constructions to build the constituent components, and then publish the results in a record expression. For example, here is a somewhat gratuitous record/object that caches the result of "s1.Length" (gratuitous because s1.Length works in constant time anyway):
type MyWidget =
{ GetLength : unit -> int;
Get : unit -> string;
Update : string -> unit }
let MakeWidget(s1:string) =
let state = ref (s1, s1.Length) in
let get() = fst(!state) in
let getLength() = snd(!state) in
let update(s:string) = state := (s,s.Length) in
{ GetLength = getLength;
Get = get;
Update = update }
Note that "private" fields and methods become local "let" bindings. Private state is often a shared mutable structure referenced by multiple closures (note you can define a supporting record MyPrivateWidgetState with mutable fields to hold the private state instead of using “ref” cells). If you adopt this style you will find yourself using more inner let-bindings (which takes some getting used to), and your types will have slimmer external interfaces.
If your methods are mutually referential you simply generalize this to use inner recursive bindings. For example, in this case the "Length" function is implemented using "Get"
type Widget =
{ GetLength : unit -> int;
Get : unit -> string;
Update : string -> unit }
let MakeWidget(s1:string) =
let state = ref(s1) in
let rec get() = !state
and getLength() = (get()).Length
and update(s) = state := s in
{ GetLength = getLength;
Get = get;
Update = update }
Many people like this style, and it is an important technique to know, because it helps to learn how to write your code in a way that ensures that your initialization process is coherent (e.g. doesn’t result in null-pointer errors). However, if you do this very often it becomes a bit of a shame that you can’t simply write recursive object expressions. As a result we have recently been discussing supporting the following syntax:
let MakeWidget(s1:string) =
let state = ref(s1) in
{ member x.GetLength() = (x.Get()).Length;
member x.Get() = !state;
member x.Update(s) = state := s }
At the time of writing this is still under consideration and relates to the ongoing work to publish F# modules and values in a more .NET-like fashion, and to support more OO-like programming directly within F#.