core lib attempt#2: problem with hard-wired module names for classes in GHC

Hello glasgow-haskell-users, i'm now trying to rewrite GHC.* in more portable fashion, "boxifying" all the definitions in hope that smart compiler will unbox them itself of course, i'm using -fno-implicit-prelude and have found some more things that still hard-wired into compiler: 'c' syntax creates GHC.Base.Char instead of Char [a..b] requires instance of GHC.Enum.Enum it will be great to fix this (i will make ticket), but that is not show-stopper the problem that i'd imagined is how this new cool base library can be used with existing GHC version? of course, users will not use -fno-implicit-prelude, so their apps will expect that Enum class is defined in GHC.Enum and no other place. or it can just re-export class defined in some other module, say Core.Enum? even more problems with GHC.Base mpdule which should define both some hard-wired types and classes what means that we can't move out of this package some intermediate code (at least, without using recursive modules) about core library attempt#2 - the main problem in bootstrapping Haskell libs (for any compiler) is the expressiveness problem - for example, in order to define "instance Show Int", you need to use '/' operation which is defined in Integral class that in turn requires Show instance. GHC solves this problem by omitting use of classes in low-level modules and switching to the # operations. this perfectly works, but don't compatible with other compilers. what are other possible solutions? 1) lift all the # operations to the corresponding operations on boxed types and change low-level modules so they work with these un-classified but boxed operations, so the following: GHC.Base: divInt (I# a) (I# b) | gtInt# a b = mulInt# a b turns into: GHC.Base: gtInt (I# a) (I# b) = gtInt# a b mulInt (I# a) (I# b) = mulInt# a b Core.Base: divInt a b | gtInt a b = mulInt a b 2) another solution is to bring together definitions of Show/Enum/Num/Integral classes and their Int/Integer instances in one large module. after all, by the limiting it to only Int/Integer instances, its size should be reasonable. This will allow to use "natural" definitions (i.e. use '*' and '>' operators), although i fear that this can produce slower code. in particular, this can create circular dictionary dependencies (i.e. Show Int instance depends on Num Int and vice versa, which, according to my experience, prevents inlining of all methods (!) in both classes; although my experience was with parameterized instances (i.e. like Show [a] <=> Num [a]), so may be it will be not a problem here) it seems that integer literals in Hugs completely don't work before introduction of fromInteger operation. so it seems that the second way is the only really possible one :) it will be great to hear critics before i will invest time in implementing second plan. as a first result, i plan to refactor GHC.Base, Show, Enum, Num, Real modules into somewhat implementing the same facilities but compatible with Hugs. i think that this will make a solid foundation for future co-development of core lib -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

| of course, i'm using -fno-implicit-prelude and have found some | more things that still hard-wired into compiler: | | 'c' syntax creates GHC.Base.Char instead of Char | [a..b] requires instance of GHC.Enum.Enum What do you think needs fixing? GHC's flag -fno-implicit-prelude currently means two things a) don't import Prelude b) use rebindable syntax Now (b) is not fully implemented, as you found, and there are other examples. E.g. guards use Bool, which is GHC.Base.Bool, not the Bool that's in scope. It would not be easy to make everything rebindable; and I am not sure why you want to for this project. If what you want is to move Char from GHC.Base to some other module, then we can easily teach GHC about the new location (I can tell you how to do that). That'd be much easier than the rebindable syntax thing. It would indeed mean that you could not use the new lib with an old GHC... but GHC and its base library are pretty closely coupled. They are gradually getting less coupled, but I doubt we can eliminate coupling altogether. Simon | it will be great to hear critics before i will invest time in | implementing second plan. as a first result, i plan to refactor | GHC.Base, Show, Enum, Num, Real modules into somewhat implementing the | same facilities but compatible with Hugs. i think that this will make | a solid foundation for future co-development of core lib But GHC's organisation evolved over some time, and we reorganised several times after discovering "gotchas". You mention some of them ... but read the library organisation notes at the top of GHC.Base. Getting the organisation to work wasn't all that easy -- and you have more constraints than we did (notably, supporting multiple compilers). In short: good project, but maybe a bit of a thankless task. I'm not 100% sure it's worth the bother. It might be easier to specify a "core prelude" that is implemented differently on each compiler, and then have a compiler-independent libraries built on the core. That is why there's a sub-directory GHC. Each compiler can have its own sub-directory; but each of these compiler sub-directories implements the same interface. That would be my suggestion, but I have not been following in detail Simon

