One of the nice things about Haskell is that you can "infix" any function by surrounding it with backquotes. This helps a lot with certain functions, either because they look like verbalised math operators, or because they just look like bedraggled OO escapees that want to appear after their subject. Examples:
Just 12 `mplus` Just 34 `mplus` mzero
-- much nicer than:
mplus (Just 12) (mplus (Just 34) mzero)
"foo" `isPrefixOf` "foonly2000"
-- much nicer than
isPrefixOf "foo" "foonly2000"
And of course if you don't feel like it, you don't have to. The choice is yours. Today I discovered how to replicate this choice without syntactic support from the language. This is important, because F# doesn't provide infixing syntax.
The trick is pretty simple. I'll Seq.append to demonstrate:
let l = [1..100]
Seq.take 49 l |> Seq.append <| Seq.skip 51 l
The key is the "pipeline operator", which is a corny name for "reverse apply". All it is is this:
let (|>) a f = f a
But it's super popular in F# because of two things:
- Using it a lot emulates the structure of dot-syntax OO code
l |> skip 2 |> take 4 |> printfn "%A")
- It encourages you to write left-to-right, top-to-bottom code instead of functional style's upside-down inside-out side-to-side and round-about. This really helps out F#'s type inferencer, which gets confused any time it has to infer OO code. If you use a method near the end of a chain, it has a better chance of inferring the type from a function you called on it earlier in the chain.
But nobody uses "reverse pipeline" in F#, which is a reverse corny name for "apply". It does come pre-defined:
let (<|) f a = f a
(In Haskell, where it's much more popular, it's f $ a = f a)
In fact, if you look at the definition, you may wonder if it does ANYTHING. It's actually very common in Haskell, used to eliminate parentheses. The reason this works is that function call has highest precedence in Haskell and F#, so everything else is lower, usually a lot lower. So instead of this:
Seq.append (Seq.take 49 l) (Seq.skip 51 l)
You can write this:
Seq.append (Seq.take 49 l) <| Seq.skip 51 l
F# groups Seq.append (Seq.take 49 l) separately from Seq.skip 51 l because of the lower precedence of (<|)
Now just repeat the same trick with pipeline to get rid of the set of parentheses around Seq.take:
Seq.take 49 l |> Seq.append <| Seq.skip 51 l
And suddenly, not only did you get rid of the parentheses, you've infixed Seq.append!
Of course if you're really addicted to Haskell, you could just write
let (++) = Seq.append
But this lets you infix, or not, depending on what reads the best.
Disclaimer: I haven't tested this extensively with type inferencing. Using this trick may lead to a lot more type annotations being required.