Ticket #1503 (closed bug: invalid)

Opened 6 years ago

Last modified 5 years ago

GHC doesn't respect monomorphism restrictions for non-type-class-restricted values that are bound

Reported by: Isaac Dupree Owned by:
Priority: normal Milestone:
Component: Compiler Version: 6.6.1
Keywords: Cc:
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: Difficulty: Unknown
Test Case: Blocked By:
Blocking: Related Tickets:

Description

Applicable in both 6.6.1 and 6.7.something.

This compiles as expected:

loop = read "foo"
int = show loop
req :: Int; req = loop

This fails to compile:

loop = undefined
int = show loop
req :: Int; req = loop

This compiles with -fmono-pat-binds (the default setting), but not with -fno-mono-pat-binds:

(loop) = undefined
int = show loop
req :: Int; req = loop

This compiles unless we use both -fno-mono-pat-binds and -fno-monomorphism-restriction, as expected:

(loop) = read "foo"
int = show loop
req :: Int; req = loop

loop=undefined versus loop=loop doesn't make any difference.

This compiles even with -fno-mono-pat-binds -fno-monomorphism-restriction. I don't understand how/why/how much 'case' makes a monomorphic binding:

x = case undefined of
      loop -> let int = show loop; req :: Int; req = loop in ()

Admittedly, this is an odd case where the Report's rationales for the M-R < http://haskell.org/onlinereport/decls.html#sect4.5.4> aren't exactly applicable. "Rule 1 prevents computations from being unexpectedly repeated" -without typeclasses, no computations will be repeated. "Rule 1 prevents ambiguity" -well, true, but not in the strong way described by that point in the Report.

Encountered in the following, and I only recently figured out why it was not working.

{-# OPTIONS_GHC -fglasgow-exts -cpp #-}
{-# LANGUAGE CPP #-}

import Data.Typeable

#ifdef __GLASGOW_HASKELL__
import GHC.Prim ( unsafeCoerce# )
#endif
#ifdef __NHC__
import NonStdUnsafeCoerce (unsafeCoerce)
#endif
#ifdef __HUGS__
import Hugs.IOExts (unsafeCoerce)
#endif
#ifdef __GLASGOW_HASKELL__
unsafeCoerce :: a -> b
unsafeCoerce = unsafeCoerce#
#endif

data Dy = forall a. Dy !a !TypeRep

fromDyM :: Typeable a => Dy -> Maybe a

-- compiles:
fromDyM (Dy a typeRep) =
  case unsafeCoerce a of
    unsafeResult | typeRep == typeOf unsafeResult -> Just unsafeResult
                 | otherwise                      -> Nothing

--fails to compile:
fromDyM (Dy a typeRep) = result
 where
  unsafeResult = unsafeCoerce a
--(unsafeResult) = unsafeCoerce a --works because of mono-pat-binds
--unsafeResult = unsafeCoerce a `asTypeOfMaybe` result --was my earlier "fix"
  result | typeRep == typeOf unsafeResult = Just unsafeResult
         | otherwise                      = Nothing

asTypeOfMaybe :: a -> Maybe a -> a
asTypeOfMaybe a b = a

Testing the above examples in Hugs seems to be useless because Hugs's monomorphism is totally broken (it tries to find the monomorphic type too early in a way that depends on what order the declarations are in).

Change History

Changed 6 years ago by Isaac Dupree

I must admit I became suspicious when noting in #1482 that -fno-monomorphism-restriction wasn't required for my examples.

Changed 6 years ago by simonpj

You give a lot of code here. Can you say exactly what is the problem that you are reporting? E.g. "With these flags I think this program should work, according to the manual".

Simon

Changed 6 years ago by Isaac Dupree

Okay, I admit that was a bit of a mess. I believe that this code should compile, but it doesn't under GHC:

loop = undefined
int = show loop
req :: Int; req = loop

Because the binding for 'loop' falls under the monomorphism restriction, it must have a monomorphic type; the 'req' line ensures that that type is "Int", which is in class Show, so "show loop" should compile, using the Show Int dictionary.

Changed 6 years ago by simonpj

No, loop does not fall under the monomorphism restriction because it is not overloaded. See Rule 1 of Section 4.5.5. of the Haskell Report; only "constrained" type variables are not generalised.

If you think something else is wrong, do report it; otherwise can you close the bug? Thanks

S

Changed 6 years ago by Isaac Dupree

  • status changed from new to closed
  • resolution set to invalid

Argh, you're right! I thought I had read that rule closely enough... I wonder if there's some way this can be made more obvious. (1) In the Report, don't use the word "restricted" to mean "restricted (to be monomorphic) IN SOME CASES" (maybe I'll ask haskell-prime). (2) the unsafeCoerce usage was a real one that confused me for a while. The only real solution I can think of is throwing out the confusing haskell98 monomorphism restriction in favor of mono-pat-binds... unless someone can come up with some kind of warning or error message that makes it clear. Usually non-function-, non-pattern-appearing bindings are at least constrained. However there is also the case where it does need to be polymorphic (unpacking a newtype that contains a universally quantified value, for example).

Well, at least, I think I understand what's actually happening a little better now... the report never says to generalize things in 'case', for example, so they're not generalized... in 3.17.3, case v of { x -> e } = ( \ x -> e ) v. The lambda function, just like a top-level function, has the bound variable as a rigid/monomorphic(4.5.4) variable, as inferred function types have to be rank-1.

Changed 5 years ago by simonmar

  • architecture changed from Unknown to Unknown/Multiple

Changed 5 years ago by simonmar

  • os changed from Unknown to Unknown/Multiple
Note: See TracTickets for help on using tickets.