将这个 monadic 计数器从 Haskell 翻译成 Scala
Translating this monadic counter from Haskell to Scala
我已经定义了一个 monadic counter in Haskell,我正在尝试将其转换为 Scala,但到目前为止失败得很惨。简而言之,问题是关于将计数器实现为状态 monad,它从环境中读取计数器增量常量,并记录计数器的历史记录(它的值序列)。
我的一个朋友改进了我的解决方案,并提出了这个简单的解决方案:
newtype Counter = Counter Int deriving (Eq)
instance Show Counter where
show (Counter i) = show i
incWith :: MonadState Counter m => Int -> m ()
incWith n = let incCounter n' (Counter i) = Counter $ i + n'
in modify (incCounter n)
inc :: (MonadReader Int m, MonadState Counter m, MonadWriter [Counter] m) => m ()
inc = ask >>= incWith >> get >>= tell . (:[])
compute :: (MonadReader Int m, MonadState Counter m, MonadWriter [Counter] m) => m ()
compute =
local (const 3) $ do
inc
inc
inc
local (const 5) $ do
inc
inc
我已经尝试 without success 将其编码为 Scala + (Cats | ScalaZ)。 Cats 的最新稳定版缺少 WriterT
的 lift
方法。在 Scalaz
中使用 ReaderWriterState
我在几个小时内无法弄清楚如何使用 local
方法。而这仅仅是开始...
如何以简单而优雅的方式翻译这个 Haskell 解决方案? (在语言允许的范围内)。
旁注:
我仍在努力弄清楚为什么我需要花这么多时间将简单的解决方案从 Haskell 转换为 Scala + FP 库(Cats、Scalaz)。在 Haskell 中找到每个类型的实例和可用函数 - class 是一件轻而易举的事,在使用 IntelliJ 的 Scala 中,GitHub 和 Whosebug 这需要我几天的时间。所以我想知道我做错了什么,我该如何改善这种情况。
如果我理解你的意图是正确的,这将转化为非常简单易懂的代码段:
import scalaz._
import Scalaz._
val SM = ReaderWriterState.rwstMonad[Id, Int, List[String], Counter]
case class Counter(value: Int)
def incWith(n: Int): State[Counter, Unit] = for {
v ← get[Counter]
_ ← put(Counter(v.value + n))
} yield ()
def inc: IRWS[Int, List[String], Counter, Counter, Unit] = for {
v ← SM.ask
c ← SM.get
_ ← SM.tell(List(s"Incrementing $c by $v "))
_ ← SM.modify(counter ⇒ Counter(counter.value + v))
} yield ()
def compute: IRWS[Int, List[String], Counter, Counter, Unit] = {
for {
_ <- SM.local[Unit](i ⇒ 3)(for {
_ ← inc
_ ← inc
_ ← inc
} yield ())
_ <- SM.local[Unit](i ⇒ 5)(for {
_ ← inc
_ ← inc
_ ← inc
} yield ())
} yield ()
}
println(incWith(5)(Counter(0)))
println(inc.run(4, Counter(0)))
println(compute.run(0, Counter(0)))
我已经定义了一个 monadic counter in Haskell,我正在尝试将其转换为 Scala,但到目前为止失败得很惨。简而言之,问题是关于将计数器实现为状态 monad,它从环境中读取计数器增量常量,并记录计数器的历史记录(它的值序列)。
我的一个朋友改进了我的解决方案,并提出了这个简单的解决方案:
newtype Counter = Counter Int deriving (Eq)
instance Show Counter where
show (Counter i) = show i
incWith :: MonadState Counter m => Int -> m ()
incWith n = let incCounter n' (Counter i) = Counter $ i + n'
in modify (incCounter n)
inc :: (MonadReader Int m, MonadState Counter m, MonadWriter [Counter] m) => m ()
inc = ask >>= incWith >> get >>= tell . (:[])
compute :: (MonadReader Int m, MonadState Counter m, MonadWriter [Counter] m) => m ()
compute =
local (const 3) $ do
inc
inc
inc
local (const 5) $ do
inc
inc
我已经尝试 without success 将其编码为 Scala + (Cats | ScalaZ)。 Cats 的最新稳定版缺少 WriterT
的 lift
方法。在 Scalaz
中使用 ReaderWriterState
我在几个小时内无法弄清楚如何使用 local
方法。而这仅仅是开始...
如何以简单而优雅的方式翻译这个 Haskell 解决方案? (在语言允许的范围内)。
旁注:
我仍在努力弄清楚为什么我需要花这么多时间将简单的解决方案从 Haskell 转换为 Scala + FP 库(Cats、Scalaz)。在 Haskell 中找到每个类型的实例和可用函数 - class 是一件轻而易举的事,在使用 IntelliJ 的 Scala 中,GitHub 和 Whosebug 这需要我几天的时间。所以我想知道我做错了什么,我该如何改善这种情况。
如果我理解你的意图是正确的,这将转化为非常简单易懂的代码段:
import scalaz._
import Scalaz._
val SM = ReaderWriterState.rwstMonad[Id, Int, List[String], Counter]
case class Counter(value: Int)
def incWith(n: Int): State[Counter, Unit] = for {
v ← get[Counter]
_ ← put(Counter(v.value + n))
} yield ()
def inc: IRWS[Int, List[String], Counter, Counter, Unit] = for {
v ← SM.ask
c ← SM.get
_ ← SM.tell(List(s"Incrementing $c by $v "))
_ ← SM.modify(counter ⇒ Counter(counter.value + v))
} yield ()
def compute: IRWS[Int, List[String], Counter, Counter, Unit] = {
for {
_ <- SM.local[Unit](i ⇒ 3)(for {
_ ← inc
_ ← inc
_ ← inc
} yield ())
_ <- SM.local[Unit](i ⇒ 5)(for {
_ ← inc
_ ← inc
_ ← inc
} yield ())
} yield ()
}
println(incWith(5)(Counter(0)))
println(inc.run(4, Counter(0)))
println(compute.run(0, Counter(0)))