为什么一个函数只能使用类型类约束的类型?

时间:2016-09-13 21:00:57

标签: haskell constraints ghc 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

这不会编译:{{1​​}}。

似乎编译器(GHC 7.10)坚持从Could not deduce (RunFoo n0) arising from a use of ‘doRunFoo’开始m的具体实例化,因此拒绝继续。但在这种情况下,我无法理解为什么程序输入错误 - fooExample明确定义了fooExample,所有RunFoo m要求都是doRunFoo。那么为什么这不起作用呢?

作为一个补充问题,是否存在某种特殊的扩展(可能与存在类型有关),允许这样的程序?理想情况下,我希望能够说doRunFoo采用存在的任何定义(?)作为RunFoo x(而不是采用RunFoo m => m的任何具体实例)。

动机

我想创建可操作于类型类约束类型某些方面的可组合函数 - 我可以提供一个具体的例子,说明我的意思,如果有必要的话!

使用更多动力和替代实施进行编辑

在一般情况下,我对这个问题的答案感到好奇,但我想我在这个问题的上下文中添加了我真正想要的是monad之间的一种约束委托。所以我想在一个monad中编写函数,只能通过在另一个类型类中调用monad的类型类来约束。然后可以在不同的上下文中运行顶级函数,执行相同的逻辑,但根据包装monad将底层实现换出。由于包装和实现monad之间存在一对一的对应关系,因此我能够使用类型族来实现此目的,所以

RunFoo

这意味着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的分辨率由包装器monad的类上下文决定,并且似乎工作正常,但与haoformayor提供的解决方案相比,这是一个非常狭窄的解决方案

1 个答案:

答案 0 :(得分:10)

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),因此mn结合使用每个人都很开心虽然我无法读懂思想,但我相信这种类型反映了你的真实意图。您只需要一个RunFoo实例,仅有n作用于第一个参数提供它。

问题是您的给定代码被隐式输入为forall n. RunFoo n => n a -> m a。这意味着您需要首先选择n RunFoo n ,然后提出类型为n a的值作为第一个参数传入。移动括号的这种简单行为(增加n的等级)完全改变了含义。

有关遇到同样问题的人的示例,请参阅this Stack Overflow question。有关RankNTypes的更好解释,请参阅Oliver's blog post on it