Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why use lambda instead of pattern matching?

I am watching a tutorial on parsers in haskell https://www.youtube.com/watch?v=9FGThag0Fqs. The lecture starts with defining some really basic parsers. These are to be used together to create more complicated parsers later. One of the basic parsers is item. This is used to extract a character from the string we are parsing.

All parsers have the following type:

type Parser a = String -> [(a, String)]                                         

The parser item is defined like this:

item :: Parser Char                                                             
item = \inp -> case inp of                                                      
                    [] -> []                                                
                    (x:xs) -> [(x,xs)]

I am not so used to this syntax, so it looks strange to me. I would have written it:

item' :: Parser Char                                                            
item' [] = []                                                                   
item' (x:xs) = [(x,xs)]

Testing it in ghci indicates that they are equal:

*Main> item ""
[]
*Main> item "abc"
[('a',"bc")]
*Main> item' ""
[]
*Main> item' "abc"
[('a',"bc")]

The lecturer makes a short comment about thinking it looks clearer, but I disagree. So my questions are:

Are they indeed completely identical? Why is the lambda version clearer?

like image 270
toftis Avatar asked Jan 02 '15 14:01

toftis


2 Answers

I believe this comes from the common practice of writing

f :: Type1 -> ... -> Typen -> Result
f x1 ... xn = someResult

where we have exactly n function arrows in the type, and exactly n arguments in the left hand side of the equation. This makes it easy to relate types and formal parameters.

If Result is a type alias for a function, then we may write

f :: Type1 -> ... -> Typen -> Result
f x1 ... xn y = something

or

f :: Type1 -> ... -> Typen -> Result
f x1 ... xn = \y -> something

The latter follows the convention above: n arrows, n variables in the left hand side. Also, on the right hand side we have something of type Result, making it easier to spot. The former instead does not, and one might miss the extra argument when reading the code quickly.

Further, this style makes it easy to convert Result to a newtype instead of a type alias:

newtype Result = R (... -> ...)

f :: Type1 -> ... -> Typen -> Result
f x1 ... xn = R $ \y -> something

The posted item :: Parser Char code is an instance of this style when n=0.

like image 119
chi Avatar answered Oct 27 '22 06:10

chi


Why you should avoid equational function definitions (by Roman Cheplyaka): http://ro-che.info/articles/2014-05-09-clauses

Major Points from the above link:

  • DRY: Function and argument names are repeated --> harder to refactor
  • Clearer shows which arguments the function decides upon
  • Easy to add pragmas (e.g. for profiling)
  • Syntactically closer to lower level code

This doesn't explain the lambda though..

like image 26
rethab Avatar answered Oct 27 '22 05:10

rethab