Haskell 将类型族实例转换为Int
我有以下代码:Haskell 将类型族实例转换为Int,haskell,type-families,Haskell,Type Families,我有以下代码: 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 ->
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
你不能。您需要
testFunc::(IdToInt a)=>ida->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
但是,您仍然需要上下文。您不能使用类型为
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
。请记住,重载方法是根据类型选择的。两个类实例都定义了类型为Int->Int
的idToInt
函数,因此类型检查器无法决定使用哪一个
您应该使用数据族而不是类型族,并声明新类型实例
data family Id a :: *
newtype instance Id () = IdUnit Int
newtype instance Id Char = IdChar Int
对于newtype实例,
Id()
和Id Char
都是int,但它们有不同的类型。Id的类型通知类型检查器要使用哪个重载函数。正如其他人所指出的,问题是编译器无法确定要使用哪个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
s和Id a
s之间是否存在一对一的对应关系?如果是这样,数据族是正确的方法。如果没有,您可能更喜欢证人。谢谢。我还是不明白。我根据你在问题中的回答发布了我的结果,以便正确格式化。@taotree:哦,那是因为你使用的是类型同义词族,而不是数据类型族。这个特定的问题实际上可能是一个bug,但一般来说,类型同义词族是毫无用处的;由于两个实例完全有可能具有相同的关联类型,因此GHC几乎放弃了推导任何内容,最终导致混乱。使用数据类型族将解决所有这些问题。