-
Notifications
You must be signed in to change notification settings - Fork 140
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support superclasses with (dependent) type variables not in the base typeclass #633
Comments
Note that there is a workaround, for typeclasses that have associated type constructors. The original example can be made to work by replacing the new variables with constructors on the base variables:
|
The superclass info is currently used in two places in BSC: (1) In (1) The conversion in
The problem here is that the type of the field (2) Typechecking infers provisos from the user's code, and then attempts to resolve them all, given a set of explicit provisos that the user wrote. For example, if we have this code:
the use of
These provisos to be resolved are often called I was assuming that the solver adds the superclasses of the How it works is that I have a patch that at least updates
(Ah, here is where we see the fields of the The fresh variable This works for resolving a
This resolves and we learn the substitution that Unfortunately, this list of superclasses isn't preserved. Later, when attempting to resolve an
Because it's not using
A third variable can depend on the other two, so this matching needs to have two types in common, and only So the fresh variables from the superclass information needs to stored somehow. One way to do this would be to just add the superclasses to the |
This is bug 1504 in Bluespec Inc's pre-GitHub bug database. But was also brought up in a recent example:
In this case, it was an attempt to bundle two provisos under one simpler name -- allowing the user to just write
IsSmallerBits#(t)
and not have to write all the other provisos, which are implied. However, this situation can also arise in more typical uses of typeclasses.BSC will accept the above code without an error, but that's because it has lazily inserted the error into the symbol table entry for this class. If you write code that uses the class, then you get an error:
You can also get this error just by forcing evaluation of the symbol table, such as by using the flag
-dsyminitial
(the first of the symbol table dump points).The error occurs in
getCls
(inMakeSymTab.hs
) which constructs the entry for a typeclass in the symbol table. The entry has several fields, includingsuper
which contains the superclass declarations. In the definition ofsuper
(line 786),getCls
converts the untyped CSyntax parsed from what the user wrote into typed CSyntax -- meaning that the kinds for type variables have been filled in. This is done using the functionconvCTypeAssumps
which takes a list of "assumptions", aka pairings of variables to their kinds. When it finds a type variable whose kind it doesn't know, it reports the user errorT0008
that we saw. (This potentially should have been an internal error.) The reason that the variable isn't found in the assumptions is because BSC only created assumptions for the variables that appear in the base -- in the example, onlya
and not_t
and_k
.BSC has performed kind inference and reported if the user wrote superclasses that don't kind/typecheck. But BSC has not kept around the results of that inference, by the time that the symbol table is being constructed. The kind inference was performed by
inferKinds
inInferKinds.hs
, and typeclasses specifically are handled ininferKDefn
at line 89. You'll see a comment there:The comment is on code that gets the list of variables from the superclasses which are not already mentioned in the base typeclass (although its use of
concatMap
and\\
is wrong and should be fixed). I added this comment in 2008, when I thought I was making BSC support such variables in superclasses (Bluespec Inc bug 1503). But this just allows BSC to compile the typeclass and store the error lazily in the symbol table; any example that attempts to use it will still fail (thus bug 1504 was opened). Some place does need to check that the new variables are dependent on the base variables, but it may be easier to do that ingetClass
than ininferKDefn
.So
inferKinds
has checked that the kinds are OK. But it has not recorded the kinds that it learned for the superclass variables. What it returns is an assumption map, pairing top level names to their kinds. So it contains entries like:This is the information that
getCls
has available. This would either need to be expanded to include the kind information for superclasses, orgetCls
would need to re-run kind inference on the superprovisos. It is already using theKI
monad to do the conversion; I don't think it's too much extra work to repeat the inference.I have written code that does this inference in
getCls
, but the example is not working yet (once I add in an instance of the typeclass and a function that carries it as a proviso). Either I need to debug the output of my code, or something also needs to be changed in the place where superclasses are introduced. I suspect that, when superclasses are added to the known contexts, the non-base variables might need to be instantiated as fresh variables (dependent, but not bound). That's still to be explored. Also, I'm assuming that this point needs the kinds in place -- if they don't, thengetCls
wouldn't need to do kind inference; but I think it does (though that's also still to be explored).A few things to note. The symbol table is constructed recursively -- this allows the construction of the typeclass' entry to lookup other typeclasses and get their entries -- but it means that we need to be careful where you add
trace
statements when debugging, to be sure not to force evaluation of things you don't need, resulting in GHC reporting<<loop>>
. (So, for example, wheregetCls
returns a tuple of the new entryc
for the symbol table and a list of errorserrs
, it is safe to add trace statements attached to the errors, but not attached to the symtab entry.) Also, the symbol table is constructed in steps, resulting ingetCls
being called twice -- and I think the results of the first call are not expected to matter. The first step of constructing the symbol table fills in most information, but doesn't have legitimate data for the typeclasses yet (or, at least, some fields of the entry are not legitimate yet, such as the superclasses); the second step adds in the corrent typeclass entry. This is possibly done because of the recursion, to provide a base that the second step can build on? The first call togetCls
, the assumption map is entirely empty (so any typeclass with superclasses will have a lazy error in the entry at that point). MaybegetCls
could be updated to not compute the superclasses the first time, but it doesn't seem to hurt.It's also worth noting that
getCls
is called both on the typeclasses defined in the current package (Cclass
) and on the typeclasses from imported files (CIclass
). When we write tests for this, should make sure to test when the typeclass is imported.We should also test, as I said, that the new variables are determined by the base variables. If not, this is a situation we call "ambiguous" and report when user writes in type signatures. This is checked by
checkForAmbiguousPreds
inTCMisc.hs
, and reportsEAmbiguousExplCtx
(T0079), which is tested inbsc.typechecker/context-errors
on examples like these:Also, I know that BSC reports kind errors in superclasses (having checked it in the past), but I don't know if this is being regression tested in the testsuite. We should check for that and add a test if not.
The text was updated successfully, but these errors were encountered: