为了学习如何编写更通用的代码,我试着写 一个简单的Array类。我们的想法是只编写一些简单的数组操作 使用Array类提供的函数,然后将不同的数据类型写为实例。 所以我想写出不同的数组表示。
对于第一次尝试,数组具有多个行和列以及可折叠的容器。
module Array where
import qualified Data.Vector as V
import Data.Foldable
class Array arr where
aRows :: arr a -> Int
aCols :: arr a -> Int
aData :: Foldable t => arr a -> t a
data VectorArray a = VectorArray
{ vRows :: !Int
, vCols :: !Int
, vData :: !(V.Vector a)}
instance Array VectorArray where
aRows = vRows
aCols = vCols
aData = vData
最后一行无法编译:
• Couldn't match type ‘t’ with ‘V.Vector’
‘t’ is a rigid type variable bound by
the type signature for:
aData :: forall (t :: * -> *) a. Foldable t => VectorArray a -> t a
at src/Array.hs:19:5
Expected type: VectorArray a -> t a
Actual type: VectorArray a -> V.Vector a
• In the expression: vData
我对错误信息的解释:你(程序员)给我一个V.Vector
a但我(GHC编译器)
想要t a
t
,其中aData
必须是可折叠类的实例。
现在,V.Vector是Foldable的一个实例。所以在我必须传递实例的每个函数中 Foldable 作为参数,我可以使用 V.Vector 类型的值。 我的问题是:为什么GHC没有“向上翻转” V.Vector 到可折叠?有没有例子,在哪里 这样的上调会成为问题的根源吗?
PS:避免上述错误的真正好方法是从Array类中删除train.test
函数
并使VectorArray成为可折叠的实例。
答案 0 :(得分:8)
aData
的类型表示对于调用者选择的任何t
,它将返回类型为t a
的值。但实现只返回Vector
。 "上溯造型"如你所说,它需要将Vector
转换为任何Foldable
结构,但不仅没有隐含的方法来执行此操作,实际上也是不可能的:如果向量为空且用户想要一个NonEmpty
列表?
答案 1 :(得分:3)
值不是类型类的实例。 Int
不是Num
; VectorArray Foo
不是Array
。 类型是类型类的实例: Int
是Num
, VectorArray
是Array
, Vector
是Foldable
。您无法将某些x :: Vector Foo
转发为x :: Foldable
,因为Foldable
不适用于值。 Haskell确实有子类型(例如mempty :: forall m. Monoid m => m
,但是因为Group m
(如果存在这样的类)也暗示Monoid m
,mempty :: forall g. Group g => g
,但它有点晦涩难懂并没有相关性。
你想要一个存在类型:
class Array arr where
aRows :: arr a -> Int
aCols :: arr a -> Int
aData :: arr a -> (exists f. Foldable f => f a)
语法exists f. Foldable f => f a
是(有点标准)psuedo-Haskell。这意味着,无论它是什么,它都是Foldable
,它包含a
,但你不知道包含类型到底是什么。你不能直接这样做,但你可以近距离接触:
data SomeFoldable a = forall f. Foldable f => SomeFoldable (f a)
-- Can't be a newtype: the Foldable f dictionary needs to be put somewhere
aData :: Array arr => arr a -> SomeFoldable a
存在类型的Haskell语法相当不正确,因为它使用相反的量词forall
来定义它们。理由是上面创建了构造函数
SomeFoldable :: forall f. Foldable f => f a -> SomeFoldable a
"忘记" f
具体是什么。
使用这些非常简单,但不如仅仅Foldable
成为Array
的超类一样好:
instance Array VectorArray where
aRows = vRows
aCols = vCols
aData = SomeFoldable . vData
sum :: (Num a, Array arr) => arr a -> a
sum arr = case aData arr of
SomeFoldable fa -> foldl' (+) 0 fa
-- Need to use patterns to destructure; let won't work (same for GADTs).
-- Foldable instance in scope on right side of case
-- Example:
instance Array [] where
aRows _ = 1
aCols = length
aData = SomeFoldable
sum [1,2,3] == 6