为什么函数不能采用仅受类型类约束的类型?
Why can't a function take a type constrained only by a typeclass?
我没有足够的词汇来表达这个问题(因此搜索答案,如果答案很容易获得,我深表歉意)。考虑以下
class RunFoo m where
runFoo :: m a -> a
class RunFooWrapper m where
doRunFoo :: (RunFoo n) => n a -> m a
newtype RunFast a = RunFast a
newtype RunSlow a = RunSlow a
fooExample :: (RunFoo m) => m Bool
fooExample = undefined
fooWrapperExample :: (RunFooWrapper m) => m Bool
fooWrapperExample = doRunFoo fooExample
这不编译:Could not deduce (RunFoo n0) arising from a use of ‘doRunFoo’
。
编译器 (GHC 7.10) 似乎坚持要从 fooExample
对 m
进行具体实例化,因此拒绝继续。但在这种情况下,我不明白为什么程序类型错误 - fooExample
明确定义了一个 RunFoo m
而所有 doRunFoo
都需要一个 RunFoo x
。那为什么这行不通呢?
作为一个补充问题,是否有某种特殊的扩展(可能与存在类型有关)允许这样的程序?理想情况下,我希望能够说 doRunFoo 接受任何定义为 RunFoo m => m
的存在性定义(?)(而不是接受 RunFoo
的任何具体实例)。
动机
我想创建可组合函数,在受类型约束的类型的某些方面运行class - 如有必要,我可以提供一个具体示例来说明我的意思!
编辑更有动力和替代实施
我很好奇这个问题在一般情况下的答案,但我想我会补充说 运行 在这个问题上我真正想要的是一种约束委托单子之间。所以我想在一个 monad 中编写函数,该 monad 仅受一个类型 class 的约束,它调用另一个类型 class 中的 monad。顶层函数在不同的上下文中可以是 运行,执行相同的逻辑,但根据包装 monad 换出底层实现。由于包装和实现 monad 之间存在一对一的对应关系,所以我能够使用类型族来做到这一点,所以
class (RunFoo (RunFooM m)) => RunFooWrapper m where
type RunFooM m :: * -> *
doRunFoo :: RunFooM m a -> m a
instance RunFooWrapper RunFooWrapperSlow where
type RunFooM RunFooWrapperSlow = RunSlow
doRunFoo :: [...]
这意味着 fooExample
m
的分辨率由包装器 monad 的 class 上下文决定,似乎工作正常,但它是一个非常狭窄的解决方案与haoformayor提供的相比。
RankNTypes
{-# language RankNTypes #-}
class RunFoo m where
runFoo :: m a -> a
class RunFooWrapper m where
doRunFoo :: (forall n. RunFoo n => n a) -> m a
fooExample :: RunFoo m => m Bool
fooExample = undefined
fooWrapperExample :: RunFooWrapper m => m Bool
fooWrapperExample = doRunFoo fooExample
(forall n. RunFoo n => n a) -> m a
是关键区别。它允许您传入 fooExample
,其类型为 forall m. RunFoo m => m Bool
(forall
由编译器隐式添加),因此 m
与 n
统一每个人都很开心。虽然我不会读心术,但我相信这种类型反映了你的真实意图。您只需要一个 RunFoo
实例,仅此而已,并且将 n
范围限定为第一个参数即可提供它。
问题是您给定的代码被隐式键入为 forall n. RunFoo n => n a -> m a
。这意味着您需要首先 选择一个 n
使得 RunFoo n
然后想出一个类型为 n a
的值作为第一个传入争论。这个简单的移动括号的动作(增加 n
的排名)完全改变了意思。
有关遇到相同问题的人的示例,请参阅 . For a better explanation of RankNTypes
than I could provide, see Oliver's blog post on it。
我没有足够的词汇来表达这个问题(因此搜索答案,如果答案很容易获得,我深表歉意)。考虑以下
class RunFoo m where
runFoo :: m a -> a
class RunFooWrapper m where
doRunFoo :: (RunFoo n) => n a -> m a
newtype RunFast a = RunFast a
newtype RunSlow a = RunSlow a
fooExample :: (RunFoo m) => m Bool
fooExample = undefined
fooWrapperExample :: (RunFooWrapper m) => m Bool
fooWrapperExample = doRunFoo fooExample
这不编译:Could not deduce (RunFoo n0) arising from a use of ‘doRunFoo’
。
编译器 (GHC 7.10) 似乎坚持要从 fooExample
对 m
进行具体实例化,因此拒绝继续。但在这种情况下,我不明白为什么程序类型错误 - fooExample
明确定义了一个 RunFoo m
而所有 doRunFoo
都需要一个 RunFoo x
。那为什么这行不通呢?
作为一个补充问题,是否有某种特殊的扩展(可能与存在类型有关)允许这样的程序?理想情况下,我希望能够说 doRunFoo 接受任何定义为 RunFoo m => m
的存在性定义(?)(而不是接受 RunFoo
的任何具体实例)。
动机
我想创建可组合函数,在受类型约束的类型的某些方面运行class - 如有必要,我可以提供一个具体示例来说明我的意思!
编辑更有动力和替代实施
我很好奇这个问题在一般情况下的答案,但我想我会补充说 运行 在这个问题上我真正想要的是一种约束委托单子之间。所以我想在一个 monad 中编写函数,该 monad 仅受一个类型 class 的约束,它调用另一个类型 class 中的 monad。顶层函数在不同的上下文中可以是 运行,执行相同的逻辑,但根据包装 monad 换出底层实现。由于包装和实现 monad 之间存在一对一的对应关系,所以我能够使用类型族来做到这一点,所以
class (RunFoo (RunFooM m)) => RunFooWrapper m where
type RunFooM m :: * -> *
doRunFoo :: RunFooM m a -> m a
instance RunFooWrapper RunFooWrapperSlow where
type RunFooM RunFooWrapperSlow = RunSlow
doRunFoo :: [...]
这意味着 fooExample
m
的分辨率由包装器 monad 的 class 上下文决定,似乎工作正常,但它是一个非常狭窄的解决方案与haoformayor提供的相比。
RankNTypes
{-# language RankNTypes #-}
class RunFoo m where
runFoo :: m a -> a
class RunFooWrapper m where
doRunFoo :: (forall n. RunFoo n => n a) -> m a
fooExample :: RunFoo m => m Bool
fooExample = undefined
fooWrapperExample :: RunFooWrapper m => m Bool
fooWrapperExample = doRunFoo fooExample
(forall n. RunFoo n => n a) -> m a
是关键区别。它允许您传入 fooExample
,其类型为 forall m. RunFoo m => m Bool
(forall
由编译器隐式添加),因此 m
与 n
统一每个人都很开心。虽然我不会读心术,但我相信这种类型反映了你的真实意图。您只需要一个 RunFoo
实例,仅此而已,并且将 n
范围限定为第一个参数即可提供它。
问题是您给定的代码被隐式键入为 forall n. RunFoo n => n a -> m a
。这意味着您需要首先 选择一个 n
使得 RunFoo n
然后想出一个类型为 n a
的值作为第一个传入争论。这个简单的移动括号的动作(增加 n
的排名)完全改变了意思。
有关遇到相同问题的人的示例,请参阅 RankNTypes
than I could provide, see Oliver's blog post on it。