【问题标题】:Inclusion of typeclasses with default implementation in Haskell在 Haskell 中包含具有默认实现的类型类
【发布时间】:2010-12-17 20:07:47
【问题描述】:

考虑以下定义:

class Foo a where
    foo :: a -> Int

class Bar a where
    bar :: a -> [Int]

现在,我怎么说“每个Foo也是一个Barbar在Haskell中默认定义为bar x = [foo x]

(无论我尝试什么,编译器都会给我“非法实例声明”“约束不小于实例头”

顺便说一句,如果有帮助的话,我可以用其他方式定义我的 FooBar 类。

【问题讨论】:

标签: haskell typeclass


【解决方案1】:
class Foo a where
    foo :: a -> Int

-- 'a' belongs to 'Bar' only if it belongs to 'Foo' also
class Foo a => Bar a where
    bar :: a -> [Int]
    bar x = [foo x] -- yes, you can specify default implementation

instance Foo Char where
    foo _ = 0

-- instance with default 'bar' implementation
instance Bar Char

【讨论】:

  • 这在模块化方面比instance Foo a => Bar a 解决方案友好得多。
  • 但是作者的“每个 Foo 也是一个 Bar”的要求不应该像这里那样用 'class Bar a => Foo a where' 来表达吗?
  • 但在我看来 m01 实际上是指 'class Foo a => Bar' a where' 否则通过 foo 定义 bar 没有意义
【解决方案2】:

由于Bar 实例通过Foo 实例的自动定义可能导致编译器无法确定的情况 - 即一个显式实例和一个通过Foo 相互冲突 - ,我们需要一些特殊选项来允许所需的行为。剩下的就很简单了。

{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}

class Foo a where
    foo :: a -> Int

class Bar a where
    bar :: a -> [Int]

instance (Foo a) => Bar a where
    bar x = [foo x]

【讨论】:

  • 如果您打算拥有更多Bar 的实例(如果没有这样做毫无意义),那么您也需要OverlappingInstances。这会导致类型推断有时会产生奇怪的结果(在谈论这些类时总是使用签名),并且通常表明您做错了。
  • 永远不要发布一个包含这样一个实例的模块。这是粗鲁的(它会导致严重的模块化问题)。如果您打算分享,总是更喜欢 max taldykin 的解决方案。
  • 真的,你不应该这样做。即使你不发布它,你也很可能会遇到其他微妙的问题。
【解决方案3】:

一般来说,您不会以这种方式使用类型类对事物进行建模[*] - 即类型类的实例应该始终是某种具体类型,尽管该类型本身可以是参数化的 - 例如pair 的 Show 实例具有以下签名:

instance (Show a, Show b) => Show (a,b) where

“泛型”的一些方法允许您对一般的基本情况进行建模,然后有特定类型的例外情况。 SYB3 允许这样做 - 不幸的是,SYB3 不是通用的泛型库,它是 Data.Data / Data.Generics,我认为是 SYB1。

[*] 在野外,故事要复杂一些——正如达里奥所说,UndecidableInstances 可以启用它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多