GADT 是否会破坏 Haskell 中的等式推理
Do GADTs break equational reasoning in Haskell
我有以下使用操作包中的 ProgramT 的解释器
{-# LANGUAGE GADTs #-}
import Control.Monad ((<=<))
import Control.Monad.Operational
data Motor' a where
--Define GADT
serialNewportT :: (Monad m, MonadIO m) => ProgramT Motor' m a -> m a
serialNewportT = eval <=< viewT
where
eval :: (Monad m, MonadIO m) => ProgramViewT Motor' m v -> m v
eval (Return x) = return x
eval (x :>>= k) = f x >>= serialNewportT . k
f :: (Monad m, MonadIO m) => Motor' a -> m a
f = undefined -- excluded for brevity
就目前而言,该代码目前可以进行类型检查。但是,我希望能够切换解释器处理不同电机的方式,所以我想将 f
作为参数,而不是将其硬编码。我尝试使用以下代码进行此切换
{-# LANGUAGE GADTs #-}
import Control.Monad ((<=<))
import Control.Monad.Operational
data Motor' a where
--Define GADT
serialNewportT :: (Monad m, MonadIO m) => (Motor' a -> m a) -> ProgramT Motor' m a -> m a
serialNewportT f = eval <=< viewT
where
eval :: (Monad m, MonadIO m) => ProgramViewT Motor' m v -> m v
eval (Return x) = return x
eval (x :>>= k) = f x >>= serialNewportT f . k
但是,此代码失败并显示错误消息
Couldn't match type ‘b’ with ‘c’
‘b’ is a rigid type variable bound by
a pattern with constructor
:>>= :: forall (instr :: * -> *) (m :: * -> *) a b.
instr b -> (b -> ProgramT instr m a) -> ProgramViewT instr m a,
in an equation for ‘eval’
‘c’ is a rigid type variable bound by
the type signature for
serialNewportT :: (Monad m, MonadIO m) =>
(Motor' c -> m c) -> ProgramT Motor' m a -> m a
Expected type: Motor' c
Actual type: Motor' b
因为我只是将全局名称替换为相同类型的本地名称,所以我认为它应该可以顺利运行。如何让类型与 f
统一作为参数?
你没有正确翻译 f 的类型。在原代码中,f
的类型是forall a . (..) => Motor' a -> m a
。它可以用类型 Motor' a
实例化为 any a
,但在非工作代码中你声明它的类型完全相同 a
与 ProgramT Motor' m a
中一样,而在函数体中,您在 Motor' b
上调用 f
用于其他一些(存在量化的)类型 b
。
你只需要更高级别的类型:
serialNewportT :: (Monad m, MonadIO m) => (forall x . Motor' x -> m x) -> ProgramT Motor' m a -> m a
我有以下使用操作包中的 ProgramT 的解释器
{-# LANGUAGE GADTs #-}
import Control.Monad ((<=<))
import Control.Monad.Operational
data Motor' a where
--Define GADT
serialNewportT :: (Monad m, MonadIO m) => ProgramT Motor' m a -> m a
serialNewportT = eval <=< viewT
where
eval :: (Monad m, MonadIO m) => ProgramViewT Motor' m v -> m v
eval (Return x) = return x
eval (x :>>= k) = f x >>= serialNewportT . k
f :: (Monad m, MonadIO m) => Motor' a -> m a
f = undefined -- excluded for brevity
就目前而言,该代码目前可以进行类型检查。但是,我希望能够切换解释器处理不同电机的方式,所以我想将 f
作为参数,而不是将其硬编码。我尝试使用以下代码进行此切换
{-# LANGUAGE GADTs #-}
import Control.Monad ((<=<))
import Control.Monad.Operational
data Motor' a where
--Define GADT
serialNewportT :: (Monad m, MonadIO m) => (Motor' a -> m a) -> ProgramT Motor' m a -> m a
serialNewportT f = eval <=< viewT
where
eval :: (Monad m, MonadIO m) => ProgramViewT Motor' m v -> m v
eval (Return x) = return x
eval (x :>>= k) = f x >>= serialNewportT f . k
但是,此代码失败并显示错误消息
Couldn't match type ‘b’ with ‘c’
‘b’ is a rigid type variable bound by
a pattern with constructor
:>>= :: forall (instr :: * -> *) (m :: * -> *) a b.
instr b -> (b -> ProgramT instr m a) -> ProgramViewT instr m a,
in an equation for ‘eval’
‘c’ is a rigid type variable bound by
the type signature for
serialNewportT :: (Monad m, MonadIO m) =>
(Motor' c -> m c) -> ProgramT Motor' m a -> m a
Expected type: Motor' c
Actual type: Motor' b
因为我只是将全局名称替换为相同类型的本地名称,所以我认为它应该可以顺利运行。如何让类型与 f
统一作为参数?
你没有正确翻译 f 的类型。在原代码中,f
的类型是forall a . (..) => Motor' a -> m a
。它可以用类型 Motor' a
实例化为 any a
,但在非工作代码中你声明它的类型完全相同 a
与 ProgramT Motor' m a
中一样,而在函数体中,您在 Motor' b
上调用 f
用于其他一些(存在量化的)类型 b
。
你只需要更高级别的类型:
serialNewportT :: (Monad m, MonadIO m) => (forall x . Motor' x -> m x) -> ProgramT Motor' m a -> m a