Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is one implementation of the constant function equal to a version using a lambda?

I'm currently revising the chapter for the lambda (\) expressions in Haskell. Was just wondering if someone could please help explain how did this:

const :: a → b → a 
const x _ = x (this part)

Get defined into this:

const :: a → (b → a) 
const x = λ_ → x (how did it become like this?)
like image 679
hamburgerwhoselflearns Avatar asked Dec 07 '25 10:12

hamburgerwhoselflearns


1 Answers

The signatures a -> b -> a and a -> (b -> a) are parsed exactly the same, much like the arithmetic expressions 1 - 2 - 3 and (1 - 2) - 3 are the same: the -> operator is right-associative, whereas the - operator is left associative, i.e. the parser effectively puts the parentheses in the right place if not explicitly specified. In other words, A -> B -> C is defined to be A -> (B -> C).

If we explicitly write a -> (b -> a), then we do this to put focus on the fact that we're dealing with curried functions, i.e. that we can accept the arguments one-by-one instead of all at once, but all multi-parameter functions in Haskell are curried anyway.

As for why const x _ = x and const x = \_ -> x are equivalent: well first, to be pedantic they're not equivalent, see bottom of this answer. But let's ignore that for now.

Both lambdas and (single) function clauses are just ways to define functions. Like,

sqPl1 :: Int -> Int
sqPl1 x = x^2 + 1

does the same as

sqPl1 = \x -> x^2 + 1

It's just a different syntax. Some would say the f x = ... notation is just syntactic sugar for binding x in a lambda, i.e. f = \x -> ..., because Haskell is based on lambda calculus and in lambda calculus lambdas are the only way to write functions. (That's a bit of an oversimplification though.)


I said they're not quite equivalent. I'm referring to two things here:

  1. You can have local definitions whose scope outlasts a parameter binding. For example if I write

    foo x y = ec * y
     where ec = {- expensive computation depending on `x` -}
    

    then ec will always be computed from scratch whenever foo is applied. However, if I write it as

    foo x = \y -> ec * y
     where ec = {- expensive computation depending on `x` -}
    

    then I can partially apply foo to one argument, and the resulting single-argument function can be evaluated with many different y values without needing to compute ec again. For example map (foo 3) [0..90] would be faster with the lambda definition. (On the flip side, if the stored value takes up a lot of memory it may be preferrable to not keep it around; it depends.)

  2. Haskell has a notion of constant applicative forms. It's a subtle topic that I won't go into here, but that can be affected by whether you write a function as a lambda or with clauses or expand arguments.

like image 161
leftaroundabout Avatar answered Dec 09 '25 15:12

leftaroundabout



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!