我有这段代码:
type family Id obj :: *
type instance Id Box = Int
我想这样做,所以我总能从Id类型系列中获得一个Int。我知道需要转换。
我认为创建课程可能会有效:
class IdToInt a where
idToInt :: Id a -> Int
instance IdToInt Box where
idToInt s = s
这实际上是编译的。但是当我尝试使用它时:
testFunc :: Id a -> Int
testFunc x = idToInt x
我收到错误:
src/Snowfall/Spatial.hs:29:22:
Couldn't match type `Id a0' with `Id a'
NB: `Id' is a type function, and may not be injective
In the first argument of `idToInt', namely `x'
In the expression: idToInt x
In an equation for `testFunc': testFunc x = idToInt x
那么,如何为类型系列ID创建转换以获取Int?
基于ehird的回答,我尝试了以下但是它也不起作用:
class IdStuff a where
type Id a :: *
idToInt :: Id a -> Int
instance IdStuff Box where
type Id Box = Int
idToInt s = s
testFunc :: (IdStuff a) => Id a -> Int
testFunc x = idToInt x
它给出错误:
src/Snowfall/Spatial.hs:45:22:
Could not deduce (Id a0 ~ Id a)
from the context (IdStuff a)
bound by the type signature for
testFunc :: IdStuff a => Id a -> Int
at src/Snowfall/Spatial.hs:45:1-22
NB: `Id' is a type function, and may not be injective
In the first argument of `idToInt', namely `x'
In the expression: idToInt x
In an equation for `testFunc': testFunc x = idToInt x
答案 0 :(得分:3)
你做不到。您需要testFunc :: (IdToInt a) => Id a -> Int
。类型系列是开放的,所以任何人都可以声明
type instance Id Blah = ()
随时提供,并且不提供转换功能。最好的办法是将类型族放在类中:
class HasId a where
type Id a
idToInt :: Id a -> Int
instance IdToInt Box where
type Id Box = Int
idToInt s = s
但是你仍然需要上下文。
答案 1 :(得分:3)
您无法使用IdToInt a => Id a -> Int
类型的函数,因为无法确定a
的类型。以下示例演示了这一点。
type family Id a :: *
type instance Id () = Int
type instance Id Char = Int
class IdToInt a where idToInt :: Id a -> Int
instance IdToInt () where idToInt x = x + 1
instance IdToInt Char where idToInt x = x - 1
main = print $ idToInt 1
由于Id () = Id Char = Int
,上述背景中idToInt
的类型为Int -> Int
,等于Id () -> Int
和Id Char -> Int
。请记住,根据类型选择重载方法。两个类实例都定义了类型为idToInt
的{{1}}函数,因此类型检查器无法决定使用哪一个。
您应该使用数据系列而不是类型系列,并声明newtype实例。
Int -> Int
对于newtype实例,data family Id a :: *
newtype instance Id () = IdUnit Int
newtype instance Id Char = IdChar Int
和Id ()
都是整数,但它们有不同的类型。 Id Char
的类型通知类型检查器使用哪个重载函数。
答案 2 :(得分:2)
正如其他人所指出的那样,问题在于编译器无法确定使用哪个a
。数据系列是一种解决方案,但有时更容易使用的替代方法是使用类型见证。
将班级更改为
class IdToInt a where
idToInt :: a -> Id a -> Int
instance IdToInt Box where
idToInt _ s = s
-- if you use this a lot, it's sometimes useful to create type witnesses to use
box = undefined :: Box
-- you can use it like
idToInt box someId
-- or
idToInt someBox (getId someBox)
您需要回答的问题是,对于任何给定的Id
,是否只应出现一种类型a
?也就是说,a
和Id a
之间是否存在一对一的对应关系?如果是这样,数据系列是正确的方法。如果没有,您可能更喜欢见证。