gracelang / minigrace

Self-hosting compiler for the Grace programming language
39 stars 22 forks source link

Impossible to add methods to intrinsic objects #282

Open apblack opened 5 years ago

apblack commented 5 years ago

A student was implementing a matrix class, and wanted index objects. She wanted to introduce an infix operator on numbers to create the index objects, like @ to create points.

Completely replacing intrinsic (aka built-in) objects like Numbers and Strings with new kinds of objects is difficult. But simply adding a few methods to those objects is pretty easy. Many of the existing methods could be defined in a trait that the intrinsic classes use. The only real issue is where to put the definition of this trait, since it would have to be imported into intrinsic, and thus could not itself import intrinsic.

kjx commented 5 years ago

Kernan has a set of magically-named traits that do this

apblack commented 5 years ago

Kernan has a set of magically-named traits that do this

How does Kernan solve the circular dependency problem?

kjx commented 5 years ago

which problem?

apblack commented 5 years ago

The intrinsic module would need to depend on a user-customisable module. Hence, that user-customisable module can’t depend on the intrinsics, because doing so would introduce a circular dependency. How does one write any module that doesn’t depend on the intrinsics? No numbers, no Booleans, no if ... then ... seems hard.

kjx commented 5 years ago

I must be missing something - why can't intrinsics depend on Strings, say, without the intrinsic module importing the Strings or Booleans extensions modules. Those modules could be inside intrinsics (or standard Grace or whatever) so there's a dependency there, but I don't see an import loop.

apblack commented 5 years ago

It may be me that's missing something — after all, I haven't actually reorganized minigrace to get rid of all of the "magic" library stuff.

My thought was that Strings and Numbers, and Exceptions, and Sequences, and probably some other stuff that is required to make them all work, are defined in the module intrinsic. Then standardGrace would import intrinsic, and export what we are used to having.

If the classes for numbers and strings use traits called, say extraNumberMethods and extraStringMethods, then we have to ask: where are these defined? Your suggestion is the extensions module. So extensions can't depend on intrinsic — unless we do allow circular dependencies. Hence, when writing extensions, we are in a pretty bare-bones language. That doesn't make extensions impossible to write, but it would be hard.

I'm not sure what you mean by extensions being inside intrinsic. Modules are not inside other modules.

We could perhaps have primitiveIntrinsics define primitiveNumber, and have that module imported by extensions, (which defines extraNumberMethods). Then standardGrace imports both, and defines number as primitiveNumber use extraNumberMethods. Seems hard to do this efficiently ....

kjx commented 5 years ago

My thought was that Strings and Numbers, and Exceptions, and Sequences, and probably some other stuff that is required to make them all work, are defined in the module intrinsic. Then standardGrace would import intrinsic, and export what we are used to having.

The thing is that those methods need to be on String, Number, etc. They don't have to be imported anywhere: rather the VM just has to get them and install them in the right place. (at least that's what Kernan does and it seems to work)

I'm not sure what you mean by extensions being inside intrinsic. Modules are not inside other modules.

they're inside dialects

apblack commented 5 years ago

Yes, sure, we could make it magic: "the VM could do it". I was hoping to make Strings, Numbers etc. like any other objects: they are defined in a module, in this case the "intrinsic" module. Which would have to import the extensions module.

the VM just has to get them ...

If the VM doesn't get them from a module, from where does it get them?

kjx commented 5 years ago

they are defined in a module, in this case the "intrinsic" module

actually defined, or defined by magic...

Which would have to import the extensions module.

which I guess it could: the "extensions" could have normal classes with nothing special and abstract methods, and the intrinsics could nominally provide opaque implementation-specific subclasses of each one (intrinsics inherit from extensisons). This seems backwards but the more I think about it the more i like it. Alternatively the intrinsics module could export factory methods for opaque implementation specific objects, and the actual grace level objects could either delegate to or inherit from those objects.

at some point the VM has to have primitive objects - actually primitive ADTs and their instances - and their methods. where they come from, nobody knows... The point is that modules can depend on (e.g. request) methods on objects without having to import the modules defining those methods and objects.

apblack commented 5 years ago

I think that you are now suggesting more or less what I suggested above: that the extensions module would have to be written in a way that does not use intrinsics. No if(_)then(_)else(_), for example. Possible, but not very user-friendly.

kjx commented 5 years ago

I wrote a long rambling rely but then crystallised to: the VM can be a black box that magically breaks circularities, just in the same way the VM magically makes numbers and strings from literals --- precisely because the VM importing magic is part of the VM making primitives magic.

The VM can "import" these extensions, but no-one else ever needs to import them: other modules see VM supplied objects as just being there, without having to import anything that defines those objects. (Conceivably they import a magic VM object that acts as a factory, but again that module would be magic and would not itself import anything).

The other option is to do this for primitive objects but leave primitive dialect methods written in some empty dialect (that empty dialect may have to be another magic VM provided module because otherwise what dialect can it be written in?). This isn't so bad: it's more or less what Kernan and Moth do, I think: because they may not have the intrinsic forms, but they do have access to primitive objects and their methods, which is all the intrinsic dialect definition really needs - see https://github.com/mwh/kernan/blob/master/GraceLanguage/prelude.grace