haskell - 在 Haskell 中声明 "subclass"

标签 haskell subclass typeclass adhoc-polymorphism

我在 Haskell 中遇到以下简单代码的问题:

import Prelude hiding (cycle).

class ICycle a where
    cycle :: a -> a

instance ICycle [a] where
    cycle [] = []
    cycle (x:xs) = xs ++ [x]

instance ICycle Bool where
    cycle True = False
    cycle False = True

instance Num a => ICycle a where
    cycle n = n+1

main = do
    print $ cycle $ [1,2,3]
    print $ cycle $ True
    print $ cycle $ 42

这里前两个实例声明按预期工作,但第三个实例声明根据标志组合触发各种错误。

我知道Num a不短于ICycle a,因此编译器无法完成类型检查。在示例中,我发现可以通过将右侧设为更大的术语或首先将感兴趣的类声明为其他类的子类来避免这种情况。相反,在这里,我本质上是想将现有类声明为新类的子类。

我想知道是否有人反对这种类型类的使用。或者,如果有一个自然的解决方案。

最佳答案

对于这个特定的示例,我认为您最好使用 newtype 来包装实例:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Prelude hiding (cycle)

class ICycle a where
    cycle :: a -> a

newtype Succ a = Succ { runSucc :: a }
  deriving (Num, Eq, Ord, Bounded, Enum, Show, Read)

newtype Pred a = Pred { runPred :: a }
  deriving (Num, Eq, Ord, Bounded, Enum, Show, Read)

instance Enum a => ICycle (Succ a) where
    cycle = Succ . succ . runSucc

instance Enum a => ICycle (Pred a) where
    cycle = Pred . pred . runPred

main = do
    print $ cycle $ (42 :: Succ Int)
    print $ cycle $ (42 :: Pred Int)

人们可以通过多种方式循环数字 - 通过 succ、通过 pred、通过加倍、通过减半。为实例使用 newtype 的优点 (正如您在问题中指出的那样,使 RHS“更大”)是它让我们拥有所有这些。

标准库使用 Product 执行相同的操作。 和 Sum对于幺半群

换个角度看,如果可以为 Num 定义一个新的父类(super class),为所有实例添加默认实现 Num,那么您将从所有这些实现中取消该选择。可能以一种没有意义的方式。

关于haskell - 在 Haskell 中声明 "subclass",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29931647/

相关文章:

C++ - std::list remove_if 不释放内存?

haskell - GADT 类型的奇怪类型推理行为(对于固定长度向量)

haskell - 在 Criterion 中进行基准测试之前强制评估函数输入

java - cxf 公开抽象类的子类

haskell - Haskell 中的引用透明度困惑

python - 继承 DataFrame 时如何为 'loc' 编写包装器方法

haskell - 为什么这个实现是可折叠类型类的错误实例?

parsing - 在 Haskell 中创建一个 Read 实例

performance - "Call by name"会减慢 Haskell 的速度吗?

haskell - 如何在这里避免显式类型签名?