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

Sal*_*Sal 3 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
Run Code Online (Sandbox Code Playgroud)

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.
Run Code Online (Sandbox Code Playgroud)

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

ehi*_*ird 5

类型签名

dispatch :: (Storable a) => AList -> SV.Vector a
Run Code Online (Sandbox Code Playgroud)

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

dispatch :: forall a. (Storable a) => AList -> SV.Vector a
Run Code Online (Sandbox Code Playgroud)

你真的想要说什么是"给我一个AList和I'l给你一个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
Run Code Online (Sandbox Code Playgroud)

(你需要{-# LANGUAGE ExistentialQuantification #-}这个.)

这给出SomeVVector了类型:

SomeVVector :: (Storable a) => SV.Vector a -> SomeVVector
Run Code Online (Sandbox Code Playgroud)

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

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


当你在评论中提到的,如果你不介意的AListInt32S和一个AListCChar小号有不同的类型,你可以使用一个GADT:

data 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
Run Code Online (Sandbox Code Playgroud)

这里,AList本质上是一个SV.Vector仅适用于某些元素类型的版本.然而,dispatch实际上并没有那么有用 - AList在你拿出它之后没有真正的方法可以"回到"中dispatch,因为类型统一GADT模式匹配提供只适用于显式模式匹配; 你不能说结果dispatch是一个SV.Vector Int32或一个SV.Vector CChar.就像是

dispatch :: (SV.Vector Int32 -> r) -> (SV.Vector CChar -> r) -> AList a -> r
Run Code Online (Sandbox Code Playgroud)

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

我认为没有合理的方法来定义有用的东西dispatch; 我建议在原始AList定义中使用显式模式匹配.