Phillip Trelford's Array

POKE 36879,255

F# Intro Talk at NextGenUG in So’ton

Last week I had the pleasure of popping down to sunny Southampton to deliver an F# introduction talk to the local branch of the very friendly and receptive NxtGenUG. Attached are the slides. If you are interested in video, I have given very similar talks at:

    Example slide:

image 

One examples I gave was taking the C# class reference example from MSDN and implementing it in F#.

 

class Child
{
   private readonly int age;
   private readonly string name;

   public Child(string name, int age)
   {
      this.name = name;
      this.age = age;
   }

   public override string ToString()
   {
      return String.Format("{0} {1}", name, age);
   }      
}

F# Class:

type Child(name,age) =
    member this.Name = name
    member this.Age = age
    override this.ToString() =
        sprintf "%s %d" name age

 

F# Record:

type Child = { Name:string; Age:int } with
    override this.ToString() = 
        sprintf "%s %d" this.Name this.Age

F# Discriminated Union:

type Child = Child of string * int
    with override this.ToString() =
        this |> (fun (Child(name,age)) -> 
            sprintf "%s %d" name age)

Thanks again to everyone who attended for making it fun and some great questions.

FSharp Intro NxtGen UG So'ton.pptx (657.07 kb)

FSharpDemos.zip (120.81 kb)

Declarative Naming

Like sentence construction, declarative programming requires careful selection of words. Nouns for class and property names, verbs for method and event names.

Ignoring the arbitrary ‘I’ at the start of interface names, nouns or adjectives can describe their capabilities, e.g. comparable, equatable and observable.

Declarative names should describe the “what” without the “how”. Short names typically indicate good abstraction. Long or schizophrenic names are a code smell suggesting poor separation of concerns and that refactoring may be required.

Luke“ Luke. Let the force words guide you.”

A Brief History of Naming

In the beginning, Microsoft operating systems such as MS-DOS or Windows 3.x did not support long file names like other operating systems such as Unix, OS/2, and the Apple Macintosh MAC OS. The earlier versions of Microsoft's operating systems had a limitation of an eight character file name or directory, and a three character extension. [Information about long file names]

Going back a bit further to the 8-bit era of computing, I remember my first Z80 assembler for the Amstrad CPC, HiSoft’s DevPac which only treated the first 6 characters of a label as significant. Meaning coding schemes for labels were required for larger programs, a bit like postal codes for addresses, .

Later with newer assemblers, CPUs and C, restrictions relaxed, but naming conventions like Hungarian notation remained to indicate things like the types of variables. Thankfully with the advent of modern compilers and IDEs, bringing type checking and type information on tooltips, the need for these notations has been removed. Nowadays we should be free to focus on good declarative naming.

Declarative Naming Resources

When following a variant of Domain-driven design (DDD), a ubiquitous language helps define names for core business concepts. This just leaves naming for technical abstractions, and there are plenty of resources out there to help.

Online:

Books:

And now for some examples…

Managers

Like a manager typically has no specific role, so a manager class is probably a poor abstraction. More often than not you’d be better starting with a collection.

INotifyPropertyChanged

In an MSDN article by Josh Smith on MVVM, a ViewModelBase class is suggested that implements the INotifyPropertyChanged interface. To me this seems a poor abstraction as a class need not be a View Model in order to implement this interface, and the name gives no obvious indication to its behaviour, instead suggesting a set of base behaviours. A better abstraction would be ObservableObject, like the framework’s ObservableCollection class which implements the not too dissimilar INotifyCollectionChanged interface.

IDictionary

An improvement on the naming of the pre-generics un-typed equivalent Hashtable class, the Dictionary class better specifies the “what” over the “how”. However the new underlying generic IDictionary interface could be improved by separating the concerns of reading and writing items across 2 interfaces.

GetOrAdd

The new ConcurrentDictionary class in .Net 4.0 contains a method GetOrAdd. Again in terms of naming, this seems to be a poor abstraction, and again by emphasizing the “how” rather than the “what”. A better name would be Obtain, a suggestion in a Martin Fowler tweet.

ToString

A good example of declarative naming, also promoting the idea of functional transformation and idempotence.

Pluralisation

I like pluralisation for collections, e.g. Items, its both short and descriptive. The new IObservable<T> interface as used by the Reactive Extensions (Rx) also seems to lend itself well to pluralisation, e.g. Notifications.

Last word?

