如何在Haskell中进一步限制现有类型类

5
有没有一种方法来进一步限制现有类型类的上下文?
例如,类型类 Functor:
class Functor f where
    fmap :: (a -> b) -> f a -> f b

这个类定义不强制要求abShow的元素。此外,这个类型类是由我自己包含的,所以我不能影响这个类的定义。后来是否有可能只允许那些属于Showab成员?


很有趣听到你为何想要人工限制代码并无正当理由的原因。也许存在一些误解需要澄清。 - Ingo
约束通常放在函数上,而不是数据/类型类上。原因是:你永远不知道你需要什么。假设你使用了Daniel的答案中的ShowFunctor。你不能将其变成一个applicative(因此也不能是Monad),因为applicative需要能够包装函数,而函数没有show实例。 - mb14
正如其他答案已经指出的那样,这是不可能的。但是,如果这是您自己的类型类,并且您希望推迟进一步限制它的决定,我在这里做了一点尝试:https://discourse.haskell.org/t/defer-extra-constraints-in-later-integration-stage-of-the-development/4713 - Miao ZhiCheng
2个回答

11

不能直接进行更改。如果要更改类的定义,必须更改源代码并重新编译。对于在标准库中定义的类,这样做会导致大量代码失效,因此不是一个现实的选项。

但是,你可以将该类进行包装并添加所需的约束条件。

class Functor f => ShowFunctor f where
    smap :: (Show a, Show b) => (a -> b) -> f a -> f b
    smap f = fmap f

然后使用那个类而不是原始类。

但也许你不需要额外的类,对于你的应用程序,在顶层定义smap并直接使用它可能已经足够了,而无需使用fmap

smap :: (Functor f, Show a, Show b) => (a -> b) -> f a -> f b
smap = fmap

1
我想这取决于使用情况。通常情况下,我认为子类化应该遵循面向对象设计原则(至少是LSP风格的思考)。毕竟,Haskell类只是OO类,除了它们没有状态,在构造函数中采用函数,并自动传递而不是被视为一等值。 - Philip JF
@PhilipJF,虽然晚了三年,但是Haskell类并不像面向对象的类那样。像MonoidEq和最明显的Read这样的类根本不适合面向对象的模型。 - dfeuer
@dfeuer 是的,他们确实会...或者至少在经过字典传递翻译之后会。 - Philip JF
@PhilipJF,这对我来说听起来有点牵强。在那个层级上,“子类型(subtyping)”纯粹是结构性的,这并不完全符合面向对象编程的工作方式。 - dfeuer
@dfeuer Haskell类的子类型是名义上的。因此,您只需要字典的名义子类型和函数的标准“结构”规则(如果您具有协变/逆变并将函数视为通用函数,则可以获得这些规则...因此,在任何合理的OOP语言中)就足以对传递字典的转换进行类型化。在像control.lens这样的东西中看到的子类型技术上也是名义上的,因为它在Haskell子类下面。 它只是CPSed,所以与您习惯的相反(从上面打开而不是从下面打开)。 - Philip JF
显示剩余6条评论

2

目前,你无法这样做而不破坏其他东西。

你有几个选择:

  1. 定义自己的受限制的Functor类。
  2. 不必担心定义一个类,只需定义一个执行所需操作的函数。
  3. 使用RMonad包
  4. 作弊。

实际上,我们现在知道如何允许实例添加约束条件,所以也许有一天这不会那么糟糕。请参阅Haskell中的子范畴,该论文处理了几乎完全相同的问题。该论文中的语法与GHC中当前有效的语法略有不同,但基本上我们想重新定义Functor类的外观。

class Functor f where
   type SubCat f :: * -> Constraint -- associated constraint
   type SubCat f = () -- default definition
   fmap :: (SubCat f a, SubCat f b) => (a -> b) -> f a -> f b

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接