假设我们有一个具有明显操作的
data Vector
(所以 Vector 是 Monoid 的实例)
(+):: Vector -> Vector -> Vector
现在,假设我们希望某个对象可以通过矢量移动。 假设我确切地知道哪些类型被认为是可移动的,所以有一些移动功能
move :: Vector -> Movable -> Movable
哪里
data Movable = Movable t1 | t2 | t3
或者课堂版
class Movable a where
move :: Vector -> a -> a
使用表格的一些实例
instance Movable ti where
move :: Vector -> ti -> ti
就我个人而言,我更喜欢课堂版。
现在,假设我们有一系列计算大致相当于
move v1 . move v2 . move v3
最终相当于
move (v1 + v2 + v3)
我们想要优化:
move (v1 + v2)
比move v1 . move v2
便宜得多。
问题:据我所知,这就是 Applicative 派上用场的地方? 如何在正确的 Haskell 中实现它?
我想出了以下方法:将信息存储在表单的某个容器中
data Move a = Move {data :: a, offset :: Vector}
并且有独立的功能
transform :: Movable a => Move a -> a
这样我们就可以实现了
instance Applicative (Move a)
但我觉得很难看。有没有更好的办法? 能不能转成Monad(只是为了使用灵活的标准库)
UPD:我需要的是: 如果我有一个可移动对象
x
,并且我变换它
y = move v1 $ move v2 $ move v3 $ x
Haskell 必须在内部将其变成
y = move (v1 + v2 + v3) x
仅当我评估时
y
。
我想懒惰会有所帮助。
data Moved a = Moved { base :: a, result :: a, offset :: Vector }
-- smart constructor; not really needed, strictly speaking
moved :: Movable a => a -> Vector -> Moved a
moved b o = Moved b (move b o) o
instance Movable a => Movable (Moved a) where
move v m = moved (base m) (v + offset m)
第一次访问
result
对象的 Moved
时,将会对其进行计算(但进一步的访问将使用之前计算的结果)。中间 result
将被丢弃,而不会产生其 move
的成本。