为类型匹配定义Storable的受限子集 [英] Defining restricted subset of Storable for type match
问题描述
以下代码出现错误 - 我怀疑它与 dispatch
函数的类型签名有关,该函数返回一个 Vector
类型
可存储a
。更新 dispatch
函数类型签名以仅执行 Int32
和 CChar
在签名类型中:
{ - #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匹配类型`CChar'与`Int32'
期望类型:向量a
实际类型:向量Int32
在表达式中:x
在`dispatch'等式中:dispatch (I x)= x
失败,模块加载:无。
假设我的错误诊断正确,我在问这个问题。如果我的诊断不正确,我会很欣赏如何解决上述错误的指示。
dispatch ::(Storable a)=> AList - > SV.Vector a
表示给我一个 AList
,我给你一个 SV.Vector a
用于任何 a
只要它是可存储
的实例即可。那是不对的!对于任何给定的值,只有一个 a
您可以提供,您选择它,而不是调用代码。如果您明确添加量词,问题可能会更容易看出:
dispatch :: forall a。 (可存储的a)=> AList - > SV.Vector a
你真正想说的是给我一个 AList
,我给你一个 SV.Vector a
用于一些 a
,但我保证它会是 Storable
的实例。为此,我们需要一个存在类型:
data SomeSVVector = forall a。 (可存储的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.Vector
出于派发
与 SomeSVVector vec的case dispatch x的结果 - > ......
。然而,这可能并不是那么有用:因为存在可以包含一个带有任何实例 Storable
元素的向量,所以只有操作可以在内部数据上执行,这些操作是由 Storable
类提供的。如果你想要用户代码可以分析和分派类型的东西,你需要一个标记联合 - 这正是你的 AList
类型已经是
如果您确实想要使用存在路线,那么我建议将您自己的类型类定义为 Storable
,其中包含您可能想要对其中的值执行的所有操作。至少,您可能希望将 Integral
添加到 SomeSVVector
的约束中。
正如你在评论中提到的,如果你不介意 AList
的 Int32
s和一个 AList
的 CChar
s具有不同的类型,你可以使用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
在这里, 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
会起作用,但这相当于您的原始标记上的模式匹配(并且比它更尴尬) union版本。
我认为没有合理的方法来定义有用的 dispatch
;我建议在你原来的 AList
定义上使用显式模式匹配。
I am getting error for the code below - I suspect it has to do with the type signature of dispatch
function which returns a Vector
of type Storable a
. What is the simple way to update dispatch
function type signature to do only Int32
and CChar
in type signature:
{-# 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
Error in 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.
I am asking this question assuming my error diagnosis is correct. If my diagnosis is incorrect, I will appreciate pointers on how to fix the error above.
The type signature
dispatch :: (Storable a) => AList -> SV.Vector a
says "give me an AList
, and I'll give you an SV.Vector a
for any a
you want, so long as it's an instance of Storable
". That's not right! For any given value, there's only one a
you can supply, and you choose it, not the calling code. The problem might be easier to see if you explicitly add the quantifier:
dispatch :: forall a. (Storable a) => AList -> SV.Vector a
What you really want to say is "give me an AList
, and I'l give you an SV.Vector a
for some a
that I choose, but I promise it'll be an instance of Storable
". For this, we need an existential type:
data SomeSVVector = forall a. (Storable a) => SomeSVVector (SV.Vector a)
dispatch :: AList -> SomeSVVector
dispatch (I x) = SomeSVVector x
dispatch (S x) = SomeSVVector x
(You'll need {-# LANGUAGE ExistentialQuantification #-}
for this.)
This gives SomeVVector
the type:
SomeVVector :: (Storable a) => SV.Vector a -> SomeVVector
You can then take the SV.Vector
out of the result of dispatch
with case dispatch x of SomeSVVector vec -> ...
. However, this probably isn't all that useful: since the existential can contain a vector with elements of any instance of Storable
, the only operations you'll be able to perform on the data inside are those offered by the Storable
class. If you want something that user code can analyse and "dispatch" on the type of, you'll need a tagged union — which is exactly what your AList
type already is.
If you do want to go down the existential route, then I would suggest defining your own typeclass as a subclass of Storable
that contains all the operations you might want to perform on the values inside. At the very least, you'll probably want to add Integral
to SomeSVVector
's constraint.
As you mentioned in the comments, if you don't mind an AList
of Int32
s and an AList
of CChar
s having different types, you can use a 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
Here, AList
is essentially a version of SV.Vector
that only works on certain element types. However, dispatch
isn't really that useful — there's no real way to "get back in" to an AList
after you take it out with dispatch
, because the type unification GADT pattern-matching offers only works with explicit pattern-matching; you can't tell that the result of dispatch
is either an SV.Vector Int32
or an SV.Vector CChar
. Something like
dispatch :: (SV.Vector Int32 -> r) -> (SV.Vector CChar -> r) -> AList a -> r
would work, but that's equivalent to (and more awkward than) pattern-matching on your original tagged union version.
I don't think there's a reasonable way to define a useful dispatch
; I would suggest using explicit pattern-matching on your original AList
definition instead.
这篇关于为类型匹配定义Storable的受限子集的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!