为内部的辅助函数添加类型签名
Add type signature for a helper function inside
首先我定义如下数据类型
data Supply s a = S (Stream s -> (a, Stream s))
data Stream a = Cons a (Stream a)
然后我想实现一个映射到具有以下类型签名的 Supply
的函数:
mapSupply :: (a -> b) -> Supply s a -> Supply s b
这是我的实现:(编译没有问题)
mapSupply :: (a -> b) -> Supply s a -> Supply s b
mapSupply mapFunc (S supFuncA) = S supFuncB where
supFuncB strm = let (la, strms) = supFuncA strm in
((mapFunc la), strms)
然后当我试图写下我在 mapSupply
中定义的名为 supFuncB
的辅助函数的类型签名时遇到了问题。
supFuncB
的类型签名非常简单,应该是:
supFuncB :: Stream s -> (b, Stream s)
然而,当我尝试在代码中添加类型签名时,出现编译错误。代码看起来像这样
mapSupply :: (a -> b) -> Supply s a -> Supply s b
mapSupply mapFunc (S supFuncA) = S supFuncB where
supFuncB :: Stream s -> (b, Stream s)
supFuncB strm = let (la, strms) = supFuncA strm in
((mapFunc la), strms)
然后编译器报错:
• Couldn't match type ‘s1’ with ‘s’
‘s1’ is a rigid type variable bound by
the type signature for:
supFuncB :: forall s1 b1. Stream s1 -> (b1, Stream s1)
at Main.hs:58:5-41
‘s’ is a rigid type variable bound by
the type signature for:
mapSupply :: forall a b s. (a -> b) -> Supply s a -> Supply s b
at Main.hs:56:1-49
Expected type: Stream s1
Actual type: Stream s
• In the expression: strms
In the expression: ((mapFunc la), strms)
In the expression:
let (la, strms) = supFuncA strm in ((mapFunc la), strms)
我是 Haskell 的新手,我不明白为什么编译会失败?如果我要在代码中添加它,正确的类型签名应该是什么。
从头开始,解决方案是打开 ScopedTypeVariables
并在 mapSupply
签名中使用显式 forall
,如下所示:
{-# LANGUAGE ScopedTypeVariables #-} -- Put this at the top of your file.
mapSupply :: forall a b s. (a -> b) -> Supply s a -> Supply s b
mapSupply mapFunc (S supFuncA) = S supFuncB where
supFuncB :: Stream s -> (b, Stream s)
supFuncB strm = let (la, strms) = supFuncA strm in
((mapFunc la), strms)
下面是对为什么需要这样做的解释。
当你这样写签名时:
mapSupply :: (a -> b) -> Supply s a -> Supply s b
GHC 实际上看到了这个:
mapSupply :: forall a b s. (a -> b) -> Supply s a -> Supply s b
forall
通常可以隐式表示 a
、b
和 s
可以是任何东西——mapSupply
是一个polymorphic 函数,所以使用它的人可以自由地为三个类型变量选择任何具体类型。明确编写 forall
s,您的第二个定义将如下所示:
mapSupply :: forall a b s. (a -> b) -> Supply s a -> Supply s b
mapSupply mapFunc (S supFuncA) = S supFuncB where
supFuncB :: forall s b. Stream s -> (b, Stream s)
supFuncB strm = let (la, strms) = supFuncA strm in
((mapFunc la), strms)
根据它,mapSupply
中的a
、b
和s
可以是任何东西,s
和[=20也是如此=] 在 supFuncB
。不过,这是一个问题。例如,定义涉及 strms
,其类型为 s
... 除了 s
,因为您使用的是 supFuncA
,它显示为 不是 来自 supFuncB
签名的那个,而是来自 mapSupply
签名的那个。尽管 mapSupply
中的 s
原则上可以是任何东西,正如我之前提到的,一旦您使用 mapSupply
实际选择了 s
,s
from supFuncB
必须匹配它。既然如此,supFuncB
签名中的 forall
就不合适了,因为它的类型变量实际上不能是任何东西。如果我们重命名来自 supFuncB
的类型变量,这样它们的名称就不会与来自 mapSupply
的变量名称冲突,就更容易看出(鉴于 forall
,这应该是一个有效的举措那个):
mapSupply :: forall a b s. (a -> b) -> Supply s a -> Supply s b
mapSupply mapFunc (S supFuncA) = S supFuncB where
supFuncB :: forall s1 b1. Stream s1 -> (b1, Stream s1)
supFuncB strm = let (la, strms) = supFuncA strm in
((mapFunc la), strms)
(GHC 在内部执行此操作,这解释了为什么您提到的错误消息是 s1
类型变量。)
这个问题只是因为添加到 supFuncB
的签名而发生的,它引入了一个隐含的 forall
。没有签名,GHC 通过不概括 supFuncB
中的类型来做你想做的事——在这种情况下,它不是多态的,而是 monomorphic in the type variables a
、b
和s
,借自mapSupply
。 ScopedTypeVariables
扩展可以在为 supFuncB
编写类型签名时恢复该行为。启用后,对签名中的类型变量使用显式 forall
将使相应定义中具有相同名称的任何类型变量引用相同的事物(只要它们不在 forall
在他们自己的签名中)。换句话说,通过这样做,可以在相应定义范围内的任何地方引用来自外部签名的变量,这证明了扩展名的合理性。
首先我定义如下数据类型
data Supply s a = S (Stream s -> (a, Stream s))
data Stream a = Cons a (Stream a)
然后我想实现一个映射到具有以下类型签名的 Supply
的函数:
mapSupply :: (a -> b) -> Supply s a -> Supply s b
这是我的实现:(编译没有问题)
mapSupply :: (a -> b) -> Supply s a -> Supply s b
mapSupply mapFunc (S supFuncA) = S supFuncB where
supFuncB strm = let (la, strms) = supFuncA strm in
((mapFunc la), strms)
然后当我试图写下我在 mapSupply
中定义的名为 supFuncB
的辅助函数的类型签名时遇到了问题。
supFuncB
的类型签名非常简单,应该是:
supFuncB :: Stream s -> (b, Stream s)
然而,当我尝试在代码中添加类型签名时,出现编译错误。代码看起来像这样
mapSupply :: (a -> b) -> Supply s a -> Supply s b
mapSupply mapFunc (S supFuncA) = S supFuncB where
supFuncB :: Stream s -> (b, Stream s)
supFuncB strm = let (la, strms) = supFuncA strm in
((mapFunc la), strms)
然后编译器报错:
• Couldn't match type ‘s1’ with ‘s’
‘s1’ is a rigid type variable bound by
the type signature for:
supFuncB :: forall s1 b1. Stream s1 -> (b1, Stream s1)
at Main.hs:58:5-41
‘s’ is a rigid type variable bound by
the type signature for:
mapSupply :: forall a b s. (a -> b) -> Supply s a -> Supply s b
at Main.hs:56:1-49
Expected type: Stream s1
Actual type: Stream s
• In the expression: strms
In the expression: ((mapFunc la), strms)
In the expression:
let (la, strms) = supFuncA strm in ((mapFunc la), strms)
我是 Haskell 的新手,我不明白为什么编译会失败?如果我要在代码中添加它,正确的类型签名应该是什么。
从头开始,解决方案是打开 ScopedTypeVariables
并在 mapSupply
签名中使用显式 forall
,如下所示:
{-# LANGUAGE ScopedTypeVariables #-} -- Put this at the top of your file.
mapSupply :: forall a b s. (a -> b) -> Supply s a -> Supply s b
mapSupply mapFunc (S supFuncA) = S supFuncB where
supFuncB :: Stream s -> (b, Stream s)
supFuncB strm = let (la, strms) = supFuncA strm in
((mapFunc la), strms)
下面是对为什么需要这样做的解释。
当你这样写签名时:
mapSupply :: (a -> b) -> Supply s a -> Supply s b
GHC 实际上看到了这个:
mapSupply :: forall a b s. (a -> b) -> Supply s a -> Supply s b
forall
通常可以隐式表示 a
、b
和 s
可以是任何东西——mapSupply
是一个polymorphic 函数,所以使用它的人可以自由地为三个类型变量选择任何具体类型。明确编写 forall
s,您的第二个定义将如下所示:
mapSupply :: forall a b s. (a -> b) -> Supply s a -> Supply s b
mapSupply mapFunc (S supFuncA) = S supFuncB where
supFuncB :: forall s b. Stream s -> (b, Stream s)
supFuncB strm = let (la, strms) = supFuncA strm in
((mapFunc la), strms)
根据它,mapSupply
中的a
、b
和s
可以是任何东西,s
和[=20也是如此=] 在 supFuncB
。不过,这是一个问题。例如,定义涉及 strms
,其类型为 s
... 除了 s
,因为您使用的是 supFuncA
,它显示为 不是 来自 supFuncB
签名的那个,而是来自 mapSupply
签名的那个。尽管 mapSupply
中的 s
原则上可以是任何东西,正如我之前提到的,一旦您使用 mapSupply
实际选择了 s
,s
from supFuncB
必须匹配它。既然如此,supFuncB
签名中的 forall
就不合适了,因为它的类型变量实际上不能是任何东西。如果我们重命名来自 supFuncB
的类型变量,这样它们的名称就不会与来自 mapSupply
的变量名称冲突,就更容易看出(鉴于 forall
,这应该是一个有效的举措那个):
mapSupply :: forall a b s. (a -> b) -> Supply s a -> Supply s b
mapSupply mapFunc (S supFuncA) = S supFuncB where
supFuncB :: forall s1 b1. Stream s1 -> (b1, Stream s1)
supFuncB strm = let (la, strms) = supFuncA strm in
((mapFunc la), strms)
(GHC 在内部执行此操作,这解释了为什么您提到的错误消息是 s1
类型变量。)
这个问题只是因为添加到 supFuncB
的签名而发生的,它引入了一个隐含的 forall
。没有签名,GHC 通过不概括 supFuncB
中的类型来做你想做的事——在这种情况下,它不是多态的,而是 monomorphic in the type variables a
、b
和s
,借自mapSupply
。 ScopedTypeVariables
扩展可以在为 supFuncB
编写类型签名时恢复该行为。启用后,对签名中的类型变量使用显式 forall
将使相应定义中具有相同名称的任何类型变量引用相同的事物(只要它们不在 forall
在他们自己的签名中)。换句话说,通过这样做,可以在相应定义范围内的任何地方引用来自外部签名的变量,这证明了扩展名的合理性。