Hello Simon, Wednesday, October 4, 2006, 5:00:25 PM, you wrote:
| 'c' syntax creates GHC.Base.Char instead of Char | [a..b] requires instance of GHC.Enum.Enum
What do you think needs fixing?
using types and classes that are in scope, of course. i have several more examples but because it's not required for me nor important for you, i will put them all into a ticket. ok?
GHC's flag -fno-implicit-prelude currently means two things a) don't import Prelude b) use rebindable syntax
Now (b) is not fully implemented, as you found, and there are other examples. E.g. guards use Bool, which is GHC.Base.Bool, not the Bool that's in scope.
moreover, guards are desugared into monads, that was a bit surprising :) my attempt to refactor base library without recompiling entire GHC was breaked by these restrictions. so now i import genuine GHC.Base and then make my development on top of it what is the simplest way to experiment with my own version of base library? can it be accomplished without recompiling entire GHC?
If what you want is to move Char from GHC.Base to some other module, then we can easily teach GHC about the new location (I can tell you how to do that). That'd be much easier than the rebindable syntax thing.
It would indeed mean that you could not use the new lib with an old GHC... but GHC and its base library are pretty closely coupled. They are gradually getting less coupled, but I doubt we can eliminate coupling altogether.
guess, that i'm trying to do? ;) is it possible to define, say, Enum class in module Core.Base and then import this module in GHC.Enum? this will make circular dependency - usage of Enum instances in Core.Base will be resolved via definition in GHC.Enum while this will import for definition Core.Base. but this scheme will allow compatibility with existing GHC versions that is a big plus. so can it work?
| it will be great to hear critics before i will invest time in | implementing second plan. as a first result, i plan to refactor | GHC.Base, Show, Enum, Num, Real modules into somewhat implementing the | same facilities but compatible with Hugs. i think that this will make | a solid foundation for future co-development of core lib
But GHC's organisation evolved over some time, and we reorganised several times after discovering "gotchas". You mention some of them ... but read the library organisation notes at the top of GHC.Base. Getting the organisation to work wasn't all that easy -- and you have more constraints than we did (notably, supporting multiple compilers).
these notes are my Bible now :))) i hope to make all these modules Hugs-compatible in a few days
In short: good project, but maybe a bit of a thankless task. I'm not 100% sure it's worth the bother.
shortly speaking, in ideal goals are: 1) make base library compiler-version independent 2) include support for other compilers (yhc and jhc) and make cheaper "entry ticket" for any new compilers 3) stop replication between compilers of almost the same code for base classes and their instances. it will be nice to have only one instance of this code and may be this can help H' committee
It might be easier to specify a "core prelude" that is implemented differently on each compiler, and then have a compiler-independent libraries built on the core. That is why there's a sub-directory GHC. Each compiler can have its own sub-directory; but each of these compiler sub-directories implements the same interface. That would be my suggestion, but I have not been following in detail
if mountain don't walk to Magomet then Magomet can walk to mountain :) i mean that now yhc and jhc compilers are don't implement existing "core prelude" interface so one can try to help them this project may be considered as a next step of simplification of the "core prelude". i hope that this will also help to reach other goals mentioned and refactoring (mainly boxifying) of existing GHC.* code turn out to be very easy. just now i'm close to compilation of Base, Show, Enum, Num and Real modules with Hugs. i'm hope that in case of success you will consider replacing current GHC.* with these modules, but anyway i have at least one "customer" - Neil :) and while we are here, can you please answer several questions? there are several definition that use c@(C# _), like this: jhead n cs | n < 10 = case unsafeChr (ord '0' + n) of c@(C# _) -> c : cs | otherwise = case unsafeChr (ord '0' + r) of c@(C# _) -> jhead q (c : cs) where (q, r) = n `quotRemInt` 10 i guess that it is for strictness and can be replaced with calls to 'seq': c -> c `seq` c : cs There a few of Float/Double-related RULES in GHC.Base: {-# RULES "plusFloat x 0.0" forall x#. plusFloat# x# 0.0# = x# .... Why they are here - by mistake or it has some sense? -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

| using types and classes that are in scope, of course. i have several | more examples but because it's not required for me nor important for | you, i will put them all into a ticket. ok? OK | what is the simplest way to experiment with my own version of base | library? can it be accomplished without recompiling entire GHC? Check out http://hackage.haskell.org/trac/ghc/wiki/Commentary/Compiler/WiredIn and please add to it as you learn more. If you want to change which wired-in or known-key things live in which module then you have to change PrelNames, and recompile GHC. Not a big job; you only change one module. If you are not changing which module things live in, then you don't need to recompile GHC. | guess, that i'm trying to do? ;) is it possible to define, say, Enum | class in module Core.Base and then import this module in GHC.Enum? | this will make circular dependency - usage of Enum instances in | Core.Base will be resolved via definition in GHC.Enum while this | will import for definition Core.Base. but this scheme will allow | compatibility with existing GHC versions that is a big plus. so can it | work? Trying to keep "compatibility with existing GHC versions" is a very strong restriction. Making recursive loops in GHC/* modules is likely to be bad. I advise against it. | there are several definition that use c@(C# _), like this: | | jhead n cs | | n < 10 = case unsafeChr (ord '0' + n) of | c@(C# _) -> c : cs | | otherwise = case unsafeChr (ord '0' + r) of | c@(C# _) -> jhead q (c : cs) | where | (q, r) = n `quotRemInt` 10 | | i guess that it is for strictness and can be replaced with calls to 'seq': | | c -> c `seq` c : cs I think so, yes. Provided seq is in scope. | There a few of Float/Double-related RULES in GHC.Base: | | {-# RULES | "plusFloat x 0.0" forall x#. plusFloat# x# 0.0# = x# | .... If the RULE was in a random module M, GHC would need to be sure to read M.hi just in case the rule was useful. If you move this RULE to GHC.Float, say, then GHC will have to read GHC.Float on every compilation, just in case you call 'plusFloat#'. But GHC.Base is read every compilation anyway, so it seems the best place to put it. Simon
participants (2)
-
Bulat Ziganshin
-
Simon Peyton-Jones