以下内容显式使用 FooA
作为 (#)
和 queryP
中的类型,按预期进行编译:
{-# LANGUAGE RankNTypes, ScopedTypeVariables #-}
module Foo where
class Foo a where
newtype FooParser a = FooParser { (#) :: FooA -> (a, FooA) }
queryP :: (FooA -> a) -> FooParser a
queryP f = FooParser $ \(b :: FooA) -> (f b, b)
data FooA = FooA Int
instance Foo FooA where
但是当我尝试使用类型类 Foo
定义 FooParser
和 queryP
时,如下所示:
{-# LANGUAGE RankNTypes, ScopedTypeVariables #-}
module Foo where
class Foo a where
newtype FooParser a = FooParser { (#) :: Foo b => b -> (a, b) }
queryP :: Foo b => (b -> a) -> FooParser a
queryP f = FooParser $ \(b :: Foo b => b) -> (f b, b)
我收到一个无法推断的错误:
Foo.hs:11:52:
Could not deduce (b ~ b1)
from the context (Foo b)
bound by the type signature for
queryP :: Foo b => (b -> a) -> FooParser a
at Foo.hs:10:11-42
or from (Foo b1)
bound by a type expected by the context: Foo b1 => b1 -> (a, b1)
at Foo.hs:11:12-53
‘b’ is a rigid type variable bound by
the type signature for queryP :: Foo b => (b -> a) -> FooParser a
at Foo.hs:10:11
‘b1’ is a rigid type variable bound by
a type expected by the context: Foo b1 => b1 -> (a, b1)
at Foo.hs:11:12
Relevant bindings include
b :: Foo b => b (bound at Foo.hs:11:26)
f :: b -> a (bound at Foo.hs:11:8)
queryP :: (b -> a) -> FooParser a (bound at Foo.hs:11:1)
In the expression: b
In the expression: (f b, b)
如何指定 queryP
中 lambda 函数中的 b
与第一个中的类型类 Foo
的实例相同f
的参数?
最佳答案
在类型定义中
newtype FooParser a = FooParser { (#) :: Foo b => b -> (a, b) }
类型变量 b
普遍绑定(bind)在更高等级的类型中;即新引入的选择器函数的类型(#)
是
*Foo> :t (#)
(#) :: Foo b => FooParser a -> b -> (a, b)
但这意味着当你构造 FooParser a
时,传递给构造函数的函数必须键入 b -> (a, b)
对于 b
的任意选择(只要 Foo b
成立):
*Foo> :t FooParser
FooParser :: (forall b. Foo b => b -> (a, b)) -> FooParser a
但是,在 queryP
给你一个 b -> a
类型的函数对于b
的一些选择(b
由调用者选择,唯一的限制是 Foo b
将保留)。
所以如果我设置b ~ b1
并调用queryP
,这意味着我正在向您传递一个 f :: b1 -> a
类型的函数。然后你必须返回给我一个 forall b. (Foo b) => b -> (a, b)
类型的函数。 (包装在构造函数 FooParser
中)。
你不能只使用f
为此,对于 b
的任何选择除了 b1
(例如 b ~ b2
)它的类型不正确。
在目前的形式下,您基本上只能使用 Foo
中的函数。类和参数充分的多态函数来构造您的 forall b. (Foo b) => b -> (a, b)
功能。根据您想要执行的操作,您可以
- 更改
queryP
这样它就需要一个多态函数:
queryP :: (forall b. Foo b => (b -> a)) -> FooParser a
queryP f = FooParser $ \b -> (f b, b)
- 更改
FooParser
这样b
是存在的约束:
{-# LANGUAGE ExistentialQuantification #-}
data FooParser a = forall b. Foo b => FooParser { (#) :: b -> (a, b) }
请注意,这两个更改意味着(并且暗示)非常不同的事情。
关于haskell - 指定嵌套类型变量约束在 Haskell 中属于同一实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31799691/