可能在记录内部或者如何为实例写入(Num(可能是a))中的值的算术

时间:2018-03-08 22:52:01

标签: haskell

鉴于我有以下内容:

data Tick = Tick { _price :: Int } deriving (Eq, Ord)

type High = Maybe Tick
type Low = Maybe Tick

data Candle = Candle
  { _open :: Open
  , _high :: High
  , _low :: Low
  , _close :: Close
  } deriving (Eq, Ord)

makeLenses ''Candle

当我创建一个蜡烛时:

candle = ...

然后我想用计算高低之间的差异:

 axis = high - low

结果:

• No instance for (Num High) arising from a use of ‘-’
• In the expression: high - low
  In an equation for ‘axis’: axis = high - low

我尝试过创建这样的实例:

instance Num High where
    (-) (Just a) (Just b) = Just (a - b)

但这会导致错误:

• Illegal instance declaration for ‘Num High’
    (All instance types must be of the form (T t1 ... tn)
     where T is not a synonym.
     Use TypeSynonymInstances if you want to disable this.)
• In the instance declaration for ‘Num High’

所以我的问题是,(Num High)的实例应该如何允许我的计算?

1 个答案:

答案 0 :(得分:4)

Arghhhh ......不!不要那样做!为什么你认为这是一个好主意?

High不是数字类型,因为它还包含非数字值Nothing 。 (即使抛开任何这样的推理,为一个类型和类定义一个实例基本上也不是一个好主意,它们都在一个已建立的标准库中,当然不是base。这样的实例会让所有知道这些库的人感到惊讶并突然遇到意外情况!此外,如果实例确实有意义,GHC人员可能已经定义了它。)

Maybe的作用是,它强迫你考虑 没有数字的情况,即没有给出上边界或下边界的情况。我不知道你想要这种情况的行为,但似乎可以立即制作axis - 结果Nothing。使用Maybe的{​​{1}}实例

可以轻松完成此操作
Applicative

...假设axis :: Maybe Int axis = (-) <$> candle^.high <*> candle^.low 本身有Tick个实例,这可能不是非常谨慎的,但至少是合理的。如果它没有Num实例,则需要打开Num构造函数:

Tick

那就是说,如果你坚持要定义那个实例(你不应该这样做),那么这很容易做到。首先,你可以解开类型同义词:

axis = Tick <$> ((-) <$> (_price <$> candle^.high) <*> (_price <$> candle^.low)

但这甚至不是必要的;正如编译器已经暗示你可以通过类型同义词直接给出实例,它只是不是Haskell98。只需启用扩展程序

即可
instance Num (Maybe Tick) where
  Just (Tick a) - Just (Tick b) = Just . Tick $ a - b
  _ - _ = Nothing

{-# LANGUAGE TypeSynonymInstances #-}

然后

{-# LANGUAGE FlexibleInstances #-}

被接受,无论好坏。

<小时/> 请注意,仅使用instance Num High where ... 定义Applicative个实例会导致问题:您希望Num-取消,即+。但是通过直截了当的(a - b) + b ≡ a(也可以写成a+b = (+)<$>a<*>b),您将拥有

(+)=liftA2(+)

......请注意,浮点实例已经表现出相同的行为

(Just 1 - Nothing) + Nothing = Nothing
                             ‡ Just 1

但至少在那里它被理解为无穷大和NaN是一切都变得有点奇怪的角落情况,而对于Prelude> (1 - 1/0) + 1/0 NaN 你期望Maybe Int是一个非常正常的,“它只是不是这里的“价值。

一个更好的例子可能是

Nothing

...如果您将import Data.AdditiveGroup instance AdditiveGroup Tick 添加到数据定义中,则无需任何显式实现即可运行:

Generic

根据定义,您可以执行

{-# LANGUAGE DeriveGeneric #-}
import GHC.Generics
data Tick = Tick { _price :: Int } deriving (Eq, Ord)