将类型族实例转换为Int

时间:2012-03-14 15:40:43

标签: 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 -> 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

3 个答案:

答案 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 () -> IntId 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?也就是说,aId a之间是否存在一对一的对应关系?如果是这样,数据系列是正确的方法。如果没有,您可能更喜欢见证。