Search code examples
haskelltypesfunctional-programmingservant

What does the :~> operator do in haskell


I was looking through a github project that seems to be a website using haskell and elm. It uses the operator :~> in a type signature in this file. I can't find a definition for this operator.

What does it do? Why would I use it? What is it called?


Solution

  • It's a type operator. If you look at the top of the file linked in the OP, you'll see the following pragma among others:

    {-# LANGUAGE TypeOperators #-}
    

    This means that the language is no longer 'plain' Haskell, but rather Haskell with a few GHC-specific extensions, one of which is the Type Operators extension.

    This extension basically just enables you to use infix operators for type definitions, instead of being limited to alphanumerical type names.

    As Chris Martin points out in his comment, this particular type operator comes from the natural-transformation package. It defines the :~> operator as a natural transformation from one functor to another.

    You could have defined it as a normal newtype like this:

    newtype NT f g x = NT (f x -> g x)
    

    Using a type operator, though, makes a function signature like

    SimpleApp -> SimpleHandler :~> Handler
    

    read more like a normal function than

    SimpleApp -> NT (SimpleHandler Handler)
    

    As an example of a natural transformation, consider listToMaybe from Data.Maybe. This functionality is also sometimes called safeHead, but the point is that it's a translation from the list ([]) functor to the Maybe functor.

    You can 'promote' that to a natural transformation like this:

    *Lib Lib Data.Maybe Control.Natural> safeHeadNT = wrapNT listToMaybe
    *Lib Lib Data.Maybe Control.Natural> :t safeHeadNT
    safeHeadNT :: [] :~> Maybe
    

    Notice that the type of safeHeadNT is given as [] :~> Maybe.

    You can unwrap it again if you want to call the function:

    *Lib Lib Data.Maybe Control.Natural> unwrapNT safeHeadNT [1,2,3]
    Just 1
    *Lib Lib Data.Maybe Control.Natural> unwrapNT safeHeadNT []
    Nothing
    

    Another option is to use the package's built-in # infix operator:

    *Lib Lib Data.Maybe Control.Natural> safeHeadNT # []
    Nothing
    *Lib Lib Data.Maybe Control.Natural> safeHeadNT # [1,2,3]
    Just 1