Live Hacking with Tomas Petricek [Desiderius part 8]

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.

Capture

Capture

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!!