For variable names the last word of the object may suffice, e.g.

let connection = new SqlConnection()

Finally

KentBeck: if you can’t find a really good name for something, pick a really terrible name (i like "fred"). then you are motivated to fix it.

Disclaimer:

For legacy code consistency may be as important as declarative naming, so abandoning coding schemes like Hungarian notation on a large codebase may be counter-productive.

Units of measure auto-conversion

In a recent article I described some prototype F# code for defining runtime units of measure with similar functionality to F#’s compile time units of measure feature. The following code extends this prototype to provide auto-conversion when adding or multiplying unit values. Note that as unit conversion is done at runtime this implementation is also usable from C#. This allows for example the following calculations to succeed:

1km + 200m = 1200m

(1m/s) / 500milliseconds = 2m

To achieve this a new Measure type introduces Base Unit types and Measure multiples.

    Base Unit type examples 
  • length
  • mass
  • time
    Measure multiple examples 
  • Kilometres (1000)
  • Metres (1)
  • Millimetres (0.001)

Defining measure types:

let length = "length"
let time = "time"
let m = Measure("m", BaseUnit(length))
let km = Measure.Kilo(m)
let s = Measure("s", BaseUnit(time))
let milliseconds = Measure.Milli(s)

Measure type definition:

type MeasureType = 
    | BaseUnit of string
    | Multiple of Measure * ValueType
    with
    member this.BaseUnitName =
        let rec traverse = function
            | BaseUnit s -> s
            | Multiple(Measure(_,m),_) -> traverse m
        traverse this
and Measure = Measure of string * MeasureType with  
    member this.Name = match this with Measure(s,_) -> s
    member this.Type = match this with Measure(_,t) -> t   
    static member Kilo (m:Measure) = 
        Measure("k"+m.Name,Multiple(m,1000.0))  
    static member Milli (m:Measure) = 
        Measure("m"+m.Name,Multiple(m,0.001))
    static member ( * ) (v:ValueType,m:Measure) = UnitValue(v,Unit(m,1))

The add and multiply operations on a UnitValue now convert to the base unit if a dimensional unit mismatch exits (see Units conversion by factor-label):

and UnitValue = UnitValue of ValueType * UnitType with
    member this.Value = match this with UnitValue(v,_) -> v
    member this.Unit = match this with UnitValue(_,u) -> u
    override this.ToString() = sprintf "%O %O" this.Value this.Unit
    static member ToBaseUnit x =
        let rec toBaseUnit = function
            | UnitValue(v,(Unit(Measure(_,BaseUnit(_)),_))) as x -> 
                x
            | UnitValue(v,Unit(Measure(_,Multiple(quantity,coefficient)),p)) -> 
                toBaseUnit (UnitValue(v*coefficient, Unit(quantity,p)))            
            | UnitValue(v,(CompositeUnit(xs))) ->
                let v, ys =
                    (v,[]) |> List.foldBack (fun x (v,ys) -> 
                        let x = toBaseUnit (UnitValue(v,x))
                        x.Value, x.Unit::ys
                    ) xs
                UnitValue(v, CompositeUnit(ys)) 
        toBaseUnit x
    static member private DoesDimensionalUnitMismatchExist lhs rhs =
        let rec measures = function
            | Unit(m,_) -> Set.singleton (m)
            | CompositeUnit(us) ->
                us |> List.map measures |> Set.unionMany                          
        measures lhs |> Set.exists (fun x ->
            measures rhs |> Set.exists (fun y ->
                y.Type.BaseUnitName = x.Type.BaseUnitName 
                && not (x = y)  
            )
        )
    static member (+) (lhs:UnitValue,rhs:UnitValue) =                         
        if lhs.Unit = rhs.Unit then       
            UnitValue(lhs.Value+rhs.Value, lhs.Unit+rhs.Unit)             
        else             
            let x1 = UnitValue.ToBaseUnit lhs
            let x2 = UnitValue.ToBaseUnit rhs
            if x1.Unit = x2.Unit then
                UnitValue(x1.Value+x2.Value,x1.Unit+x2.Unit)
            else                                                      
                raise (new System.InvalidOperationException())                 
    static member (*) (lhs:UnitValue,rhs:UnitValue) =            
        if UnitValue.DoesDimensionalUnitMismatchExist lhs.Unit rhs.Unit then            
            let lhs = UnitValue.ToBaseUnit lhs
            let rhs = UnitValue.ToBaseUnit rhs
            UnitValue(lhs.Value*rhs.Value,lhs.Unit*rhs.Unit)
        else
            UnitValue(lhs.Value*rhs.Value,lhs.Unit*rhs.Unit)   
    static member (*) (lhs:UnitValue,rhs:ValueType) =                        
        UnitValue(lhs.Value*rhs,lhs.Unit)      
    static member (/) (lhs:UnitValue,rhs:UnitValue) =
        if UnitValue.DoesDimensionalUnitMismatchExist lhs.Unit rhs.Unit then            
            let lhs = UnitValue.ToBaseUnit lhs
            let rhs = UnitValue.ToBaseUnit rhs
            UnitValue(lhs.Value/rhs.Value,lhs.Unit/rhs.Unit)
        else                 
            UnitValue(lhs.Value/rhs.Value,lhs.Unit/rhs.Unit)   
    static member (/) (lhs:UnitValue,rhs:ValueType) =
        UnitValue(lhs.Value/rhs,lhs.Unit) 

