F#: Advantages of converting top-level functions to member methods?
- by J Cooper
Earlier I requested some feedback on my first F# project. Before closing the question because the scope was too large, someone was kind enough to look it over and leave some feedback.
One of the things they mentioned was pointing out that I had a number of regular functions that could be converted to be methods on my datatypes. Dutifully I went through changing things like
let getDecisions hand =
let (/=/) card1 card2 = matchValue card1 = matchValue card2
let canSplit() =
let isPair() =
match hand.Cards with
| card1 :: card2 :: [] when card1 /=/ card2 -> true
| _ -> false
not (hasState Splitting hand) && isPair()
let decisions = [Hit; Stand]
let split = if canSplit() then [Split] else []
let doubleDown = if hasState Initial hand then [DoubleDown] else []
decisions @ split @ doubleDown
to this:
type Hand
// ...stuff...
member hand.GetDecisions =
let (/=/) (c1 : Card) (c2 : Card) = c1.MatchValue = c2.MatchValue
let canSplit() =
let isPair() =
match hand.Cards with
| card1 :: card2 :: [] when card1 /=/ card2 -> true
| _ -> false
not (hand.HasState Splitting) && isPair()
let decisions = [Hit; Stand]
let split = if canSplit() then [Split] else []
let doubleDown = if hand.HasState Initial then [DoubleDown] else []
decisions @ split @ doubleDown
Now, I don't doubt I'm an idiot, but other than (I'm guessing) making C# interop easier, what did that gain me? Specifically, I found a couple *dis*advantages, not counting the extra work of conversion (which I won't count, since I could have done it this way in the first place, I suppose, although that would have made using F# Interactive more of a pain). For one thing, I'm now no longer able to work with function "pipelining" easily. I had to go and change some |> chained |> calls to (some |> chained).Calls etc. Also, it seemed to make my type system dumber--whereas with my original version, my program needed no type annotations, after converting largely to member methods, I got a bunch of errors about lookups being indeterminate at that point, and I had to go and add type annotations (an example of this is in the (/=/) above).
I hope I haven't come off too dubious, as I appreciate the advice I received, and writing idiomatic code is important to me. I'm just curious why the idiom is the way it is :)
Thanks!