为什么函数不能采用仅受类型类约束的类型?

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) 似乎坚持要从 fooExamplem 进行具体实例化,因此拒绝继续。但在这种情况下,我不明白为什么程序类型错误 - 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 Boolforall 由编译器隐式添加),因此 mn 统一每个人都很开心。虽然我不会读心术,但我相信这种类型反映了你的真实意图。您只需要一个 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