Lambda Functions

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

Lambda Functions

Philip Scott-2
Okay, thanks everyone for the help with the last question; I've got
another one for you if you are keen. It's short and sweet.

You can easily define a lambda expression that takes a tuple - e.g. in ghci

 > let foo = \(x,y) -> 42
foo :: (t, t1) -> Integer

 > foo (5,5)
42

Yay. Now that isn't a very exciting function. Tt takes two anythings and
gives you a nice meaningful number. Now let us say for some perverse
reason I want to make a lambda to test for equality. That is,
reimplement the functionality of (==) using, well, (==).

 > let foo2 = \(x,y) -> x == y

foo2 :: ((), ()) -> Bool

Uh-oh. Now things are getting a little odd. I am not entirely sure what
that type signature means (I was expecting to see something about the
types having to be the same and an instance of the Eq class but alas..)
The function certainly doesn't do what I want it to:

 > foo2 (5,5)

    No instance for (Num ())
      arising from the literal `5' at <interactive>:1:6
    Possible fix: add an instance declaration for (Num ())
    In the expression: 5
    In the first argument of `foo2', namely `(5, 5)'
    In the expression: foo2 (5, 5)

This is itself a stupid problem, but it is a distilled version of some
trouble I was having writing a filter that works on a list of tuples.
Any pointers or slaps in the face for being too stupid warmly welcomed.
I have already tried my usual trick of smothering the thing parentheses
but it appeared to be all in vain.

All the best,

Philip

Reply | Threaded
Open this post in threaded view
|

Lambda Functions

Thomas Davie

On 26 Feb 2009, at 23:07, Philip Scott wrote:

> Okay, thanks everyone for the help with the last question; I've got  
> another one for you if you are keen. It's short and sweet.
>
> You can easily define a lambda expression that takes a tuple - e.g.  
> in ghci
>
> > let foo = \(x,y) -> 42
> foo :: (t, t1) -> Integer
>
> > foo (5,5)
> 42
>
> Yay. Now that isn't a very exciting function. Tt takes two anythings  
> and gives you a nice meaningful number. Now let us say for some  
> perverse reason I want to make a lambda to test for equality. That  
> is, reimplement the functionality of (==) using, well, (==).
>
> > let foo2 = \(x,y) -> x == y
>
> foo2 :: ((), ()) -> Bool
>
> Uh-oh. Now things are getting a little odd. I am not entirely sure  
> what that type signature means (I was expecting to see something  
> about the types having to be the same and an instance of the Eq  
> class but alas..) The function certainly doesn't do what I want it to:
>
> > foo2 (5,5)
>
>   No instance for (Num ())
>     arising from the literal `5' at <interactive>:1:6
>   Possible fix: add an instance declaration for (Num ())
>   In the expression: 5
>   In the first argument of `foo2', namely `(5, 5)'
>   In the expression: foo2 (5, 5)
>
> This is itself a stupid problem, but it is a distilled version of  
> some trouble I was having writing a filter that works on a list of  
> tuples. Any pointers or slaps in the face for being too stupid  
> warmly welcomed. I have already tried my usual trick of smothering  
> the thing parentheses but it appeared to be all in vain.

I'm not 100% certain here, so someone may correct me, but I think this  
is what's going on:

foo2 has no arguments.  Because of this, ghci makes it a CAF.  At this  
point, the monomorphism restriction kicks in, and the CAF has to be  
monomorphic.  ghc then chooses a type for it, and defaulting choses  
the most simple type it can find ? () (this is the type that contains  
one value ? () ).

If instead, you do let foo3 (x,y) = x == y, you will get the type you  
expected.

Another way to write it ofc would simply be uncurry (==).

Bob
Reply | Threaded
Open this post in threaded view
|

Lambda Functions

Felipe Lessa
On Thu, Feb 26, 2009 at 7:25 PM, Thomas Davie <[hidden email]> wrote:
> I'm not 100% certain here, so someone may correct me, but I think this is
> what's going on:
[snip]

Yep, that's right. Is it just me or the monomorphism restriction
suddenly became the hot topic here on beginners?

> Another way to write it ofc would simply be uncurry (==).

Better yet, write a type signature. I highly recommend writing type
signatures for *all* top-level definitions, including non-exported
ones:

- You avoid most cases where the monomorphism restriction would bother you.

- You avoid type errors on other functions (sometimes you make a
mistake but the code type checks with a wrong signature, and the type
error shows up only when you use the function elsewhere).

- It gives you some insights before implementing, and helps whoever
reads your code.

- It allows you to specialize the code when polymorphism is not needed.

Probably there are other reasons as well, but these are the most prominent.

--
Felipe.
Reply | Threaded
Open this post in threaded view
|

Lambda Functions

Brent Yorgey-2
On Thu, Feb 26, 2009 at 07:51:32PM -0300, Felipe Lessa wrote:
>
> Better yet, write a type signature. I highly recommend writing type
> signatures for *all* top-level definitions, including non-exported
> ones:
>
> - It gives you some insights before implementing

Hear, hear!  If you can't write down the intended type of a function,
you have very little hope of implementing it correctly. =)

-Brent
Reply | Threaded
Open this post in threaded view
|

Lambda Functions

Andrew Wagner
In reply to this post by Felipe Lessa
Excellent list. It seems like every time I hear about a problem with the
MMR, I go "wow, how come I never ran across that? oh yeah, because I write
type signatures..."
On Thu, Feb 26, 2009 at 5:51 PM, Felipe Lessa <[hidden email]>wrote:

