鉴于我有以下内容:
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)
的实例应该如何允许我的计算?
答案 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)