Despite having been using F# as my go-to language for any personal project for 5+ years now, there are still simple things about the language that -- not being pointed out in the texts -- had passed me by until now. Prompted by a number of questions on the MSDN F# forum about porting some category theory implementation in ML to F#, I tried the exercise myself over a few evenings.
To my surprise, almost everything just ported over with mechanical syntax changes -- with the notable exception that F# doesn't have an
infix keyword to mark a function as behaving like an operator. This included repeated idioms involving pattern matching in function arguments
type Cat<'o,'m> = Cat of ('m->'o)*('m->'o)*('o->'m)*('m*'m->'m) let source(Cat(s,_,_,_)) = s
even though you can't do multiple overloads of the same function name with different patterns, Erlang style (those would have to be different cases in a
function expression); you can even have your destructuring and eat it too as in
let K (Cat(_,_,id',_) as A) (a) = ...
where you have names for a component of the first argument and the first argument as a whole brought into scope. This generalises to being able to provide names for components of a tuple argument
type Functor<'o,'m,'o1,'m1> = Ffunctor of Cat<'o, 'm> *('o->'o1)*('m->'m1)*Cat<'o1, 'm1> let apply_Fun_CoCone(Ffunctor(_,fo,fm,_) as F, c) = ...
you just have to parenthesise appropriately to distinguish taking the last element or the tuple as a whole.
There is one caveat -- if you want to do pure destructuring to assign names to components and write
let Cat(_,_,id,comp) = ...
this gets parsed as defining a function
Cat on a tuple that returns a constant; unlike in SML you have to provide a dummy name for the whole expression
let Cat(_,_,id,comp) as cat' = ...
to get the destructuring assignment to
comp that you wanted in the first place.