The only change to the Unit type is that it references a Measure type instead of a literal string signifying the measure:

and UnitType =
    | Unit of Measure * int
    | CompositeUnit of UnitType list
    static member Create(m) = Unit(m,1)
    override this.ToString() =
        let exponent = function
            | Unit(_,n) -> n
            | CompositeUnit(_) ->                
                raise (new System.InvalidOperationException())
        let rec toString = function        
            | Unit(s,n) when n=0 -> ""
            | Unit(Measure(s,_),n) when n=1 -> s
            | Unit(Measure(s,_),n)          -> s + " ^ " + n.ToString()            
            | CompositeUnit(us) ->               
                let ps, ns = 
                    us |> List.partition (fun u -> exponent u >= 0)
                let join xs = 
                    let s = xs |> List.map toString |> List.toArray             
                    System.String.Join(" ",s)
                match ps,ns with 
                | ps, [] -> join ps
                | ps, ns ->
                    let ns = ns |> List.map UnitType.Reciprocal
                    join ps + " / " + join ns
        match this with
        | Unit(_,n) when n < 0 -> " / " + toString this
        | _ -> toString this        
    static member ( * ) (v:ValueType,u:UnitType) = UnitValue(v,u)    
    static member ( * ) (lhs:UnitType,rhs:UnitType) =
        let text = function
            | Unit(Measure(s,_),_) -> s
            | CompositeUnit(us) -> us.ToString()       
        let normalize us u =
            let t = text u
            match us |> List.tryFind (fun x -> text x = t), u with
            | Some(Unit(s,n) as v), Unit(_,n') ->
                us |> List.map (fun x -> if x = v then Unit(s,n+n') else x)                 
            | Some(_), _ -> raise (new System.NotImplementedException())
            | None, _ -> us@[u]
        let normalize' us us' =
            us' |> List.fold (fun (acc) x -> normalize acc x) us        
        match lhs,rhs with
        | Unit(u1,p1), Unit(u2,p2) when u1 = u2 ->
            Unit(u1,p1+p2)
        | Unit(u1,p1), Unit(u2,p2) ->            
            CompositeUnit([lhs;rhs])
        | CompositeUnit(us), Unit(_,_) ->
            CompositeUnit(normalize us rhs)
        | Unit(_,_), CompositeUnit(us) ->
            CompositeUnit(normalize' [lhs]  us)
        | CompositeUnit(us), CompositeUnit(us') ->
            CompositeUnit(normalize' us us')
        | _,_ -> raise (new System.NotImplementedException())
    static member Reciprocal x =
        let rec reciprocal = function
            | Unit(s,n) -> Unit(s,-n)
            | CompositeUnit(us) -> CompositeUnit(us |> List.map reciprocal)
        reciprocal x
    static member ( / ) (lhs:UnitType,rhs:UnitType) =        
        lhs * (UnitType.Reciprocal rhs)
    static member ( + ) (lhs:UnitType,rhs:UnitType) =       
        if lhs = rhs then lhs                
        else raise (new System.InvalidOperationException())
and ValueType = float

    Known issues
  • Operator precedence means 10 * m / 2 * s = 5 m s instead of 5 m /s
    - As a workaround use brackets, i.e. (10 * m) / (2 * s)
  • Conversions requiring constant difference like degrees to kelvins are not supported

UnitType.fs (7.17 kb)