如何显示类型变量之间的关系

时间:2019-01-25 19:30:41

标签: haskell functional-programming ghc dependent-type higher-kinded-types

对于类型DoorHallway

 data DoorState :: Type where
   Opened :: DoorState
   Closed :: DoorState
   Locked :: DoorState
   deriving (Bounded, Enum, Eq, Ord, Show)

 data Door :: DoorState -> Type where
   Door :: {material :: String} -> Door s
   deriving (Show)

 data Hallway :: [DoorState] -> Type where
   Origin :: Hallway '[]
   Section :: Door ds -> Hallway dsl -> Hallway (ds : dsl)

appendHallway的定义有效:

 appendHallway :: forall (ds :: DoorState) (dsl :: [DoorState]). Door ds -> Hallway dsl -> Hallway (ds : dsl)
 appendHallway d rest = Section d rest

appendHallway部分中明确指出dsdsl之间的关系的forall的定义不起作用:

 appendHallway :: forall t (ds :: t) (dsl :: [t]). (t ~ DoorState) => Door ds -> Hallway dsl -> Hallway (ds : dsl)
 appendHallway d rest = Section d rest

返回的错误如下:

 error:
     • Expected kind ‘DoorState’, but ‘ds’ has kind ‘t’
     • In the first argument of ‘Door’, namely ‘ds’
       In the type signature:
         appendHallway :: forall t (ds :: t) (dsl :: [t]).
                          (t ~ DoorState) => Door ds -> Hallway dsl -> Hallway (ds : dsl)
     |
 351 | appendHallway :: forall t (ds :: t) (dsl :: [t]). (t ~ DoorState) => Door ds -> Hallway dsl -> Hallway (ds : dsl)
     |                                                                           ^^

上面的示例可能有些人为,但是在某些情况下,指示较高种类的类型变量之间的关系将是有帮助的,甚至是必要的。此错误是对GHC当前版本的限制吗?还是在GHC的将来版本中,上述错误也是荒谬的?是否有另一种方式可以表达GHC会接受的dsdsl之间的关系?

2 个答案:

答案 0 :(得分:2)

Haskell具有用于计算,类型和种类的独立命名空间。当你写

forall (ds :: t). ...

变量t是种类级别的变量,但是在您编写时

t ~ DoorState => ...

变量t是类型级别的变量t,这是一个完全不相关的变量。确实,所有类型平等仅在类型级别上。据我所知,在目前的GHC Haskell中根本没有任何形式可以将种类相等表示为约束。

答案 1 :(得分:2)

您写的东西确实是胡说八道。 =>的LHS的约束仅在值级别上存在,就像->的LHS的约束仅在值级别上一样。更具体地说(尽管这是一个微弱的内存),a ~ b的一个实例在其中包含基本类型为a ~# b的实例(以相同的方式data Showable = forall s. Show s => Showable s持有一种类型的对象) Type)。您实际上需要完成a ~# b的所有工作,但需要解开a ~ b的包装才能完成。但是,您无法以一种无法谈论a ~ b中的DoorState的方式谈论类型中的值级别参数DoorState -> Door _can'tTalkAboutTheDoorState

您可以做的是这个。定义

type family Cast (x :: (a :: Type)) :: (b :: Type) where
   Cast x = x
-- this seems strange, but realize the equation is actually
-- Cast @a @a (x :: a) = (x :: a)
-- Cast-ing some type from one kind to another only goes through
-- when they're actually the same kind
-- can make a, b, or both explicit instead of implicit

然后

appendHallway :: forall t (ds :: t) (dsl :: [t]).
                 (t ~ DoorState) =>
                 Door (Cast ds) ->
                 Hallway (Cast dsl) ->
                 Hallway (Cast ds : Cast dsl)
appendHallway d rest = Section d rest

只要知道t ~ DoorState,类型族应用程序Cast @t @DoorState dsCast @[t] @[DoorState] dsl就会分别减少为dsdsl