This post is part of a series:
Functional Adventures in F# - A simple planner
Functional Adventures in F# - Calling F# from C#
Functional Adventures in F# - Using Map Collections
Functional Adventures in F# - Application State
Functional Adventures in F# - Types with member functions
Functional Adventures in F# - Getting rid of loops
Functional Adventures in F# - Getting rid of temp variables
Functional Adventures in F# - The MailboxProcessor
Functional Adventures in F# - Persisting Application State
Functional Adventures in F# - Adding Snapshot Persisting
Functional Adventures in F# - Type-safe identifiers
Functional Adventures in F# - Event sourcing lessons learned
I started converting my small but handy 3D math library to F# and very quickly I ran into a problem. Not that unexpected as I am a beginner with F#.
So say that you have some types:
type Vector3 =
{
X:float
Y:float
Z:float
}
type Vector4 =
{
X:float
Y:float
Z:float
W:float
}
That in the end will have quite similar functionality like .. for example Add, Divide etc. With the knowledge that I have so far of F# I ended up withlet addVector3 (value1:Vector3) (value2:Vector3) =
{
X = value1.X + value2.X
Y = value1.Y + value2.Y
Z = value1.Z + value2.Z
}
let addVector4 (value1:Vector4) (value2:Vector4) =
{
X = value1.X + value2.X
Y = value1.Y + value2.Y
Z = value1.Z + value2.Z
W = value1.W + value2.W
}
I quickly thought that there must be a cleaner way to write this. I like tidy code, neat code.. not code that just looks bad and will turn into a maintenance hell even before it is shipped.Luckily it turns out you can define functions as members of types. So the above 2 functions would look like:
type Vector3 =
{
X:float
Y:float
Z:float
}
member this.add (value2:Vector3) =
{
X = this.X + value2.X
Y = this.Y + value2.Y
Z = this.Z + value2.Z
}
type Vector4 =
{
X:float
Y:float
Z:float
W:float
}
member this.add (value2:Vector4) =
{
X = this.X + value2.X
Y = this.Y + value2.Y
Z = this.Z + value2.Z
W = this.W + value2.W
}
Here, both are called just add, and they can be invoked directly on the objects.let value1 = { X = 1.0; Y = 1.0; Z = 1.0 }
let value2 = { X = 2.0; Y = 2.0; Z = 2.0 }
let added = value1.add value2
A little cumbersome syntax if you want to do more complex calculations. So lets look at operator overloading in F# to so that we can get nice arithmetic operators into play for our new typestype Vector3 =
{
X:float
Y:float
Z:float
}
static member (+) (value1:Vector3, value2:Vector3) =
{
X = value1.X + value2.X
Y = value1.Y + value2.Y
Z = value1.Z + value2.Z
}
static member maxValue = { X = Microsoft.FSharp.Core.float.MaxValue; Y = Microsoft.FSharp.Core.float.MaxValue; Z = Microsoft.FSharp.Core.float.MaxValue; }
member this.add (value2:Vector3) = this + value2
This lets us write something like this instead:
let added = value1 + value2Also note the static member maxValue that is a static function that is invoked by Vector3.maxValue, in this case it just returns the max vector but it can be any function.
So there.
Update: evidently static members should start with capital letter according to design guide-lines. Makes sense as they are easier to spot.
All code provided as-is. This is copied from my own code-base, May need some additional programming to work. Use for whatever you want, how you want! If you find this helpful, please leave a comment, not required but appreciated! :)
Hope this helps someone out there!


No comments:
Post a Comment