在下面的片段中(我已经抽象了所有其他琐碎的部分)
data T s = T (s -> s)
foo :: T s -> s -> s
foo (T f) x = bar x where
bar :: s -> s
bar a = f a
我收到了以下错误
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
{/ 1}} {1}}实际上意味着相同的类型。
然后我找到了这个页面Scoped type variables,这表明我可以使用{-# LANGUAGE ScopedTypeVariables #-}
,这对我们没有帮助。我得到了同样的错误。
答案 0 :(得分:7)
“where”子句中的类型变量与父项在同一名称空间中吗?
没有*。如果您在false
方面考虑foo :: s -> s
,这会更容易一些。毕竟,类型变量表明该函数适用于任何类型foo :: forall 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
。但forall s.
中的那个是错误的。毕竟,您不能在那里选择任何 bar
,而是在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
,请参阅答案的后面部分。
答案 1 :(得分:5)
ScopedTypeVariables
确实是解决方案,但是还需要使用它们:您必须在类型签名中添加显式forall
来声明要扩展的变量,如下所示:
foo :: forall s. T s -> s -> s
这样,代码中签名的含义不取决于是否启用了扩展名。