Primitive types and Prelude shenanigans

Simon Peyton-Jones simonpj@microsoft.com
Fri, 16 Feb 2001 04:14:24 -0800


|  | Some while ago I modified GHC to have an extra runtime
|  | flag to let you change this behaviour.  The effect was
|  | that 3 turns into simply (fromInt 3), and the
|  | "fromInt" means "whatever fromInt is in scope".
| 
| Hmmm... so how about:
| 
|   foo fromInt = 3
| 
| Would this translate to:
| 
|   foo f = f 3

This exactly what will happen.  But you are right to say that
it is perhaps not what you want.

Another alternative would be: "3" turns into "Prelude.fromInt 3",
where "Prelude.fromInt" means "whatever Prelude.fromInt is in scope".
So then you'd have to say
	import Prelude ()
	import MyPrelude as Prelude
(as Malcolm and Marcin suggested).  Maybe that's a good plan; it's a little
more
heavyweight.  [Incidentally, if this is nhc's behaviour, it's not H98.
The Report (tries to) stress that you get the "fromInt from the actual
standard Prelude" regardless of what is in scope.  That's why I'm not
going to make it the default behaviour.]

Yet another possibility would to be say you get "the unqualified 
fromInt that's in scope at top level".  But that seems worse.

Re Bools, Koen and Marcin write (respectively)

|  | [...] guarantee that (if otherwise e1 e2) = e1.
| 
| I do not understand this. "otherwise" is simply a function
| name, that can be used, redefined or hidden, by anyone. It
| is not used in any desugaring. Why change that behaviour?

| > So much for numerics.  It's much less obvious what to do 
| about booleans.
| 
| IMHO a natural generalization (not necessarily useful) is to follow
| the definition of the 'if' syntactic sugar literally. 'if' expands
| to the appropriate 'case'. So Prelude.True and Prelude.False must be
| defined, and they must have the same type (otherwise we get a type
| error each time we use 'if'). This would allow even
|     data FancyBool a = True | False | DontKnow a

The point is that there must be a *defined* desugaring.  The 
desugaring in the report defines the behaviour, but the compiler is
free to do differently.  If one is to be free to rebind types, the
desugaring must be fully defined.  Marcin suggests that 'if' is just
syntactic sugar.  But that would be a disaster if the new Bool type
didn't have constructors True and False.  For example, maybe Bool becomes
a function:
	type Bool = forall b. b -> b -> b
No constructor 'True'!  Here I think the right thing is to say that
desugaring for boolean constructs uses a function 'if' assumed to have
type 	
	if :: forall b. Bool -> b -> b -> b
Now the programmer can define both Bool and if, and the compiler will
be happy.  


My point is this: there is some *design* to do here.  It's not obvious
what the design should be.  But if anyone feels inclined to do the design
(in consultation with the community of course) then I'd be inclined to 
implement it in GHC.  (Though I'm not writing a blank cheque!)  
Decoupling the prelude is a desirable goal.

Simon