Search code examples
haskelloperator-precedencecurrying

Chaining parameters without parentheses in Haskell


I'm trying to write a convenience wrapper for hnetcdf library. Instead of dealing with its low-level interface, I want to be able to describe NetCDF file structure in a simple declarative manner. To achieve this I've created a bunch of ADTs:

data Info = Info FileInfo Dimentions Variables
data FileInfo   = FileInfo String Int
data Dimentions = Dimentions [Dimention]
data Variables  = Variables [Variable]
data Dimention  = BoundedDimention String Int | UnboundedDimention String
data Variable   = Variable String Int NC.NcType [Dimention]

They work if I use them like in the following example:

writeNetCDF :: FilePath -> IO ()
writeNetCDF filename = do
    let dimX, dimY, dimZ = ...
    let var1, var2       = ...
    let info = Info (FileInfo filename 1)
                    (Dimentions [dimX, dimY, dimZ])
                    (Variables  [var1, var2])
    ...

but I really dislike extra parenthesis. I tried to use ($) and (.), but with no luck:

    -- No way.
    let info = Info $ FileInfo filename 1
                    $ Dimentions [dimX, dimY, dimZ]
                    $ Variables  [var1, var2]

Is there another way to chain curried parameters?


Solution

  • Sure. The problem of $ in this case is that it's right associative:

    let info = Info $ FileInfo filename 1
                    $ Dimentions [dimX, dimY, dimZ]
                    $ Variables  [var1, var2]
             = Info ( FileInfo filename 1
                      ( Dimentions [dimX, dimY, dimZ]
                        ( Variables  [var1, var2])
                      )
                    )
    

    So all you need is an operator that's left associative:

    ($<) :: (a -> b) -> a -> b
    ($<) = ($)
    
    infixl 0 $< -- left associative
    

    Now the parentheses are correct:

    let info = Info $< FileInfo filename 1
                    $< Dimentions [dimX, dimY, dimZ]
                    $< Variables  [var1, var2]
             = (
                (
                 (Info 
                   (FileInfo filename 1)
                 )
                 (Dimentions [dimX, dimY, dimZ])
                )
                (Variables  [var1, var2])
               )