Conversation
|
Note that while this is a lot, the suggestions for these will largely compile fine (less variation in kind names), and are usually presentable, so I think automation will be fine. It is a lot though. |
|
In a similar vein, now we have kind signatures it might be nice to suggest adding them when defining partially applied type synonyms also (for the documentation purposes argument). That's a separate issue though. |
|
@hdgarrood I've addressed the doc and todo you pointed out. |
|
How do others feel about the kind signature warning? Do we want to inflict that many warnings on the community? |
|
Perhaps it might be worth raising this on Discourse? There are around 300 packages in the set, which is less than one warning per package on average, so perhaps it's not that bad. Then again I suspect there are probably a few packages with a lot of warnings, i.e. they're probably not evenly distributed. |
Yes, this is true. Many of them are occupied by |
|
It's basically anything that is phantom, or |
|
I wouldn't have a problem with imposing it - it's warnings, not errors, so aside from when people are building |
Bring `polykinds` branch up to master
hdgarrood
left a comment
There was a problem hiding this comment.
I feel like this is probably okay to merge now? I haven't looked at the entire diff, but what I have reviewed looks good to me, and this has already had a decent amount of testing.
|
Thanks all! 🎉 |
|
Author of GHC's I'm very curious about your experience in adapting these ideas to PureScript. I'm not sure that further comments on this GitHub trail are the best way, but I thought posting here would get the attention of those that would want to join the conversation. Happy to have a call or email thread on this. Oh, and 🎉 ! |
|
@goldfirere Absolutely! The best place for public discussion is the PureScript Discourse instance. Otherwise, maintainer emails are listed in the project package file and I believe are current. |
This PR implements PolyKinds based on Kind Inference for Datatypes.
Type In Type
The type-checker now supports
TypeInType(orType :: Type), so the oldKinddata type and namespace is now gone. Kinds and types are the same and exist in the same namespace.Previously one could do:
Where the kind
Booleanand typeBooleanwere two different things. This is no longer the case. ThePrimkindBooleanis now removed, and you can just usePrimtypeBooleanin the same way. This is a breaking change.The compiler still supports the old
foreign import kindsyntax but it warns that it's deprecated.It is treated internally as:
Likewise,
kindimports and exports are deprecated and treated the same as a type import or export.The special unary
#syntax for row kinds is still supported, but deprecated and will warn. There is nowPrim.Row :: Type -> Typewhich can be used like a normal type constructor.All of these deprecations have suggested fixes in the JSON output, so tools like
purescript-suggest(or your IDE plugin) can automatically apply them.Kind Signatures
With PolyKinds, all type-level declarations are generalized.
Previously, this had the
Type-defaulted kindType -> Type. Now this will be generalized toforall k. k -> Type.When writing polymorphic things, it's often nice to write signatures. Since we don't really quantify over free type variables, it's also necessary in the case that two poly-kinded arguments must have the same kind. I've introduced kind signature declarations, similar to standalone kind signatures in GHC.
In GHC, all signatures use the
typeprefix, but I've opted to have it use the same keyword as the subsequent declaration because we already haveforeign import data(rather thanforeign import type) and because it makes things align nicer. Signatures have the same rule as value-level signatures, so they must always be followed by the "real" declaration.I've also introduced the
Prim.Constraintkind to support signatures on classes. For example,Prim.Row.Consnow has the signature:Implementation Details
TypeChecker.KindsObviously this is where the bulk of the changes have happened since it is completely rewritten. It's mostly a straightforward implementation of the paper, but extended in a few ways:
Functorinstance forProxyis really an instance forProxy @Type, and we need that information up front when solving.The API surface area is a little larger to account for this of course. The main change is that
kindsOfAllaccepts class declarations as part of the binding group.TypeChecker.MonadTypeChecker.SynonymsSynonym expansion takes kind arguments, which are also substituted. This is needed for things like polykinded type class dictionaries, because the fields in the dictionary record are annotated with the kind provided to the synonym.
TypeChecker.UnifyThis has changes for kind applications, and also kind checks unification solutions.
TypesKind/Typedistinction is gone, so anything previously referencing kinds is now replaced withType.KindAppwas added for instantiated kinds.Skolems are tagged with their kinds. This is needed in the solver. Previously the kind-checker looked up it's type variable in the environment but it isn't the case that a skolem's type variable is necessarily in scope. This was only a coincidence because it only checked explicitly provided kinds.Constraintnow carries kinds as well as types. This is necessary for solving things likeRowToListwhere the kind ofRowListdepends on the kind of theRowwe are converting. In a future with fullConstraintKinds, this will just beKindApps andTypeApps.One interesting consequence of supporting rows in the kind-checker is that
()has the kindforall k. k -> Row k. This means the kind checker will annotate()with a kind-application (eg.() @Type). I added a pattern synonymREmptyKindedand replaced cases ofREmptywith this to account for anything that relies on matchingREmptyto work, but now also must handleKindApp REmpty ty. ReallyRConsshould also have a kind application of some sort, but since it's trivial enough to infer the kind, I didn't bother.Sugar.TypeClassesThe overall desugaring for type classes remains the same, but I've had to change how they are added and represented in the environment. Previously the compiler just turned a class into a type synonym for a record, and added that kind to the type environment. This meant that classes had a kind
Typeinstead ofConstraint, which is not compatible with top-level kind signatures. I've changed this type synonym to have the name-mangled formClass$Dictso that the actual class can use the "real" name in the environment. You can't actually write this name-mangled form, but it might need some care in IDE code to prevent it from suggesting it for import.Since this happens before type-checking (and thus before kind-checking), there's some dodgy code to expand type synonyms before kinds are known. I don't know if this is a problem in practice (you might just get garbage ill-kinded results and bad errors). My opinion is that this desugaring happens too soon in the pipeline and should happen after other type declarations are kind checked.
Sugar.BindingGroupsSince all type-level declarations are kind checked now, we need to do a more thorough dependency analysis.
ErrorsandPretty.TypesThe kind-checker elaborates all
foralls to have explicit kinds, and also introduced explicit kind-applications. In general, we don't want to expose this to users in errors and warnings. For example, with the following top-level declaration:You'll get a warning with a type signature. The fully elaborated signature is:
But we still suggest:
forall t6. Proxy t6By default when printing these types in errors, all kind applications and kinds annotations are removed, with type variables being pruned based on usage. This gives us something that you'd write today in the existing compiler. Note that I am not doing this cleanup when printing error hints, because I think it adds important context. When the
--verbose-errorsflag is provided, you will always see fully elaborated kinds in errors.Note that there is no support for explicit kind-applications in the parser, so these types can't necessarily be written.
Future Work
My hope is that this PR also enables several long-desired type-level features:
ConstraintKinds- I think this should be straightforward. It's primarily the matter of replacing the second-classConstraintdata type withTypeand doing work in the solver (the devil is always in the details, however). Otherwise the kind-checking aspect should just work.DataKinds- This is a matter of putting appropriate data constructor signatures in the type enivironment and any syntactic changes. Otherwise it should just work.ExistentialQuantification- This will need syntactic changes, a small addition to the kind-checker for constructor declarations, and some work to introduce the existential type variables and dictionaries in case branches.I also think there's some potential improvements we could make to the architecture:
replaceTypeSynoynmsand it's very easy to get wrong. Since we need to kind check everything now anyways, I think it would be straightforward enough to do this in a single place.Remaining TODOs