When I was at NDC, there was a real cool type of session called the “Functional Programming Lab Hour” where you could go to do some functional programming with among others, Tomas Petricek. Initially I went there to ask about a strange null error I was getting in combining C# and F# which apparently has to do with where the F# code get injected. That can happen after functions are called, which results in null. However, Tomas looked at my code and had so. many. cool. ideas!
A better syntax for lists
I made my BiddingSystems (= (Condition * Bid) list by concatenating items, like this:
(both (points 15 17) (forAllSuits (cards 2 13)), Bid (1, SA)) :: (points 12 19 & cards 5 13 Spades, Bid (1, Spades)) :: (both (points 12 19) (cards 5 13 Clubs) , Bid (1, Clubs)) :: .... [(both (points 12 19) (cards 4 13 Spades) , Bid (1, Spades))]
But there is nice syntax that lets you enumerate all options without ugly colons in between:
[ (both (points 15 17) (forAllSuits (cards 2 13)), Bid (1, SA)) (points 12 19 & cards 5 13 Spades, Bid (1, Spades)) (both (points 12 19) (cards 5 13 Clubs) , Bid (1, Clubs)) .... (both (points 12 19) (cards 4 13 Spades) , Bid (1, Spades))]
Enters are implicit colons, so you don’t have to type them. [I hate (semi)colons!] Nice huh?! I did not know that was possible and it saves me some ::. However, all lines need to be indented in the same way in order for this to work. But that is a good idea anyway.
Using infixes instead
I explained to Tomas how I made both and either because the syntax for creating union types contains too much commas and brackets. Compare
And (NPoints (12,19, Spades), NCards (4,13,Spades))
to
both (points 12 19) (cards 4 13 Spades)
Then, he proposed to use infix operators instead, to create something like:
(points 12 19) && (cards 4 13 Spades)
Which would be even nicer as we could then drop the brackets and write:
points 12 19 && cards 4 13 Spades
Combined with an associated bid, we’d then have
points 12 19 && cards 4 13 Spades => Bid (1, Spades)
instead of
(both (points 12 19) (cards 5 13 Spades) , Bid (1, Spades))
What symbols to pick?
The symbols above are nice: && meaning ‘and’ and => for the resulting bid, but no joy :(, these have some issues.
Firstly, I was already using && for the boolean and elsewhere in my code, in the fits function:
let rec fits (cards:Hand) (c:Condition):bool = match c with | NCards (min, max, s) -> cardsofSuitinHand cards s >= min && cardsofSuitinHand cards s <= max | NPoints (min, max) -> pointsinHand cards >= min && pointsinHand cards <= max | And (p,q) -> fits cards p && fits cards q | Or (p,q) -> fits cards p || fits cards q
So overloading && resulted in a compile error. Boo. Luckily, I did not use the single & anywhere, so we could define:
let (&) (p:Condition)(q: Condition):Condition = And (p,q)
That is not too bad, we can now write:
(points 15 17 & cards 5 13 Spades, Bid (1, Spades))
instead of:
(both (points 15 17) (cards 5 13 Spades 13)), Bid (1, SA))
Because & is associative, we could even use it on three conditions without brackets, and drop the all function we introduced before.
Infix numero dos
Now for the corresponding bid, we introduce a second infix operator. ‘=>’ is the most logical, I would say. Unfortunately ‘=>’ binds stringer than &, as we found out looking at the language spec.
This means using => requires extra brackets around the condition:
(points 15 17 & cards 5 13 Spades) => Bid (1, Spades)
Certainly better than we had, but it would be even nicer if we could have
points 15 17 & cards 5 13 Spades => Bid (1, Spades)
We could use ‘->’ which binds weaker than &, but, another bummer, ‘->’ is one of those operators you cannot overload 🙁 So we have two options: pick something else for the and, for example ^&, or even + or ++ , which would be stronger than =>
I thought about this option, but overloading + is not a good plan for obvious reasons, and ++ are two characters and I will be typing lots of rules with lots of combinations. So, I went with the second option: use & for and and something lower in the table
I could pick something below &. As you can see, there aren’t not a lot of options for the latter and in the end I settled on using & and := for the bid, because it looks most like an arrow. My Pascal heart is weeping a bit but at least we got rid of most brackets:
points 15 17 & cards 5 13 Spades := Bid (1, Spades)
All of these changes in about 30 min of pairing, wow. Thank you Tomas!!