I was playing around in the REPL trying to understand Data.NonEmpty
’s definition, so I came up with the following types and wanted to check their kind(?) when this error popped up:
> import Data.List (List(..), (:))
>
> data T f a = T f a -- ≈ Data.Tuple
> data T' f a = T' (f a)
> data T'' f a = T'' a (f a)
>
> x = T 1 "a" :: T Int String
> y = T' (1 : Nil) :: T' List Int
> z = T'' 27 (1 : Nil) :: T'' List Int
>
> :kind T
Type -> Type -> Type
> :kind T''
(Type -> Type) -> Type -> Type
> :kind T'
Error found:
at <internal>:0:0 - 0:0 (line 0, column 0 - line 0, column 0)
Type variable k is undefined.
while inferring the kind of k
while checking that type k
has kind Type
while inferring the kind of T' @k
See https://github.com/purescript/documentation/blob/master/errors/UndefinedTypeVariable.md
for more information, or to contribute content related to this error.
Went to link in the error description, but it does not seem to be related to this one (or I failed to make the connection). As far as I understand, f
in data T' f a
has to be a functor, so why is :kind
unable to infer this, yet it has no problem with T''
(or NonEmpty
)?
I can kinda help with this. The issue being bumped into here is called “polymorphic kinds” or “polykinds.” Here’s some references on the matter:
If you add a kind annotation to T'
:
data T' :: (Type -> Type) -> Type -> Type
data T' f a = T' (f a)
then it’ll work (of course that kinda defeats the purpose of asking the REPL to print the kind information).
I think it otherwise could have kind
data T' :: forall k. (k -> Type) -> k -> Type
I can’t do a good job of explaining why T'
needs the annotation and others don’t, and I can’t do a good job of explaining what other k
’s you could use to do something useful besides Type
.
1 Like
No worries and thank you! You already did plenty as it would have taken me quite a while to stumble upon “polymorphic kinds”… and I didn’t even know about kind signatures…
T'
needs an annotation because the kind of a
cannot be unambiguously inferred. It depends on what f
is. Which itself depends on what a
is.
For example:
> data F a = F a
> :kind T' F
Type -> Type
> data G a = G (a Int)
> :kind T' G
(Type -> Type) -> Type
In these two examples the kinds of a
are different. In the first example a :: Type
and in the second - a :: Type -> Type
. This means that the kind of a
has to be polymorphic.
In T
this doesn’t happen, because both f
and a
are used in a position where there would be a datum (e.g. T "foo" 42
), and therefore both f
and a
must be Type
. No polymorphism.
In T''
this doesn’t happen, because a
is used in a data position, and therefore a :: Type
, and then f a
is used in a data position, and therefore f a :: Type
, and therefore f :: Type -> Type
. No polymorphism here either.
What fascinates me is why REPL can’t show the polymorphic kind. This works fine at the type level:
> f x = x + zero
> :t f
forall (a25 :: Type). Semiring a25 => a25 -> a25
And the compiler itself is even able to figure this out when in IDE support mode:
(and, of course, when compiling)
So why not in REPL?
3 Likes