【问题标题】:Typeclass method which returns another typeclass in Haskell在 Haskell 中返回另一个类型类的类型类方法
【发布时间】:2018-11-27 05:13:51
【问题描述】:

我有这个任务,我必须在 Haskell 中定义 2 个类型类。一个很简单——它实现了 Ord 并且有一种方法可以将给定的类型转换为 int。像这样的:

class (Ord a) => Id a where
  toInt :: a -> Int

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

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

我收到一个我不知道如何解决的错误。

• 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     |             

谁能告诉我如何在其他人的 typeclass 方法中返回 typeclass? 我应该首先实现一些 Id 类型类的实例吗?

【问题讨论】:

    标签: haskell typeclass


    【解决方案1】:

    您将返回值声明为 HKT Id a,但您想要的是另一种类型,受类型类 Id 约束。

    你需要的不是getId :: a -> Id a,而是getId :: Id b => a -> b

    【讨论】:

    • 这可能typecheck,但我怀疑它是否真的工作。问题中给出的Id 类使得getId :: Id b => a -> b 基本上无法实现。
    【解决方案2】:

    了解 Haskell class 与 OO 语言中的类非常不同是很重要的。它是一个类型类,即它“将类型的集合组合在一起”,而 OO 类将值的集合组合在一起。 IOW,OO 类一种类型(包含值),但 Haskell 类更像是一种类型类型

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

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

    length :: [a] -> Int
    

    它接受一个列表并且不关心其元素的类型。但是你说你确实关心一下使用了什么类型,也就是说,你要求它们在类中。这是一个约束,它是用=> 表示法编写的。 toInt其实有签名(类声明自动生成)

    toInt :: Id a => a -> Int
    

    坦率地说,我怀疑这真的是你想要的。如果您对Id 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.
    

    那么,你有更明智的类型

    toInt :: Id' a -> Int
    

    不需要任何约束。

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

    getId 方法也是一个受约束的多态函数,它可以获取“具有Id”的内容并返回该 ID。在这种情况下,一个类是有意义的(因为你可以有不同的数据结构,可能以不同的方式存储它们的 ID)。这实际上是

    class HasId a where
      getId :: a -> Id' a
    

    根据Welperooni's answer,确实也可以使用您原来的Id 类定义HasId,只是它并没有多大意义。 Id可以用作标识符的所有类型的类。您可以表示 getId 可以产生任何此类类型的 Id:

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

    请注意,result-Id 类型现在完全独立于您要计算其 Id 的对象的类型。 b 始终可以是 any Id 类型。正如我所说,这没有意义:为什么您需要一大堆不同的类型来表示 Id,但需要所有 HasId 类型来支持所有这些

    事实上,这无法按现状实现,因为没有机制允许您生成任意Id 类型的Id。这将需要一个额外的方法

    class Id a where
      toInt :: a -> Int
      fromInt :: Int -> a
    

    现在你可以做

    class HasId a where
      getId :: Id b => a -> b
    
    data SomeObj { objName :: String
                 , objId :: Int }
    
    instance HasId SomeObj where
      getId (SomeObj _ i) = fromInt i
    

    但是,如果您要求任何Id 类型可以从Int 转换为Int,那么这些类型必须Int 本身同构。因此,newtype Id' a 方法几乎肯定更好。

    【讨论】:

    • 感谢您的扩展回答!非常感谢您的帮助。我实际上需要它明天的测试所以再次非常感谢你:)
    猜你喜欢
    • 2015-09-07
    • 2012-02-27
    • 1970-01-01
    • 2021-12-25
    • 1970-01-01
    • 1970-01-01
    • 2013-05-09
    • 1970-01-01
    • 2011-01-13
    相关资源
    最近更新 更多