【问题标题】:Is there a workaround for the lack of type class backtracking?缺少类型类回溯是否有解决方法?
【发布时间】:2015-02-17 21:01:48
【问题描述】:

this talk around the 1:20 mark 中,Edward Kmett 提到 Haskell 中缺少“类型类回溯”。考虑在列表上实现的“集合相等”(忽略顺序和多重性)的问题:

equals :: [a] -> [a] -> Bool

由于类型类的性质,如果我们只有 Eq a,我无法提供低效的 O(n²) 比较,但如果我们有 Ord a,则可以有效地比较 O(n log n) 中的列表。

我确实明白为什么这样的设施会有问题。同时,Edward 说,有一些“你可以玩的技巧”提到实例类型族。

因此我的问题是,实现相同效果的解决方法是什么:

  • 提供了一个(低效的)默认实现
  • 如果用户可以提供有关该类型的一些额外信息,我们会“解锁”更高效的实现

此解决方法不一定需要使用类型类。

编辑:有些人建议简单地提供equalsefficientEquals 作为两种不同的方法。总的来说,我同意这是更符合 Haskell 习惯的方法。但是,我不相信这总是可能的。例如,如果上面的 equals 方法本身是类型类的一部分会怎样:

class SetLike s where
    equals :: Eq a => s a -> s a -> Bool

假设这个类是由其他人提供的,所以我不能简单地向类型类添加一个新函数。我现在想为[] 定义实例。我知道[] 始终可以提供equals 的实现,无论a 有什么限制,但如果有更多关于a 的信息可用,我无法告诉我的实例使用更高效的版本。

也许我可以将列表包装在一个新类型中并用一些额外的类型信息标记它?

【问题讨论】:

  • 这很有趣,我不知道答案。但是一个快速而肮脏的解决方案是定义一个不同的函数,比如 efficientEquals :: (Ord a) => [a] -> [a] -> Boolequals :: (Eq a) => [a] -> [a] -> Bool
  • 请明确表示我们正在讨论比较列表不考虑顺序(也可能是多重性)。显然(==) 不需要列表上的 O(n^2)。
  • 有一些技术使用OverlappingInstances,或封闭类型族,在某些情况下可用于获得您正在寻找的东西(虽然不是真的在这里;除非您同意,比如说,用你关心的Ord 实例枚举/复制所有类型)。请参阅the "advanced overlap" 维基页面。 "extra method trick" 也可能与此处相关。
  • 这里的主要问题是 Eq 是 Ord 的超类。所以 every Ord 类型也是 Eq 类型,这意味着您希望仅在 Ord a not 成立的情况下选择 Eq 情况。 Haskell 中的类型类系统经过严格设计,以确保它永远不会提交否定的约束;这个属性是它提供的许多一致性保证的核心,因为通过单独编译模块,GHC 永远无法区分“不存在实例”和“没有实例在范围内”。
  • 我能想到的方法可以让您想出一个适用于 Eq 和 Ord 实例的equals,但我想不出任何方法不仅会抱怨模棱两可的类型变量当调用事物时是 Ord 的实例。您最终不得不在每个呼叫站点手动说(equals :: Ord a => [a] -> [a] -> Bool) xs ys。而且,如果您愿意手动声明要调用的 equals 版本,它们也可能具有不同的名称,这样更容易做到。

标签: haskell typeclass


【解决方案1】:

在您编辑的场景中,您可以使用 GADT 作为您的 Ord 约束的证明:

{-# LANGUAGE GADTs #-}

import Data.List

class SetLike s where
    equals :: Eq a => s a -> s a -> Bool

data OrdList a where
    OrdList :: Ord a=> [a] -> OrdList a

instance SetLike OrdList where
    equals (OrdList a) (OrdList b) = sort a == sort b

为了好玩,这里使用了我在评论中提到的OverlappingInstances 技巧。有很多方法可以做这种事情:

{-# LANGUAGE DefaultSignatures, OverlappingInstances, UndecidableInstances, MultiParamTypeClasses, FlexibleInstances #-}

import Data.List

class Equals a where
    equals :: [a] -> [a] -> Bool

    default equals :: Ord a=> [a] -> [a] -> Bool
    equals as bs = sort as == sort bs

instance Equals Int
instance Equals Integer
instance Equals Char
-- etc.

instance (Eq a)=> Equals a where
    equals = error "slow version"

【讨论】:

  • 不幸的是,OverlappingInstances 取消了我们通常通过类型类获得的一些保证,不是吗?不过,我真的很喜欢 GADT 解决方案!
猜你喜欢
  • 2017-01-19
  • 2023-04-08
  • 2010-09-22
  • 2013-03-31
  • 1970-01-01
  • 2023-04-04
  • 2023-03-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多