我正在学习Haskell,并正在实现一个类的算法.它工作正常,但是类的要求是我保持计数乘以或加两个数的总次数.这就是我在其他语言中使用全局变量的原因,我的理解是它对Haskell来说是诅咒.
一种选择是让每个函数返回此数据及其实际结果.但这似乎并不好玩.
这就是我的想法:假设我有一些功能f :: Double -> Double
.我可以创建一个数据类型,(Double, IO)
然后使用仿函数来定义乘法中的乘法(Double, IO)
来进行乘法并向IO写入内容.然后我可以将我的新数据传递到我的函数中.
这有意义吗?有更简单的方法吗?
编辑:更清楚的是,在OO语言中,我将声明一个继承自然Double
后覆盖*
操作的类.这将允许我不必重写我的函数的类型签名.我想知道在Haskell中是否有某种方法可以做到这一点.
具体来说,如果我定义f :: Double -> Double
那么我应该能够做functor :: (Double -> Double) -> (DoubleM -> DoubleM)
对吗?然后我可以保持我的功能与现在一样.
实际上,你的第一个想法(用每个值返回计数)并不是坏的,并且可以由Writer monad(Control.Monad.Writer
来自mtl包或Control.Monad.Trans.Writer
变换器包)更抽象地表达.从本质上讲,编写器monad允许每个计算都有一个关联的"输出",只要它是一个实例,它就可以是任何东西Monoid
- 一个定义的类:
mempty
),即分配给'return'的输出在这种情况下,输出是一个操作计数,'empty'值是零,并且组合操作是加法.例如,如果您要单独跟踪操作:
data Counts = Counts { additions: Int, multiplications: Int }
Run Code Online (Sandbox Code Playgroud)
使该类型成为Monoid
(在模块中Data.Monoid
)的实例,并将您的操作定义为:
add :: Num a => a -> a -> Writer Counts a
add x y = do
tell (Counts {additions = 1, multiplications = 0})
return (x + y)
Run Code Online (Sandbox Code Playgroud)
作者monad与你的Monoid实例一起,负责将所有'tell'传播到顶层.如果你愿意,你甚至可以实现一个Num
实例Num a => Writer Counts a
(或者,最好是为新类型实现你不创建一个孤立实例),这样你就可以使用普通的数值运算符.