ViewPatterns 以不可预测的方式影响类型检查
ViewPatterns affects typechecking in an unpredictable manner
考虑以下代码片段:
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Vector.Unboxed as UV
import qualified Data.Vector.Generic as V
bar :: Int -> UV.Vector Char -> (Text, Text)
bar i v = (t_pre, t_post)
where
f = T.pack . V.toList
(t_pre, t_post) = (\(x, y) -> (f x, f y)) $ V.splitAt i v
如您所料,编译正常。但是,如果将函数调用替换为视图模式,则会出现类型错误。
{-# LANGUAGE ViewPatterns #-}
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Vector.Unboxed as UV
import qualified Data.Vector.Generic as V
bar :: Int -> UV.Vector Char -> (Text, Text)
bar i v = (t_pre, t_post)
where
f = T.pack . V.toList
(f -> t_pre, f -> t_post) = V.splitAt i v
这将打印以下消息(-fprint-potential-instances
):
• Ambiguous type variable ‘v0’ arising from a use of ‘V.toList’
prevents the constraint ‘(V.Vector v0 Char)’ from being solved.
Relevant bindings include
f :: v0 Char -> Text (bound at Weird.hs:11:5)
Probable fix: use a type annotation to specify what ‘v0’ should be.
These potential instances exist:
instance V.Vector UV.Vector Char
-- Defined in ‘Data.Vector.Unboxed.Base’
...plus one instance involving out-of-scope types
instance primitive-0.6.3.0:Data.Primitive.Types.Prim a =>
V.Vector Data.Vector.Primitive.Vector a
-- Defined in ‘Data.Vector.Primitive’
• In the second argument of ‘(.)’, namely ‘V.toList’
In the expression: T.pack . V.toList
In an equation for ‘f’: f = T.pack . V.toList
|
11 | f = T.pack . V.toList
| ^^^^^^^^
Weird.hs:13:6: error:
Variable not in scope: f :: UV.Vector Char -> t
|
13 | (f -> t_pre, f -> t_post) = V.splitAt i v
| ^
Weird.hs:13:18: error:
Variable not in scope: f :: UV.Vector Char -> t1
|
13 | (f -> t_pre, f -> t_post) = V.splitAt i v
| ^
我的理解是这两种表达方式是完全等价的,因为视图模式只是函数应用,没有命名绑定变量。我误解了视图模式吗?是不是以一种意想不到的方式与类型检查器交互的脱糖?如果我在两个调用站点内联 f
的定义,类型错误就会消失。
我已经用 GHCi 8.4.3 测试过了。
更新:这是一个编译器错误。有关详细信息,请参阅 GHC Trac #14293。
这确实很奇怪。这可能是一个错误。
修改原代码如下
where
f x = T.pack (V.toList x)
(f -> t_pre, f -> t_post) = V.splitAt i v
让 GHC 请求 FlexibleContexts
。这样做之后,我们得到一个非常奇怪的错误:
Variable not in scope: f :: UV.Vector Char -> t
|
12 | (f -> t_pre, f -> t_post) = V.splitAt i v
| ^
Variable not in scope: f :: UV.Vector Char -> t1
|
12 | (f -> t_pre, f -> t_post) = V.splitAt i v
| ^
这对我来说似乎是一个错误。 f
应该在范围内。
将 f
移动到全局范围:
...
where
(f -> t_pre, f -> t_post) = V.splitAt i v
f x = T.pack (V.toList x)
代码现在可以正常工作了。如果我们将全局 f
恢复为 pointfree 定义,它甚至可以工作。
使用显式类型注释,如
where
f :: UV.Vector Char -> Text
f x = T.pack (V.toList x)
(f -> t_pre, f -> t_post) = V.splitAt i v
产生令人费解的错误消息
• Variable not in scope: f :: UV.Vector Char -> t
• Perhaps you meant ‘f’ (line 12)
我不明白到底发生了什么。在 GHCi 中,这两个都工作正常
> let f = id ; foo (f -> x) = x in foo ()
()
> let bar = foo () where {f = id ; foo (f -> x) = x} in bar
()
因此,我们可以在视图模式中使用本地 f
。尽管如此,当 f
的类型需要一些更仔细的类型推断(?)时,它就不能在视图模式中使用。这看起来像一个错误。最起码报错信息应该更清楚了。
你的 f
有问题,这是由单态限制引起的。如果你 eta 扩展 f
,给它一个类型签名,或者打开 NoMonomorphismRestriction
,那么这个错误就会消失。
但是你仍然有这些错误,这让我很吃惊!
Weird.hs:13:6: error:
Variable not in scope: f :: UV.Vector Char -> t
|
13 | (f -> t_pre, f -> t_post) = V.splitAt i v
| ^
Weird.hs:13:18: error:
Variable not in scope: f :: UV.Vector Char -> t1
|
13 | (f -> t_pre, f -> t_post) = V.splitAt i v
| ^
我想如果在同一范围内定义视图模式,它们将不起作用。为了查看视图模式是否需要是顶级的,我尝试了
bar :: Int -> UV.Vector Char -> (Text, Text)
bar i v = let (f -> t_pre, f -> t_post) = V.splitAt i v in (t_pre, t_post)
where
f = T.pack . V.toList
效果很好。所以我尝试了
f = T.pack . V.toList
(f -> t_pre, f -> t_post) = V.splitAt 0 UV.empty
失败,f
不在范围内。
最后,如果我将这些模式放在函数调用下
f = T.pack . V.toList
g (f -> t_pre, f -> t_post) = V.splitAt 0 UV.empty
然后又好了。所以我猜规则是 "value" 模式绑定不能使用在同一范围内定义的视图模式。我觉得很奇怪,它甚至可能是一个错误。
考虑以下代码片段:
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Vector.Unboxed as UV
import qualified Data.Vector.Generic as V
bar :: Int -> UV.Vector Char -> (Text, Text)
bar i v = (t_pre, t_post)
where
f = T.pack . V.toList
(t_pre, t_post) = (\(x, y) -> (f x, f y)) $ V.splitAt i v
如您所料,编译正常。但是,如果将函数调用替换为视图模式,则会出现类型错误。
{-# LANGUAGE ViewPatterns #-}
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Vector.Unboxed as UV
import qualified Data.Vector.Generic as V
bar :: Int -> UV.Vector Char -> (Text, Text)
bar i v = (t_pre, t_post)
where
f = T.pack . V.toList
(f -> t_pre, f -> t_post) = V.splitAt i v
这将打印以下消息(-fprint-potential-instances
):
• Ambiguous type variable ‘v0’ arising from a use of ‘V.toList’
prevents the constraint ‘(V.Vector v0 Char)’ from being solved.
Relevant bindings include
f :: v0 Char -> Text (bound at Weird.hs:11:5)
Probable fix: use a type annotation to specify what ‘v0’ should be.
These potential instances exist:
instance V.Vector UV.Vector Char
-- Defined in ‘Data.Vector.Unboxed.Base’
...plus one instance involving out-of-scope types
instance primitive-0.6.3.0:Data.Primitive.Types.Prim a =>
V.Vector Data.Vector.Primitive.Vector a
-- Defined in ‘Data.Vector.Primitive’
• In the second argument of ‘(.)’, namely ‘V.toList’
In the expression: T.pack . V.toList
In an equation for ‘f’: f = T.pack . V.toList
|
11 | f = T.pack . V.toList
| ^^^^^^^^
Weird.hs:13:6: error:
Variable not in scope: f :: UV.Vector Char -> t
|
13 | (f -> t_pre, f -> t_post) = V.splitAt i v
| ^
Weird.hs:13:18: error:
Variable not in scope: f :: UV.Vector Char -> t1
|
13 | (f -> t_pre, f -> t_post) = V.splitAt i v
| ^
我的理解是这两种表达方式是完全等价的,因为视图模式只是函数应用,没有命名绑定变量。我误解了视图模式吗?是不是以一种意想不到的方式与类型检查器交互的脱糖?如果我在两个调用站点内联 f
的定义,类型错误就会消失。
我已经用 GHCi 8.4.3 测试过了。
更新:这是一个编译器错误。有关详细信息,请参阅 GHC Trac #14293。
这确实很奇怪。这可能是一个错误。
修改原代码如下
where
f x = T.pack (V.toList x)
(f -> t_pre, f -> t_post) = V.splitAt i v
让 GHC 请求 FlexibleContexts
。这样做之后,我们得到一个非常奇怪的错误:
Variable not in scope: f :: UV.Vector Char -> t
|
12 | (f -> t_pre, f -> t_post) = V.splitAt i v
| ^
Variable not in scope: f :: UV.Vector Char -> t1
|
12 | (f -> t_pre, f -> t_post) = V.splitAt i v
| ^
这对我来说似乎是一个错误。 f
应该在范围内。
将 f
移动到全局范围:
...
where
(f -> t_pre, f -> t_post) = V.splitAt i v
f x = T.pack (V.toList x)
代码现在可以正常工作了。如果我们将全局 f
恢复为 pointfree 定义,它甚至可以工作。
使用显式类型注释,如
where
f :: UV.Vector Char -> Text
f x = T.pack (V.toList x)
(f -> t_pre, f -> t_post) = V.splitAt i v
产生令人费解的错误消息
• Variable not in scope: f :: UV.Vector Char -> t
• Perhaps you meant ‘f’ (line 12)
我不明白到底发生了什么。在 GHCi 中,这两个都工作正常
> let f = id ; foo (f -> x) = x in foo ()
()
> let bar = foo () where {f = id ; foo (f -> x) = x} in bar
()
因此,我们可以在视图模式中使用本地 f
。尽管如此,当 f
的类型需要一些更仔细的类型推断(?)时,它就不能在视图模式中使用。这看起来像一个错误。最起码报错信息应该更清楚了。
你的 f
有问题,这是由单态限制引起的。如果你 eta 扩展 f
,给它一个类型签名,或者打开 NoMonomorphismRestriction
,那么这个错误就会消失。
但是你仍然有这些错误,这让我很吃惊!
Weird.hs:13:6: error:
Variable not in scope: f :: UV.Vector Char -> t
|
13 | (f -> t_pre, f -> t_post) = V.splitAt i v
| ^
Weird.hs:13:18: error:
Variable not in scope: f :: UV.Vector Char -> t1
|
13 | (f -> t_pre, f -> t_post) = V.splitAt i v
| ^
我想如果在同一范围内定义视图模式,它们将不起作用。为了查看视图模式是否需要是顶级的,我尝试了
bar :: Int -> UV.Vector Char -> (Text, Text)
bar i v = let (f -> t_pre, f -> t_post) = V.splitAt i v in (t_pre, t_post)
where
f = T.pack . V.toList
效果很好。所以我尝试了
f = T.pack . V.toList
(f -> t_pre, f -> t_post) = V.splitAt 0 UV.empty
失败,f
不在范围内。
最后,如果我将这些模式放在函数调用下
f = T.pack . V.toList
g (f -> t_pre, f -> t_post) = V.splitAt 0 UV.empty
然后又好了。所以我猜规则是 "value" 模式绑定不能使用在同一范围内定义的视图模式。我觉得很奇怪,它甚至可能是一个错误。