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
。