4
votes

I have defined a monadic counter in Haskell which I'm trying to translate to Scala, failing miserably so far. The problem in a nutshell is about implementing a counter as a state monad, which reads the counter increment constant from the environment, and logs the history of the counter (its sequence of values).

A friend of mine improved upon my solution, and came up with this simple solution:

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

I've tried without success to code this into Scala + (Cats | ScalaZ). The latest stable version of Cats lacks a lift method for WriterT. And with ReaderWriterState in Scalaz I could not figure out in a couple of hours how to use the local method. And that's only the begining ...

How this Haskell solution could be translated in a simple and elegant way? (to the extent allowed by the language).

Side note:

I'm still trying to figure out why I need to spend so much time to translate simple solutions from Haskell to Scala + FP libraries (Cats, Scalaz). In Haskell finding the instances and available functions of each type-class is a breeze, in Scala using IntelliJ, GitHub and StackOverflow this takes me days. So I'm wondering what am I doing wrong, and how could I improve this situation.

1
About your side note: You don't find the scala doc helpful in exploring the API?Samar
So far not. The problem that I see a method defined as private to a class (take local above for instance), which means that you cannot use it directly but through one of the multiple instances of this private class. I don't think you can do this from the Scala doc alone.Damian Nadales
With regards to your side note, the Scala standard library does not come equipped with algebras such as Monad. If you want access to these facilities then either you have to hand code them yourself or as you did use Scalaz or Cats. However, due to the verbosity of the language and it's type system, it is comparatively more work to get the same thing in Haskell done in Scala. As Edward Kmett says, Haskell gives him a much higher power to weight ratio. Your frustrations prove that.M.K.
@Samar, I would say that the biggest barrier is that they are unable to transfer any of their OOP knowledge over to the FP paradigm. If we even take the most basic underlying concept of referential transparency in FP, immediately the imperative programmer is stumped. Firstly they have to understand that the notion of a function in FP is that of a mathematical function, not a function/procedure in the imperative sense (which sometimes has an implied environment baggaged with it).M.K.
If we take away the basic tools of an OOP programmer such as inheritance then you have almost crippled him. And we haven't even begun to talk about other FP bread and butter concepts in FP such as HOFs, lambda calculus, ADTs, algebra and co-algebras etc...M.K.

1 Answers

2
votes

If I understood your intentions right, this translates to very easy and comprehensible piece of code:

  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)))