【问题标题】:Returning something from another type class B in function of type class A in Haskell在Haskell中类型类A的函数中从另一个类型类B返回一些东西
【发布时间】:2012-02-27 06:18:58
【问题描述】:

我正在做一个有趣的项目,我试图从 Java 中重做一些基本的数据类型和概念。目前我正在处理迭代器。

我的方法如下: (1) 将接口转换为类型类 (2) 为实际实现声明自定义数据类型和实例

所以我创建了以下类型类:

class Iterator it where
    next :: it e -> (it e, e)
    hasNext :: it e -> Bool

class Iterable i where
    iterator :: Iterator it => i e -> it e

class Iterable c => Collection c where
    add :: c e -> e -> c e

是的,我正在尝试翻译迭代器的概念(在这种情况下,它只是实际列表周围的一个框)。

这是我对一个简单列表的实现:

data LinkedList e = Element e (LinkedList e) | Nil
    deriving (Show, Eq)

instance Collection LinkedList where
    add Nil e = Element e Nil
    add (Element x xs) e = Element x $ add xs e

为了简化,我排除了其他函数,如删除、包含、addAll。

这是迭代器:

data LinkedListIterator e = It (LinkedList e)

instance Iterator LinkedListIterator where
    hasNext (It Nil) = False
    hasNext (It _) = True
    next (It (Element x xs)) = (It xs, x)

最后,Iterable LinkedList 的实例丢失了。我就是这样做的:

instance Iterable LinkedList where
    iterator list = It list

迭代器函数将列表包装成LinkedListIterator 并返回它。这里 GHC 声明一个错误:

Could not deduce (it ~ LinkedListIterator)
from the context (Iterator it)
  bound by the type signature for
             iterator :: Iterator it => LinkedList e -> it e

  `it' is a rigid type variable bound by
       the type signature for
         iterator :: Iterator it => LinkedList e -> it e

Expected type: it e
  Actual type: LinkedListIterator e

我不太明白。 LinkedListIterator 有一个 Iterator 的实例,那么为什么预期的类型“it e”与实际类型“LinkedListIterator e”不兼容(据我了解,一个 Iterator e) .波浪号 (~) 到底是什么意思?什么是刚性类型变量?

编辑:我将标题从 Translating Java Types into Haskell types: type deduction fail due to rigid type variable 更改为 Returning something from another type class B in function of type class A in Haskell 因为我相信我的实际问题与 return-something-of-type-class-B-from-type 有关-迭代器函数中的-class-A-issue。

解决方案:感谢答案,我现在将代码更改为以下版本。但是,我在阅读Typeclassopedia 时玩得很开心,只能推荐它。如前所述,应该学习 haskell 习语。

data Iterator c e = Next (Iterator c e, e) | Empty
    deriving (Show, Eq)

next :: Iterator c e -> (Iterator c e, e)
next (Next (i, e)) = (i, e)

hasNext :: Iterator c e -> Bool
hasNext Empty = False
hasNext _ = True

class Iterable i where
    iterator :: i e -> Iterator (i e) e

instance Iterable LinkedList where
    iterator Nil = Empty
    iterator (Element x xs) = Next (iterator xs, x)

【问题讨论】:

    标签: java haskell types type-constraints


    【解决方案1】:
    iterator :: Iterator it => i e -> it e
    

    这意味着调用者可以选择it作为他们想要的任何东西,只要它实现Iterator。另一种看待它的方式是,iterator 承诺适用于所有实现Iterator 的类型it

    无论调用者要求什么,您的实现都会提供LinkedListIterator

    编译器无法证明它们是同一事物(因为调用者可能需要不同的Iterator 实现),因此会发出错误。

    这与 Java 不同,Java 中调用者选择输入的类,而被调用者选择输出的类。在 Haskell 中,调用者选择输入输出的类型。

    ~ 表示类型相等。


    一些更广泛的观点。 (我很欣赏您尝试将 Java 习语翻译成 Haskell,但 imo 您需要学习 Haskell 习语。)

    1. 有时您不想返回实现类型类的值,而只想返回一个值。

      如果 Iterator 不是类型类,而是数据类型...

      data Iterator e = Iterator {next    :: (Iterator e, e),
                                  hasNext :: Bool}
      

      ...那么您可以只返回一个 Iterator 类型的值,而不必担心不同的类型类实现。

      惰性意味着迭代器中的连续值在被请求之前不会生成(并且不会抛出异常)。只要您不使用旧的迭代器值,这些值就可以在您迭代时被垃圾回收,因此我们仍然使用常量空间。

    2. Iterator 的更好定义是

      data Iterator e = Iterator {next :: Maybe (Iterator e, e)}
      

      这更好,因为它使您更难从迭代器中请求下一个值,而无需先检查是否有下一个值。

    3. 我对@9​​87654338@ 的第二个定义看起来有点像你对LinkedList 的定义,也像标准Haskell 列表(它们本身就是链表)的定义。实际上,在需要迭代时使用 Haskell 列表作为中间数据结构是惯用的。

    4. 阅读 Typeclassopedia 中的 FoldableTraversable 类型类。事实上,read the Typeclassopedia,它很好地介绍了一些听起来更吓人的类型类。

    【讨论】:

    • 好答案 - Iterator 类可以添加关联类型,因此 LinkedLists 将始终使用 LinkedListIterator(尽管这将我们很快带入高级领域)。
    • 这是另一种思考方式。 Java 的子类型多态对应于存在类型。如果我有一个返回 Iterator 类型的方法,我的意思是“这个函数可以返回一个 some 类型的值 T,它是 Iterator 的子类型。”在 Haskell 中,我有一个普遍量化的类型。 Iterator it => ... -> it 的意思是“这个函数可以返回一个 any 类型的值 t 这是Iterator 的一个实例。”
    猜你喜欢
    • 2018-11-27
    • 1970-01-01
    • 2016-09-25
    • 2015-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-25
    • 1970-01-01
    相关资源
    最近更新 更多