> On Thu, Feb 26, 2009 at 7:25 PM, Thomas Davie <[hidden email]> wrote:
> > I'm not 100% certain here, so someone may correct me, but I think this is
> > what's going on:
> [snip]
>
> Yep, that's right. Is it just me or the monomorphism restriction
> suddenly became the hot topic here on beginners?
>
> > Another way to write it ofc would simply be uncurry (==).
>
> Better yet, write a type signature. I highly recommend writing type
> signatures for *all* top-level definitions, including non-exported
> ones:
>
> - You avoid most cases where the monomorphism restriction would bother you.
>
> - You avoid type errors on other functions (sometimes you make a
> mistake but the code type checks with a wrong signature, and the type
> error shows up only when you use the function elsewhere).
>
> - It gives you some insights before implementing, and helps whoever
> reads your code.
>
> - It allows you to specialize the code when polymorphism is not needed.
>
> Probably there are other reasons as well, but these are the most prominent.
>
> --
> Felipe.
> _______________________________________________
> Beginners mailing list
> [hidden email]
> http://www.haskell.org/mailman/listinfo/beginners
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/beginners/attachments/20090226/8c993f12/attachment.htm
Reply | Threaded
Open this post in threaded view
|

Lambda Functions

Matt R
On 26/02/2009, Andrew Wagner <[hidden email]> wrote:
> Excellent list. It seems like every time I hear about a problem with the
> MMR, I go "wow, how come I never ran across that? oh yeah, because I write
> type signatures..."

Certainly. Still, it seems relatively easy to bump into it when
tinkering on ghci.

Out of interest, what are the disadvantages to blanket turning off the
restriction?

-- Matt
Reply | Threaded
Open this post in threaded view
|

Lambda Functions

Brent Yorgey-2
The only disadvantage is that some things you might expect to be
constants will actually get recomputed every time they are used. For
example, suppose you define

  foo = very expensive computation involving a bunch of numbers

You might think that foo will get computed just once, the first time
it is needed.  However, if foo ends up with a polymorphic type, like,
say

  foo :: Num a => a

then it is not actually a constant, but a function which takes a Num
dictionary (i.e. a record of the Num methods) as an argument.  So it
will be recomputed every time it is used, since it might have
different values for different types.

Now, you might wonder why this is such a big deal.  The answer is: it
isn't.  I have the MR automatically turned off in my .ghci file, and
I've never missed it.  Furthermore, the monomorphism restriction will
be removed in the next version of the Haskell language standard.

-Brent

On Fri, Feb 27, 2009 at 08:41:36AM +0000, Matt R wrote:

> On 26/02/2009, Andrew Wagner <[hidden email]> wrote:
> > Excellent list. It seems like every time I hear about a problem with the
> > MMR, I go "wow, how come I never ran across that? oh yeah, because I write
> > type signatures..."
>
> Certainly. Still, it seems relatively easy to bump into it when
> tinkering on ghci.
>
> Out of interest, what are the disadvantages to blanket turning off the
> restriction?
>
> -- Matt
> _______________________________________________
> Beginners mailing list
> [hidden email]
> http://www.haskell.org/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Lambda Functions

Peter Verswyvelen-2
On Fri, Feb 27, 2009 at 6:31 PM, Brent Yorgey <[hidden email]>wrote:

> The only disadvantage is that some things you might expect to be
> constants will actually get recomputed every time they are used. For
> example, suppose you define
>
>  foo = very expensive computation involving a bunch of numbers
>
> You might think that foo will get computed just once, the first time
> it is needed.  However, if foo ends up with a polymorphic type, like,
> say
>
>  foo :: Num a => a
>
> then it is not actually a constant, but a function which takes a Num
> dictionary (i.e. a record of the Num methods) as an argument.  So it
> will be recomputed every time it is used, since it might have
> different values for different types.


Yes indeed. Beginners might want to verify this with the following little
program (just assume 42 takes a very long to compute, on some alien computer
this took 7? million years ;-)

import Debug.Trace

foo :: Num a => a
foo = trace "foo" $ 42

bar :: Int
bar = trace "bar" $ 42

When evaluating foo (e.g in GHCi) "foo" will be printed every time, bar only
once.

I guess a clever implementation would only compute foo once for each
different type of a? But then of course you'll hit a runtime performance
penalty every time when accessing foo since this will require a lookup...
Mmm, this feels as a case where you can't determine at compile time if a
function - in this case a polymorphic CAF - will need memoization or not...

For example (just a quick hack)

import           Data.Typeable
import           Data.IORef
import qualified Data.IntMap as M
import           System.IO.Unsafe
import           Debug.Trace

fooCache :: IORef (M.IntMap a)
fooCache = unsafePerformIO $ newIORef M.empty

foo :: (Typeable a, Num a) => a
foo = unsafePerformIO $ do
        key <- typeRepKey (typeOf value)
        atomicModifyIORef fooCache (updateCache key)
  where
    value = trace "foo is computed" $ 42
    updateCache key cache =
      case key `M.lookup` cache of
        Just n -> (cache, trace "foo is cached" n)
        Nothing -> (M.insert key value cache, value)



Now, you might wonder why this is such a big deal.  The answer is: it
> isn't.  I have the MR automatically turned off in my .ghci file, and
> I've never missed it.  Furthermore, the monomorphism restriction will
> be removed in the next version of the Haskell language standard.


Cool. But I guess the next version of the standard will take a while? :)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/beginners/attachments/20090228/91955b29/attachment.htm