为类型匹配定义Storable的受限子集

时间:2012-02-07 14:48:33

标签: haskell

我收到以下代码的错误 - 我怀疑它与dispatch函数的类型签名有关,它返回Vector类型Storable a。更新dispatch函数类型签名以在类型签名中仅执行Int32CChar的简单方法是什么:

{-#  LANGUAGE BangPatterns #-}
import Data.Vector.Storable as SV
import Foreign.C.Types (CChar)
import GHC.Int (Int32)

data AList = I {-# UNPACK #-} !(SV.Vector Int32)
              | S {-# UNPACK #-} !(SV.Vector CChar)


dispatch :: (Storable a) => AList -> SV.Vector a
dispatch (I x) = x
dispatch (S x) = x

ghci 7.4.1中的错误:

    test.hs:11:18:
    Couldn't match type `CChar' with `Int32'
    Expected type: Vector a
      Actual type: Vector Int32
    In the expression: x
    In an equation for `dispatch': dispatch (I x) = x
Failed, modules loaded: none.

假设我的错误诊断正确,我问这个问题。如果我的诊断不正确,我将非常感谢如何解决上述错误。

1 个答案:

答案 0 :(得分:5)

类型签名

dispatch :: (Storable a) => AList -> SV.Vector a

说“给我一个AList,我会给你一个SV.Vector a 任何 a你想要的,只要它是一个实例Storable”。那是不对的!对于任何给定的值,您只能提供一个 a选择它,而不是调用代码。如果您明确添加量词,则可能更容易看到问题:

dispatch :: forall a. (Storable a) => AList -> SV.Vector a

你真正想说的是“给我一个AList,我会给你一个SV.Vector a 我选择的 a,但我保证它会成为Storable的一个实例。为此,我们需要存在类型:

data SomeSVVector = forall a. (Storable a) => SomeSVVector (SV.Vector a)

dispatch :: AList -> SomeSVVector
dispatch (I x) = SomeSVVector x
dispatch (S x) = SomeSVVector x

(你需要{-# LANGUAGE ExistentialQuantification #-}。)

这会给SomeVVector类型:

SomeVVector :: (Storable a) => SV.Vector a -> SomeVVector

然后,您可以使用SV.Vectordispatch的结果中取出case dispatch x of SomeSVVector vec -> ...。但是,这可能不是那么有用:因为存在主义可以包含一个带有任何 Storable实例元素的向量,所以操作能够对内部数据执行的是Storable类提供的数据。如果你想要用户代码可以分析并“分派”类型的东西,你需要一个标记的联合 - 这正是你的AList类型已经存在的。

如果您确实想要沿着存在路径前进,那么我建议您将自己的类型类定义为Storable的子类,其中包含您可能希望对内部值执行的所有操作。至少,您可能希望将Integral添加到SomeSVVector的约束中。


正如您在评论中提到的,如果您不介意AList Int32AList CChardata AList a where I :: {-# UNPACK #-} !(SV.Vector Int32) -> AList Int32 S :: {-# UNPACK #-} !(SV.Vector CChar) -> AList CChar dispatch :: AList a -> SV.Vector a dispatch (I x) = x dispatch (S x) = x 具有不同的类型,您可以使用一个GADT:

AList

此处,SV.Vector本质上是dispatch的一个版本,仅适用于某些元素类型。但是,AList并没有那么有用 - 在用dispatch取出dispatch之后,没有真正的方法可以“回到”SV.Vector Int32,因为类型统一GADT模式 - 匹配商品仅适用于显式模式匹配;您无法确定SV.Vector CChar的结果是dispatch :: (SV.Vector Int32 -> r) -> (SV.Vector CChar -> r) -> AList a -> r 还是dispatch。像

这样的东西
AList

会起作用,但这相当于原始标记联合版本上的模式匹配(并且比其更尴尬)。

我认为没有合理的方法来定义有用的{{1}};我建议您在原始{{1}}定义中使用显式模式匹配。