Haskell:类型变量在“中”;其中;与其父级在同一命名空间中的子句?

Haskell:类型变量在“中”;其中;与其父级在同一命名空间中的子句?,haskell,functional-programming,Haskell,Functional Programming,在下面的代码片段中(我已经抽象了所有其他琐碎的部分) 我犯了以下错误 Couldn't match expected type `s1' with actual type `s' `s1' is a rigid type variable bound by the type signature for bar :: s1 -> s1 at /tmp/test.hs:5:12 `s' is a rigid type variable bound by the

在下面的代码片段中(我已经抽象了所有其他琐碎的部分)

我犯了以下错误

Couldn't match expected type `s1' with actual type `s'
  `s1' is a rigid type variable bound by
       the type signature for bar :: s1 -> s1 at /tmp/test.hs:5:12
  `s' is a rigid type variable bound by
      the type signature for foo :: T s -> s -> s at /tmp/test.hs:3:8
In the return type of a call of `f'
In the expression: f a
In an equation for `bar': bar a = f a
我的猜测是
bar
签名中的类型变量与
foo
不共享名称空间,因此编译器无法推断这两个
s
实际上意味着相同的类型


然后我找到了这个页面,它建议我可以使用
{-#LANGUAGE ScopedTypeVariables}
,这没有帮助。我也遇到了同样的错误。

ScopedTypeVariables
确实是解决方案,但使用它们还有一个额外的要求:必须在声明要设置范围的变量的类型签名中为所有添加显式的
,如下所示:

foo :: forall s. T s -> s -> s

这样,代码中签名的含义就不取决于是否启用了扩展。

ScopedTypeVariables
确实是解决方案,但使用它们还有一个额外的要求:必须在声明要定义范围的变量的类型签名中为所有
添加显式的
,如下所示:

foo :: forall s. T s -> s -> s
这使得代码中签名的含义不取决于是否启用了扩展

“where”子句中的类型变量与其父级是否在同一命名空间中

否*。如果从
foo::forall s的角度考虑
foo::s->s
,这会变得容易一些。s->s
。毕竟,类型变量表示该函数适用于任何类型
s
。让我们向代码中添加显式量化:

{-# LANGUAGE ExplicitForAll #-}

data T s = T (s -> s)

foo :: forall s. T s -> s -> s
foo (T f) x = bar x where
    bar :: forall s. s -> s
    bar a = f a
如您所见,所有s.
都有两个
。但是
栏中的那一个是错误的。毕竟,您不能在那里选择任何
s
,而是选择已在
s
中使用的。这可以通过启用
ScopedTypeVariables
来实现:

{-# LANGUAGE ScopedTypeVariables #-}

data T s = T (s -> s)

--     vvvvvvvv  explicit here
foo :: forall s. T s -> s -> s
foo (T f) x = bar x where
    --     vvvvvv  same as above here
    bar :: s -> s
    bar a = f a
但是,有一些技巧可以去除
ScopedTypeVariables
。例如,在这种情况下,请执行以下操作:

data T s = T (s -> s)

foo :: T s -> s -> s
foo (T f) x = (bar `asTypeOf` idType x) x where

    bar a = f a

    idType :: a -> a -> a
    idType a _ = a

-- For completion, definition and type of 'asTypeOf'
-- asTypeOf :: a -> a -> a
-- asTypeOf x _ = x
对于任何
x::s
而言,术语
idType x
具有类型
s->s
,并且
asTypeOf
强制两者具有相同的类型

根据您的实际代码,类似的内容可能或多或少是可行的


*在这种情况下,由于可以使用
ScopedTypeVariables
,请参阅答案的后面部分

“where”子句中的类型变量与其父级是否在同一命名空间中

否*。如果从
foo::forall s的角度考虑
foo::s->s
,这会变得容易一些。s->s
。毕竟,类型变量表示该函数适用于任何类型
s
。让我们向代码中添加显式量化:

{-# LANGUAGE ExplicitForAll #-}

data T s = T (s -> s)

foo :: forall s. T s -> s -> s
foo (T f) x = bar x where
    bar :: forall s. s -> s
    bar a = f a
如您所见,所有s.
都有两个
。但是
栏中的那一个是错误的。毕竟,您不能在那里选择任何
s
,而是选择已在
s
中使用的。这可以通过启用
ScopedTypeVariables
来实现:

{-# LANGUAGE ScopedTypeVariables #-}

data T s = T (s -> s)

--     vvvvvvvv  explicit here
foo :: forall s. T s -> s -> s
foo (T f) x = bar x where
    --     vvvvvv  same as above here
    bar :: s -> s
    bar a = f a
但是,有一些技巧可以去除
ScopedTypeVariables
。例如,在这种情况下,请执行以下操作:

data T s = T (s -> s)

foo :: T s -> s -> s
foo (T f) x = (bar `asTypeOf` idType x) x where

    bar a = f a

    idType :: a -> a -> a
    idType a _ = a

-- For completion, definition and type of 'asTypeOf'
-- asTypeOf :: a -> a -> a
-- asTypeOf x _ = x
对于任何
x::s
而言,术语
idType x
具有类型
s->s
,并且
asTypeOf
强制两者具有相同的类型

根据您的实际代码,类似的内容可能或多或少是可行的



*好的,在这种情况下,由于可以使用
ScopedTypeVariables
,请参见答案的后面部分。

正如wiki(应该)上的示例所暗示的,您需要在
foo
的类型签名中为所有s
添加
才能将其与
ScopedTypeVariables
一起实际使用。正如wiki(应该)上的示例所暗示的,您需要在
foo
的类型签名中为所有的
添加一个
,才能将其实际用于
ScopedTypeVariables
。虽然可以使用
bar`typeOf`typeOf
,但我发现上面明确的
idType
定义更具可读性。我可能会将它定义为
idType\ua=a
,或者甚至定义为
idType=undefined
,但毕竟它的实际定义并不重要。我考虑过
idType=undefined
,但即使我们只是使用它的类型,在这种情况下,我也不会对部分函数感到舒服。但是是的,
idType=asTypeOf
。虽然可以使用
bar`typeOf`typeOf
,但我发现上面明确的
idType
定义更具可读性。我可能会将它定义为
idType\ua=a
,或者甚至定义为
idType=undefined
,但毕竟它的实际定义并不重要。我考虑过
idType=undefined
,但即使我们只是使用它的类型,在这种情况下,我也不会对部分函数感到舒服。但是是的,
idType=asTypeOf