Difference between revisions of "Parameter order"

From HaskellWiki
Jump to navigation Jump to search
(subtract)
(elaborate on Data.Map lookup functions)
Line 53: Line 53:
 
Sometimes library writers have infix usage of functions in mind.
 
Sometimes library writers have infix usage of functions in mind.
 
See for instance <hask>Data.Bits</hask> and [[syntactic sugar/Cons |Cons of syntactic sugar]].
 
See for instance <hask>Data.Bits</hask> and [[syntactic sugar/Cons |Cons of syntactic sugar]].
Unfortunately the order of arguments which seems to be natural for many programmers is reversed
+
Unfortunately the order of arguments to infix operators, which seems to be natural for many programmers,
with respect to the one, we encourage above.
+
is reversed with respect to the one we encourage above.
 
Maybe this only indicates that parameter order should be reverse, at all,
 
Maybe this only indicates that parameter order should be reverse, at all,
 
meaning that the name of the called function comes after the arguments (Reverse Polish Notation).
 
meaning that the name of the called function comes after the arguments (Reverse Polish Notation).
   
The operators <hask>(-)</hask>, <hask>(/)</hask>, <hask>div</hask>, <hask>mod</hask> are adaptions of the mathematical tradition,
+
The operators <hask>(-)</hask>, <hask>(/)</hask>, <hask>(^)</hask>, <hask>(^^)</hask>, <hask>(**)</hask>, <hask>div</hask>, <hask>mod</hask>
e.g. <hask> a `div` b</hask>, <hask> a `mod` b</hask>.
+
(used as <hask> a `div` b</hask>, <hask> a `mod` b</hask>) are adaptions of the mathematical tradition.
Hoever when using section in most cases the first argument is omitted.
+
However when using section, in most cases the first argument is omitted.
 
This strongly indicates that their parameter order is unnatural in the Haskell sense.
 
This strongly indicates that their parameter order is unnatural in the Haskell sense.
 
However, for the subtraction there also exists <hask>subtract</hask>, which is better for partial application.
 
However, for the subtraction there also exists <hask>subtract</hask>, which is better for partial application.
Line 66: Line 66:
 
There are more cases where there is even no simple reason,
 
There are more cases where there is even no simple reason,
 
why the parameter order was chosen in an unnatural way.
 
why the parameter order was chosen in an unnatural way.
Cf. <hask>Data.Map.lookup</hask>, <hask>Data.Map.findWithDefault</hask>, <hask>Data.Map.lookupIndex</hask>, <hask>Data.Map.findIndex</hask>.
+
* <hask>Data.Map.lookup :: (Monad m, Ord k) => k -> Map k a -> m a</hask>
  +
* <hask>Data.Map.findWithDefault :: Ord k => a -> k -> Map k a -> a</hask>
  +
* <hask>Data.Map.lookupIndex :: (Monad m, Ord k) => k -> Map k a -> m Int</hask>
  +
* <hask>Data.Map.findIndex :: Ord k => k -> Map k a -> Int</hask>
  +
Since objects of type <hask>Map</hask> represent mappings,
  +
it is natural to have some function which transforms a <hask>Map</hask> object to the represented function.
  +
All of the functions above do this in some way,
  +
where <hask>Data.Map.findWithDefault</hask> is certainly closest to the ideal Map->Function transformer.
  +
See the type <haskell>flip (Data.Map.findWithDefault deflt) :: Ord k => Map k a -> (k -> a)</haskell>.
  +
Unfortunately the parameters are ordered in a way that requires a flip for revealing this connection.
  +
Maybe the library designer immitated the signature of <hask>Data.List.lookup</hask> here.
   
   

Revision as of 12:37, 18 October 2006

The parameter order of Haskell functions is an important design decision when programming libraries. The parameter order shall

  • allow piping,
  • be consistent across similar functions.

Parameters in Haskell are rather reversed compared to imperative or object oriented languages. In an object oriented language, the object to work on is the very first parameter. In a function call it is often written even before the function name, say file in file.write("bla"). Strictly spoken, in Haskell it is not possible to alter objects, but there are many functions which return a somehow altered input object. This object should be the last parameter because then you can compose a sequence of operations on this object using the function composition operator .. The code

sum . map f . filter p . scanl (*) 1

describes a function, which applies three transformations to a list. This can be written so easily because the list is always the last parameter.

The order of the parameters except the last one is not so critical. However you should keep in mind that also transformations on functions are perfectly ok in Haskell. That's why function operators like the differentiation and integration in functional analysis should have the parameter of the derived/integrated function at the last position and the transformed function should be the parameter before the last one.

integrate :: a -> (a -> a) -> (a -> a)
integrate f0 f x = ...

differentiate :: a -> (a -> a) -> (a -> a)
differentiate h f x = ...

-- continuous extension, aka function limit
continuous :: (a -> a) -> (a -> a)
continuous f x = ...

exampleTransform = differentiate h . continuous


The third thing to consider is that it is easily possible to fix parameters, which are at the beginning. E.g.

sum = foldl (+) 0
product = foldl (*) 1

that's why we can consider the parameter order of foldl to be a good one. We also see in this example that it is easily possible to generate a function with the first parameters fixed and that functions shall be prepared for this.


Bad examples

Sometimes library writers have infix usage of functions in mind. See for instance Data.Bits and Cons of syntactic sugar. Unfortunately the order of arguments to infix operators, which seems to be natural for many programmers, is reversed with respect to the one we encourage above. Maybe this only indicates that parameter order should be reverse, at all, meaning that the name of the called function comes after the arguments (Reverse Polish Notation).

The operators (-), (/), (^), (^^), (**), div, mod (used as a `div` b, a `mod` b) are adaptions of the mathematical tradition. However when using section, in most cases the first argument is omitted. This strongly indicates that their parameter order is unnatural in the Haskell sense. However, for the subtraction there also exists subtract, which is better for partial application.

There are more cases where there is even no simple reason, why the parameter order was chosen in an unnatural way.

  • Data.Map.lookup :: (Monad m, Ord k) => k -> Map k a -> m a
  • Data.Map.findWithDefault :: Ord k => a -> k -> Map k a -> a
  • Data.Map.lookupIndex :: (Monad m, Ord k) => k -> Map k a -> m Int
  • Data.Map.findIndex :: Ord k => k -> Map k a -> Int

Since objects of type Map represent mappings, it is natural to have some function which transforms a Map object to the represented function. All of the functions above do this in some way, where Data.Map.findWithDefault is certainly closest to the ideal Map->Function transformer.

See the type
flip (Data.Map.findWithDefault deflt) :: Ord k => Map k a -> (k -> a)
.

Unfortunately the parameters are ordered in a way that requires a flip for revealing this connection. Maybe the library designer immitated the signature of Data.List.lookup here.


The rule of thumb

What do we learn from all this considerations?

The more important the parameter, the more frequently it changes, the closer shall it be at the end of the parameter list. If there is some recursion involved, probably the parameter, on which you recurse, is the one which should be at the last position.