Haskell:类型变量是"其中"与父母在同一命名空间中的子句?

时间:2015-10-11 15:20:13

标签: haskell functional-programming

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

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 #-},这对我们没有帮助。我得到了同样的错误。

2 个答案:

答案 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 :: sidType x一词的类型为s -> s,而asTypeOf强制两者具有相同的类型。

根据您的实际代码,这样的事情或多或少可行。

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

答案 1 :(得分:5)

ScopedTypeVariables确实是解决方案,但是还需要使用它们:您必须在类型签名中添加显式forall来声明要扩展的变量,如下所示:

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

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