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 with
let 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 value2A 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 types
type 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 + value2This 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