
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

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

On Thu, Feb 26, 2009 at 7:25 PM, Thomas Davie
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.

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

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
On Thu, Feb 26, 2009 at 7:25 PM, Thomas Davie
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 Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners

On 26/02/2009, Andrew Wagner
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

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
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 Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners

On Fri, Feb 27, 2009 at 6:31 PM, Brent Yorgey
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? :)
participants (7)
-
Andrew Wagner
-
Brent Yorgey
-
Felipe Lessa
-
Matt R
-
Peter Verswyvelen
-
Philip Scott
-
Thomas Davie