繁体   English   中英

如何对类型类实例施加类型约束?

[英]How to impose type constraints on typeclass instances?

我正在通过阅读为您学习 Haskell 来学习 Haskell 非常好! . 制作我们自己的类型和类型类部分接近尾声时,定义了 class YesNo来模拟 javascript 等语言的真实性:

class YesNo a where  
    yesno :: a -> Bool 

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

(etc.)

在阅读参考之前,我试图自己充实实例作为练习,并认为我可以很聪明并为所有Num类型定义它:

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

我将跳过这对FlexibleInstances的要求,我想我在文档和这个答案之间已经理解了。 一旦打开,编译器就会抱怨“约束‘Num a’不小于实例头‘YesNo a’”。 这个问题的答案很好地解释了这意味着什么。 使用newtype提供的新类型解决方案,我想出了类似的东西

newtype TruthyNum a = TruthyNum a

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

但现在我不得不说例如yesno $ TruthyNum 0而不是yesno 0

这感觉不对。 如果不为每种此类类型写出一个实例,真的没有办法干净地表达Num类型的yesno吗? 或者,退一步说,一个经验丰富的 Haskell 黑客如何在“定义一个按照 [选择你的脚本语言] 实现真实性的类型类”的前提下来?

非常好的问题! 我会newtype你一样定义一个新类型。 我不会直接使用它,而是通过它派生。

{-# Language DerivingVia              #-}
{-# Language StandaloneDeriving       #-}
{-# Language StandaloneKindSignatures #-}

import Data.Kind (Type, Constraint)

type  YesNo :: Type -> Constraint
class YesNo a where
  yesno :: a -> Bool

type    TruthyNum :: Type -> Type
newtype TruthyNum a = TruthyNum a

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

-- standalone deriving, is used when deriving an instance
-- outside of the data declaration
deriving via TruthyNum Int
  instance YesNo Int
deriving via TruthyNum Integer
  instance YesNo Integer
deriving via TruthyNum Float
  instance YesNo Float

Applicative提升是另一种以这种方式重叠的行为。 给定Applicative f你可以举起像

  • Semigroup a , Monoid a , Num a

进入

  • Semigroup (fa) , Monoid (fa) , Num (fa)
instance (Applicative f, Num a) => Num (f a) where
  (+)         = liftA2 (+)
  (-)         = liftA2 (-)
  (*)         = liftA2 (*)
  negate      = liftA  . negate
  abs         = liftA  . abs
  signum      = liftA  . signum
  fromInteger = liftA0 . fromInteger where liftA0 = pure

我们不编写重叠实例,而是创建新类型Ap fa的实例。

type    Ap :: (k -> Type) -> (k -> Type)
newtype Ap f a = Ap (f a)
  deriving newtype (Functor, Applicative, ..)

instance (Applicative f, Num a) => Num (Ap @Type f a) where
  (+)         = liftA2 (+)
  (-)         = liftA2 (-)
  (*)         = liftA2 (*)
  negate      = liftA  . negate
  abs         = liftA  . abs
  signum      = liftA  . signum
  fromInteger = liftA0 . fromInteger where liftA0 = pure

这是一个蹦床示例,它派生依赖于先前推导的实例。 数据类型是“3D 向量”,但它只是具有 3 个相同类型的 arguments 的数据类型。

  1. 等式的内置推导和V3的通用表示
  2. Generically1 (一个newtype )使用来自 1 的泛型表示派生Applicative
  3. Ap通过从 2 中提升Applicative来导出Num
  4. TruthyNum使用相等性和Num实例从 3 派生YesNo

我们推导出的Applicative涉及提升pure a = V3 aaa所以当我们写0:: V3 Int我们实际上是指V3 0 0 0:: V3 Int

这意味着您的YesNo实例是通过与(/= V3 0 0 0)进行比较来实现的。 因此,当值为0时,我们将V3视为“假”。

-- >> 0 :: V3 Int
-- V3 0 0 0
-- >> yesno (V3 0 0 0)
-- False
-- >> yesno (V3 0 0 2)
-- True
data V3 a = V3 a a a
  deriving
  stock (Eq, Show, Generic1)

  deriving (Functor, Applicative)
  via Generically1 V3

  deriving (Semigroup, Monoid, Num)
  via Ap V3 a

  deriving YesNo
  via TruthyNum (V3 a)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM