指定嵌套类型变量约束在 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 定义 FooParserqueryP 时:

{-# 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 函数中的 bf 的第一个参数中的类型类 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 选择 bb 由调用者选择,唯一的限制是 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) }

请注意,这两个变化意味着(和暗示)非常不同的事情。