【问题标题】:Define instance of a typeclass for any kind of number为任何类型的数字定义类型类的实例
【发布时间】:2020-09-19 12:33:13
【问题描述】:

每个人。我是 Haskell 的新手。我想定义一个适用于任何数字的类型类实例。举个例子,我写一个简单的typeclass:

class YesNo a where
    yesno :: a -> Bool

基本上,yesno 函数模仿 JavaScript 的布尔检查行为。如果你给它一个“真实”的值,它会返回True,否则它会返回False。以下是一些使用它的示例:

yesno "3" -- True
yesno 3.0 -- True
yesno 0 -- False
yesno ""  -- False
yesno [] -- False

有一个问题,在 Haskell 中有几种类型来表示不同的数字:Int、Integer、Float... 虽然我可以为这些类型编写一个 yesno 函数,但我有分别为它们定义实例。


class YesNo a where
    yesno :: a -> Bool

instance YesNo Integer where
    yesno 0 = False
    yesno _ = True

instance YesNo Int where
    yesno 0 = False
    yesno _ = True

instance YesNo Float where
    yesno 0 = False
    yesno _ = True

instance YesNo Double where
    yesno 0 = False
    yesno _ = True

虽然有效,但写起来看起来非常麻烦和无聊。我想知道是否有更简洁的方法来完成这个,比如:

instance YesNo Integer where
instance YesNo Int where
instance YesNo Float where
instance YesNo Double where
    yesno 0 = False
    yesno _ = True

代码显然不符合 Haskell 的语法。我还尝试为实例添加类型约束。

instance (Num a) => YesNo a where
    yesno 0 = False
    yesno _ = True

我希望它会起作用,但它没有,并且在 GHCi 中显示了以下错误。看来实例的类型必须是具体的,类型类是不可接受的。

TypeClass.hs:39:21: error:
    • Illegal instance declaration for ‘YesNo a’
        (All instance types must be of the form (T a1 ... an)
         where a1 ... an are *distinct type variables*,
         and each type variable appears at most once in the instance head.
         Use FlexibleInstances if you want to disable this.)
    • In the instance declaration for ‘YesNo a’

【问题讨论】:

  • 您是否尝试过编译器的建议并启用FlexibleInstances?您可以通过将{-# LANGUAGE FlexibleInstances #-} 放在文件顶部来做到这一点。
  • 通常同意@RobinZigmond 的观点,即-XFlexibleInstances 完全没有问题,但是在这种情况下,它只是把石头踢到了路上:首先你还需要-XUndecidableInstances(仍然可以),然后一旦你有 instance (Num a) => YesNo a 工作所有 other 实例,如 instance YesNo Bool 将会中断,因为它们是 overlapping 就语言所知。同样,GHC 确实具有允许重叠实例的功能,但我不建议这样做。

标签: haskell


【解决方案1】:

如果您控制类声明,您可以通过这种方式对单个约束执行此操作:

{-# Language DefaultSignatures #-}
class YesNo a where
    yesno :: a -> Bool
    default yesno :: (Eq a, Num a) => a -> Bool
    yesno 0 = False
    yesno _ = True

instance YesNo Integer
instance YesNo Int
instance YesNo Float
instance YesNo Double

如果你不控制类声明,或者你想拥有两种不同的默认值,那么你可以做的最好的事情是这样的:

class YesNo a where
    yesno :: a -> Bool

yesnoNum :: (Eq a, Num a) => a -> Bool
yesnoNum 0 = False
yesnoNum _ = True

yesnoMonoid :: (Eq a, Monoid a) => a -> Bool
yesnoMonoid a = mempty /= a

instance YesNo Integer where yesno = yesnoNum
instance YesNo Int     where yesno = yesnoNum
instance YesNo Float   where yesno = yesnoNum
instance YesNo Double  where yesno = yesnoNum
instance Eq a => YesNo [a] where yesno = yesnoMonoid -- just an example, use null and no Eq constraint in real code

【讨论】:

    猜你喜欢
    • 2015-11-15
    • 1970-01-01
    • 1970-01-01
    • 2020-03-10
    • 1970-01-01
    • 2019-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多