感谢this question的回答,我已经定义了类似的类型:
data Chain = forall a. Integral a => Chain [[a]] [a] a a
如果你愿意,我需要为每个字段或参数编写一个getter函数。这是我的第一次尝试:
getSimplices (Chain simplices _ _ _) = simplices
但是当我尝试编译ghc时会出现以下错误:
Chain.hs:10:40: error:
• Couldn't match expected type ‘t’ with actual type ‘[[a]]’
because type variable ‘a’ would escape its scope
This (rigid, skolem) type variable is bound by
a pattern with constructor:
Chain :: forall a. Integral a => [[a]] -> [a] -> a -> a -> Chain,
in an equation for ‘getSimplices’
at Chain.hs:10:15-35
• In the expression: simplices
In an equation for ‘getSimplices’:
getSimplices (Chain simplices _ _ _) = simplices
• Relevant bindings include
simplices :: [[a]] (bound at Chain.hs:10:21)
getSimplices :: Chain -> t (bound at Chain.hs:10:1)
我这样修好了:
getSimplices (Chain simplices _ _ _) = map (map fromIntegral) simplices
即使某种ghc魔法可以防止这种情况对于吸气者来说非常慢,但我认为以这种方式修复某些东西只是非常残忍。有没有更好的方法来为这样的类型定义getter?
答案 0 :(得分:8)
存在类型的构造函数必然会忘记"关于a
:
Chain :: Integral a => [[a]] -> [[a]] -> a -> a -> Chain
请注意结果类型不再取决于a
。
这样做的结果是,当我们在Chain
上进行模式匹配时,我们无法对a
是什么做出任何假设。毕竟我们在建造时可以选择任何东西。我们对a
的唯一了解是它必须是一个整体类型。因此,我们只能使用Integral
类型的方法进行访问。
一般规则是,当我们在Chain
上进行模式匹配时,我们必须返回不依赖于a
的类型的值。像
getter (Chain _ _ x _) = x
违反了这条规则。毕竟,那会有什么类型?
getter :: Chain -> ???
当然不是
getter :: Chain -> a
这意味着我们可以从Chain
中提取任何我们希望的类型。这实际上是行不通的:我们无法自由选择要放入的类型,然后也可以自由选择要取出的类型。
然而,我们可以做的是利用Integral
:
getter :: Chain -> Integer
getter (Chain _ _ x _) = fromIntegral x
Integer
不依赖于a
,所以没关系。
答案 1 :(得分:7)
您认为getSimplices
应该具有哪种类型? “显而易见”的选择是
getSimplices :: Integral a => Chain -> [[a]]
但这不适用于您的实施,因为getSimplices
的来电者可以选择a
,并且无法保证Chain
中存储的值属于调用者想要的类型,因为你抛弃了那些信息。考虑:
let c = Chain [[1 :: Int]] [2] 3 4
in (getSimplices c) :: [[Integer]]
您的两种类型签名显然允许这样做,它们可以适用于任何Integral类型,但如果没有一些转换,显然也无法工作:Int
必须以某种方式转换为Integer
。
正如评论所说,这是不寻常的。将类型参数添加到Chain会更简单,以便它跟踪用于创建它的类型,并将其输出约束为该类型:
data Chain a = Chain [[a]] [a] a a
getSimplices :: Chain a -> [[a]]
getSimplices (Chain xs _ _ _) = xs