Formatting function types

From HaskellWiki
Revision as of 09:04, 16 May 2017 by Andreask (talk | contribs) (Add summation analogy)
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

A very common way (at least in the base libraries) of formatting function types seems to be this:

hPutBuf :: Handle                       -- handle to write to
        -> Ptr a                        -- address of buffer
        -> Int                          -- number of bytes of data in buffer
        -> IO ()

I remember when I first started learning Haskell, and these many-arrowed functions seemed very strange to me: "Okay, we give it a handle, and get a pointer, and, um, from this we get an int, and from this an action? Er?"

I'd like to interject here for a moment. The way to read a Signature in that style is similar to reading a multiline summation. e.g

 hPutBuf :: Handle
         -> Ptr a 
         -> Int   
         -> IO ()
 hPutBuf:   Handle
          + Ptr a 
          + Int
        ------------- 
          = IO ()

I aggree with the argument, that for beginners it is hard to see what a function returns, but that has to do with currying. A right associative way for defining a function signature would (only in this case and worse in others) be easier to read.

The problem here is that the first parameter has a distinguished look while the other parameters and the return value all look the same. I think that a naive reader is inclined to assume that line breaks are situated at major structural boundaries. Consider two different interpretations of the structure of the type term:

  (((Handle               (Handle
  -> Ptr a)             ->(Ptr a
  -> Int)               ->(Int
  -> IO ())             -> IO ())))

Which one looks more natural?

The point of this rant is just this: the aforementioned multi-line formatting style should only be used for left-associative infix operators. For right-associative ones (such as the function arrow), the One True Way is this:

Handle ->
Ptr a ->
Int ->
IO ()

Unfortunately the first (misleading) style is used by Haddock for library documentation and GHC for error reporting.


See also