[Haskell-beginners] Coercing existential according to type-level Maybe

Dmitriy Matrosov sgf.dma at gmail.com
Thu Dec 30 13:10:01 UTC 2021


Hi.

I've tried to write a function to coerce existential value according to type
stored in type-level 'Maybe' (i.e. 'Just or 'Nothing).

> {-# LANGUAGE DataKinds #-}
> {-# LANGUAGE RankNTypes #-}
> {-# LANGUAGE KindSignatures #-}
> {-# LANGUAGE PolyKinds #-}
> {-# LANGUAGE TypeFamilies #-}
> {-# LANGUAGE GADTs #-}
>
> import Data.Kind
> import Data.Proxy
> import Unsafe.Coerce
>
> class FromTypeMaybe k where
>     type ToType k
>     fromTypeMaybe :: (a -> ToType k) -> Proxy k -> a -> Maybe (ToType k)
>
> instance forall (t :: Type). FromTypeMaybe ('Nothing :: Maybe t) where
>     type ToType 'Nothing = t
>     fromTypeMaybe f _ x = Nothing
>
> instance forall (t :: Type). FromTypeMaybe ('Just t :: Maybe Type) where
>     type ToType ('Just t) = t
>     fromTypeMaybe f _ x = Just (f x)
>
> data Any where
>     Any :: a -> Any
>
> unAny :: Any -> t
> unAny (Any x) = unsafeCoerce x

This works as far as i can see

    *Main> fromTypeMaybe unAny (Proxy @('Just Int)) (Any 3)
    Just 3
    *Main> fromTypeMaybe unAny (Proxy @'Nothing) undefined
    Nothing

but i don't understand how 'Nothing instance works:

                   type   kind                                  kind?
                    vvv  vvvvvv                                  vvv
    instance forall (t :: Type). FromTypeMaybe ('Nothing :: Maybe t) where
         type ToType 'Nothing = t
                               ^^^
                               type

(in case alignment breaks: variable 't' in forall is type-variable with kind
'Type', but in "Maybe t" in instance head it's used as kind-variable. And then
in associated type-family the same variable 't' is used as type-variable
again).

If i try to write "'Nothing"'s kind as 'Maybe Type' (in the same manner as
'Just has)

    instance forall (t :: Type). FromTypeMaybe ('Nothing :: Maybe Type) where

i get an error:

    1.lhs:21:3: error:
        • Type variable ‘t’ is mentioned in the RHS,
            but not bound on the LHS of the family instance
        • In the type instance declaration for ‘ToType’
          In the instance declaration for
            ‘FromTypeMaybe ('Nothing :: Maybe Type)’
       |
    21 | > instance forall (t :: Type). FromTypeMaybe ('Nothing ::
Maybe Type) where
       |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...

Well, i'm not sure, that understand why 'forall' in instance head may not be
used as family's LHS, but.. that's probably ok.  On the other hand, if i try
to write 'Just instance using type-variable 't' as kind-variable (in the same
manner as 'Nothing has):

    instance forall (t :: Type). FromTypeMaybe ('Just t :: Maybe t) where

i get an error too:

    1.lhs:25:47: error:
        • Expected kind ‘Maybe t’, but ‘'Just t’ has kind ‘Maybe *’
        • In the first argument of ‘FromTypeMaybe’, namely
            ‘('Just t :: Maybe t)’
          In the instance declaration for
            ‘FromTypeMaybe ('Just t :: Maybe t)’
       |
    25 | > instance forall (t :: Type). FromTypeMaybe ('Just t :: Maybe t) where

Well, that's also probably expected, because though i've declared
type-variable 't' with kind 'Type' in forall, due to 'DataKinds' extension
type 't' is also promoted to kind 't' and, thus, by using ('Just t :: Maybe t)
i say, that ('Just t) should have kind 'Maybe t', not 'Maybe Type', which it
really has.

But if that's so, how working 'Nothing instance

    instance forall (t :: Type). FromTypeMaybe ('Nothing :: Maybe t) where

may work at all? It has the same problem as 'Just instance above:
type-variable 't' is promoted to kind 't' and 'Nothing will have kind 'Maybe
t' instead of 'Maybe Type' ?


More information about the Beginners mailing list