Conversation
This commit starts work on visible type applications. This change in particular adds support for parsing '@'-prefixed type variables that are consumed by visible type applications. They can also be referred to as VtaTypeVars to distinguish them from TypeVars. Future Work: * Fix up pattern matching for the `TypeVarBinding` type. * Add representation and conversion for VtaTypeVars on the `AST`.
Positions.hs * Recognize '@' in typeVarBindingRange Flatten.hs * Recognize '@' in flattenTypeVarBinding Utils.hs * Add ignore patterns for TypeVarBinding Convert.hs * Add ignore patterns for TypeVarBinding
Convert.hs * Fix up conversion to ForAll. Types.hs * Create the VtaForAll type with Aeson instances. * Insert VtaForAll as a field to the ForAll constructor. * Fix up pattern matching for ForAll. Note that NotVtaForAll is preferred as a "default", especially for internal utilities. Environment.hs * Fix up pattern matching for ForAll. Also add the tyForAllVta constructor.
This fixes a bug in the kind inference rule where the VtaForAll field is accidentally overwritten by NotVtaForAll.
This fixes a bug where Prim names aren't desugared in VisibleTypeApp, which omits their quantification, making them not unify properly.
|
CI errors seem to be related to JSON Encoding/Decoding as I'd added a field to the |
This doesn't change much of the current implementation.
This allows constrained types to be solved eagerly when performing visible type applications. As a consequence, non-VtaTypeVars are replaced with unknowns and are then generalized back down the type checking pipeline.
|
I've started working on having visible type abstractions available for type class members like so: class Functor f where
map :: forall @f a b. (a -> b) -> (f a -> f b)Although attempting to write an instance ends up with an error: data Id a = Id a
instance Functor Id where
map f (Id a) = Id (f a)emits: Error found:
in module Main
at Main.purs:9:10 - 9:14 (line 9, column 10 - line 9, column 14)
Could not match type
Id
with type
f0
while trying to match type Id t2
with type f0 a3
while checking that expression case f $0 of
f (Id a) -> Id (f a)
has type f0 b1
in value declaration $functorId1
where f0 is a rigid type variable
bound at (line 0, column 0 - line 0, column 0)
a3 is a rigid type variable
bound at (line 0, column 0 - line 0, column 0)
b1 is a rigid type variable
bound at (line 0, column 0 - line 0, column 0)
t2 is an unknown type
See https://github.com/purescript/documentation/blob/master/errors/TypesDoNotUnify.md for more information,
or to contribute content related to this error.If anyone could point me to the right direction regarding this I'd be more than delighted with the help! |
|
I'm not familiar with how type applications are implemented, so I'm just thinking out loud. Doesn't that error make sense? forall @f. class Functor f where
map :: forall a b. (a -> b) -> f a -> f band then it's like |
|
@JordanMartinez That's correct, although the goal is to have class Functor @f where
map :: forall a b. (a -> b) -> (f a -> f b)And this would work just fine with: mapArray = map @ArrayOn the other hand, could we do this for data constructors as well? data Either @a @b = Left a | Right b
left = Left @(Proxy "this binds to b") 0
right = Right @(Proxy "this binds to a") 0
maybeLeft = Left @(Proxy "this binds to a") @(Proxy "this binds to b")
maybeRight = Right @(Proxy "this binds to a") @(Proxy "this binds to b") |
|
A bit more commentary on why the visible type abstraction on the class member fails: as the purescript/src/Language/PureScript/TypeChecker/Types.hs Lines 292 to 294 in 7bf9e61 The alternative syntax that I've proposed would likely be more trivial to implement as it doesn't interfere with said type-checking routines. All that's needed to be done is for the map :: forall f a b. (a -> b) -> (f a -> f b)
-- into
map :: forall @f a b. (a -> b) -> (f a -> f b)Alternatively, there's also the choice of making |
|
Because of the new semantics for visible type abstractions in type classes, I've removed the
Also the changelog entry for this feature is getting a bit long and more tutorial-like. I plan on writing an article for my blog in the future, as this feature is released. Would linking to that be better? |
This prevents warnings from being emitted for type classes defined solely as type-level functions.
|
I'm okay with the length of the changelog, since it's basically covering the various usages. It's not optional information. I'll need to give more thought to the design. Definitely want to help move this along and make sure it's going to work well for users down the road. My initial question is: Thinking about it a bit more, I'm not sure I like the idea of the required type arguments as opposed to optional ones. On its own it seems fine, but how it interacts with other features is less clear (e.g. my question above). Like how will we print them differently from non-required type arguments? What if you alias a method with an ordinary declaration – can it still have a required type argument? Is it even necessary? Maybe we're trying to do too much work for the user with the feature. We already have Of course it's okay to leave some stuff for future work, but ideally we can plan ahead so that it won't require breaking changes. |
It would be great if we could support how things are printed currently, since you are doing that for polykinded types it would be great to also do it for polykinded instances. Lmk if you want help hacking on that, I don't anticipate too much difficulty given what you have already? |
|
There's something funny going on with constructors: > :t Tuple
forall (a1 :: Type) (b2 :: Type). a1 -> b2 -> Tuple a1 b2Printing their type doesn't include the vta quantification, even though they obviously support it! I would offer to fix it but I haven't found the right place yet :) |
I think this is because of how constructors are monomorphized and then generalized back, which causes the loss of information. This was actually something that I had to get around with the |
Not at the moment, no. If we do support it though, what do you imagine would the syntax look like? I've tried having them on the members before as sort of an "override", like this, but this is potentially confusing. class A a where
a1 :: forall @a. a -> Int
a2 :: a -> Int
This is indeed something to consider. The implementation is definitely designed around just accepting loss of information, for example, skipping is implemented as just losing the visibility for a type argument; aliasing too, would have certain implications. For what it's worth, GHC tries to be a bit smarter in that it keeps track of which unification variables should generalize into visible arguments—but then, should we follow GHC? 😄
For what it's worth, expanding With the implications in mind, it shouldn't be too hard making visibility opt-in for class heads. So this would still throw an class A a where
a :: StringWhile this won't throw anything: class A @a where
a :: StringUntil it's skipped, in which x = a @_ |
This algorithm now involves instantiating the appropriate quantified variables such that kind subsumption operates on unification variables instead of free ones.
|
The most recent changeset makes use of #4376. This brings the implementation closer to what is used by GHC, as only the |
| -- Collect all free type variables in visKnd, such that | ||
| -- quantifiers in bfrVis can be partitioned by whether | ||
| -- or not they appear within visKnd. | ||
| -- | ||
| -- Given: | ||
| -- | ||
| -- forall (j :: Type) (k :: Type) (@t :: k -> Type) | ||
| -- | ||
| -- Then: | ||
| -- | ||
| -- 1. freeInVisKnd: { k } | ||
| -- 2. bfrVisY: [ k ] | ||
| -- 3. bfrVisN: [ j ] |
There was a problem hiding this comment.
This doesn't take into account fringe cases such as:
forall (a :: Type) (b :: a) (@c :: Proxy b -> Type)Where a also has to be instantiated alongside b. I doubt the compiler is ever able to synthesize such a type signature in most regular usage.
If there ever is a need for this, one way to go about it is to build a directed graph to figure out which variables need to be instantiated, starting from the kind of the visible quantifier. For example:
forall (a :: Type) (e :: Type) (b :: a) (c :: a -> Proxy b) (@d :: Proxy c)Yields something along the lines of:
[ (d, [c]), (c, [a, b]), (b, [a]), (a, []) (e, []) ]All that's left is to create the unification variables and replace as necessary.
There was a problem hiding this comment.
Maybe this should be inlined into the code rather than this issue, then?
|
I suppose this PR can be closed, right? |
Closes #3137. This PR adds support for visible type applications and abstractions. The latter pertains to syntax for declaring which universally quantified variables can be instantiated with the former. Here's a simple example:
Unlike GHC's
TypeApplications, polytypes with variables that aren't prefixed with@cannot be used for visible type application:Visible type abstractions can also appear in class heads, like so:
Checklist: