Typeclass方法,它在Haskell中返回另一个类型类

izo*_*top 0 haskell typeclass

我有这个任务,我必须在Haskell中定义2个类型类.一个很简单 - 它实现Ord并且有一个方法将给定类型转换为int.像这样的东西:

class (Ord a) => Id a where
  toInt :: a -> Int
Run Code Online (Sandbox Code Playgroud)

但是我有另一个类型类HasId,它有一个方法getId.此方法应返回Id类型类.所以我写了这样的东西:

class HasId a where
  getId :: a -> Id a 
Run Code Online (Sandbox Code Playgroud)

我得到一个错误,我不知道如何解决.

• Expected kind ‘* -> Constraint’, but ‘Int’ has kind ‘*’
• In the class declaration for ‘HasId’
 |
 | class (Int a) => HasId a where     |        ^^^^^

• Expected a type, but ‘Id a’ has kind ‘Constraint’
• In the type signature: getId :: a -> Id a
  In the class declaration for ‘HasId’
 |
 |  getId :: a -> Id a     |             
Run Code Online (Sandbox Code Playgroud)

有人可以告诉我如何在另一个类型类方法中返回类型类?我应该首先实现一些Id类型类的实例吗?

lef*_*out 7

重要的是要理解Haskell class与OO语言中的类非常不同.它是一个类型类,即它"将一组类型组合在一起",而一个OO类将一组值组合在一起.IOW,OO类是一个类型(包含值),但Haskell类更像是一种类型,就像它一样.

现在,函数/方法总是将值作为参数,并将值作为结果返回.但它不能给你一个类型,也不能给你一个"类型类的价值",因为没有这样的东西.

相反,使用类型类的方法是:您编写一个多态函数,即接受或产生某些未指定类型值的函数.这就是类型变量的用途.通常,多态签名意味着该函数可以处理任何类型的值,例如

length :: [a] -> Int
Run Code Online (Sandbox Code Playgroud)

它采用列表而不关心其元素的类型.但是你说你有点关心使用什么类型,即你需要他们在课堂上.这是一个约束,它是用=>符号写的.toInt实际上有签名(由类声明自动生成)

toInt :: Id a => a -> Int
Run Code Online (Sandbox Code Playgroud)

坦率地说,我怀疑这真的是你想要的.如果您只能Id a将其转换为a Int,那么结果实际上是一个Int(只是带有一个类型注释,指示此Id所属的对象类型),而不管a参数如何.因此Id,您应该考虑使用类型而不是类Id

newtype Id' a = Id {toInt :: Int}  -- The prime ' symbol has no particular meaning,
                                   -- I just use it for disambiguation.
Run Code Online (Sandbox Code Playgroud)

然后,你有更明智的类型

toInt :: Id' a -> Int
Run Code Online (Sandbox Code Playgroud)

不需要任何约束.

newtype 更类似于OO类,因为它实际上定义了一个具有可以传递的值的具体类型.

该getId方法也是一个约束多态函数,可以采取"有Id"的东西,并返回该Id.在这种情况下,一个类是有意义的(因为你可以有不同的数据结构,可以以不同的方式存储他们的ID).这实际上是

class HasId a where
  getId :: a -> Id' a
Run Code Online (Sandbox Code Playgroud)

根据Welperooni的回答,HasId使用原始Id类定义确实也是可以转换的,只是它没有多大意义.Id是可以用作识别符的所有类型的类.您可以表示getId可以产生任何此类型的Id:

class HasId a where
  getId :: Id b => a -> b
Run Code Online (Sandbox Code Playgroud)

请注意,result-Id类型现在完全独立于要计算其Id的对象类型.b可以永远是任何 Id类型.正如我所说,这没有意义:为什么你需要很多代表Ids的不同类型,但需要所有HasId类型来支持它们?

事实上,这不能实现,因为没有机制允许您生成任意Id类型的Id .这需要一个额外的方法

class Id a where
  toInt :: a -> Int
  fromInt :: Int -> a
Run Code Online (Sandbox Code Playgroud)

现在你可以做到

class HasId a where
  getId :: Id b => a -> b

data SomeObj { objName :: String
             , objId :: Int }

instance HasId SomeObj where
  getId (SomeObj _ i) = fromInt i
Run Code Online (Sandbox Code Playgroud)

但是,如果您要求任何Id类型都可以转换为和转换Int,则这些类型必须与其Int自身同构.因此,这种newtype Id' a方法几乎肯定更好.