指定嵌套类型变量约束在 Haskell 中具有相同的实例
Specify nested type variable constraints are of same instance in Haskell
以下明确使用 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
与 f
的第一个参数中的类型类 Foo
属于同一个实例?
在类型定义中
newtype FooParser a = FooParser { (#) :: Foo b => b -> (a, b) }
类型变量b
普遍绑定在更高级别的类型中;即新引入的选择器函数(#)
的类型是
*Foo> :t (#)
(#) :: Foo b => FooParser a -> b -> (a, b)
但这意味着当您构造一个 FooParser a
时,您传递给构造函数的函数必须在 b -> (a, b)
处输入 any 选择 b
(只要 Foo b
成立):
*Foo> :t FooParser
FooParser :: (forall b. Foo b => b -> (a, b)) -> FooParser a
然而,在 queryP
中,你得到了一个 b -> a
类型的函数,用于 some 选择 b
(b
由调用者选择,唯一的限制是 Foo b
将保留)。
因此,如果我设置 b ~ b1
并调用 queryP
,这意味着我向您传递了一个 f :: b1 -> a
类型的函数。然后你必须 return 给我一个 forall b. (Foo b) => b -> (a, b)
类型的函数(包装在构造函数 FooParser
中)。
你不能仅仅为了这个目的而使用f
,因为除了b1
(例如b ~ b2
)之外的b
的任何选择都不会是正确的类型。
在目前的形式中,您基本上只能使用 Foo
class 中的函数和参数化足够多态的函数来构造您的 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) }
请注意,这两个变化意味着(和暗示)非常不同的事情。
以下明确使用 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
与 f
的第一个参数中的类型类 Foo
属于同一个实例?
在类型定义中
newtype FooParser a = FooParser { (#) :: Foo b => b -> (a, b) }
类型变量b
普遍绑定在更高级别的类型中;即新引入的选择器函数(#)
的类型是
*Foo> :t (#)
(#) :: Foo b => FooParser a -> b -> (a, b)
但这意味着当您构造一个 FooParser a
时,您传递给构造函数的函数必须在 b -> (a, b)
处输入 any 选择 b
(只要 Foo b
成立):
*Foo> :t FooParser
FooParser :: (forall b. Foo b => b -> (a, b)) -> FooParser a
然而,在 queryP
中,你得到了一个 b -> a
类型的函数,用于 some 选择 b
(b
由调用者选择,唯一的限制是 Foo b
将保留)。
因此,如果我设置 b ~ b1
并调用 queryP
,这意味着我向您传递了一个 f :: b1 -> a
类型的函数。然后你必须 return 给我一个 forall b. (Foo b) => b -> (a, b)
类型的函数(包装在构造函数 FooParser
中)。
你不能仅仅为了这个目的而使用f
,因为除了b1
(例如b ~ b2
)之外的b
的任何选择都不会是正确的类型。
在目前的形式中,您基本上只能使用 Foo
class 中的函数和参数化足够多态的函数来构造您的 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) }
请注意,这两个变化意味着(和暗示)非常不同的事情。