reaver-project / vapor

Vapor is a language made as a part of the Reaver Project effort. The name has originated as a word play on a word "vaporware" - the author often called this language a "vaporlang", and later decided to keep that name.
Other
5 stars 4 forks source link

analyzer: implementation of typeclasses. #50

Closed griwes closed 5 years ago

griwes commented 5 years ago

Current and/or expected limitations

The matching algorithms for "is this a specialization of that typeclass function?" for the purposes of recognizing what is going on in the first place is currently limited to matching by the number of arguments. This of course limits overloading beyond what I want the limits to be, and has to be fixed in a future version of the compiler.

Implementation notes

This section of this comment exists literally to let me wrap my head around a possible implementation strategy for typeclass instances, as this appears to be a part of the compiler that will actually require having notes for me to be able to implement this (and even verify that I'm doing things in roughly the right way).

Steps to instantiate a typeclass and then get an actual instance:

  1. Create a bare bones instance expression object; this needs to refer to the original templated type. Save the arguments here (or is it even necessary?). Track functions/function declarations that haven't been overwritten yet in this class. (This will be an additional vector; is there a chance I could introduce an intermediate "builder" class for this? Probably; try to do this when first implementing the thing..)
  2. The bare bones instance expression object should also, at the same time, cache an actual type of instances (i.e. seeing how the template is function (parameters...) -> typeclass, this is the value of type typeclass).
  3. The expression from (1) should be saved in the instantiations cache, even though it won't be very expensive, really. (As this is just the entry point, this shouldn't be very expensive, but nevertheless we can just cache it.)
  4. Copy the cached "builder".
  5. Go through definitions in an instance_literal, adding them to the builder. Match each of them against the original typeclass literal.
    • Make it an error to define something multiple times.
    • Make it an error to define something that wasn't defined in the typeclass.
    • Make it an error to define a function unmatched in the typeclass literal.
    • Make it an error to declare a function. (Check that this is actually guaranteed on the parser level!)
    • For each named function definition, make sure there's an overload_set that's specific to this instance that has the functions added.
  6. If there are some declarations in the typeclass literal that weren't overriden in the instance literal, error and tell the user to define them.
  7. If there are some definitions in the typeclass literal that weren't overriden in the instance literal, instantiate those (here's where the points below - about analysis_context - come into light). Make sure the functions land in a new overload_set that's specific to this instance. (Same note as last bullet point in (4).)

General notes I couldn't capture in the sketch of the algorithm above:

Here's also other random notes that I made to myself when writing down the above points:

Some problematic things

I can fix this problem by making T(arguments...) always refer to the type of instances, and give it a member or a member function called ??? (need to bikeshed the name so hard). This would make it less convenient to get the default instance, but would fix the unsoundness and inconsistency of the system.

After bikeshedding with some people, here's the solution: T(arguments...) refers to the type of instances; default(T(arguments...)) refers to the default instance of T(arguments...).