Search code examples
scalahaskelltypesmonadscontinuations

Scala 3. Adapting Continuation monad example from Haskell to Scala


Learning Scala 3 with monadic topics.

came across understandable breakdown of Continuation monad at https://jsdw.me/posts/haskell-cont-monad/

When I try to adopt simple code into Scala

twoC = \out -> out 2
helloC = \out -> out "hello"
ret val = \out -> out val

inC `bind` fn = \out -> inC (\inCval -> (fn inCval) out)

fourC = twoC `bind` \two -> ret (two*2)

twoHelloC = twoC `bind` \two ->
              helloC `bind` \hello ->
                ret $ (show two)++hello

i can have fourC(identity) compiling and working well. But binds in twoHelloC complain about Int/String type mismatch.

current impl of bind I have:


val twoCont: (Int => Int) => Int =
  out => out(2)

val helloCont: (String => String) => String =
  out => out("hello")

type Cont[X, R] = (X => R) => R

extension[A, FinalRes] (inCont: Cont[A, FinalRes]) {
  infix def bind[B](fn: A => Cont[B, FinalRes]): Cont[B, FinalRes] = {
    (out: B => FinalRes) => inCont(inContVal => (fn(inContVal))(out))
  }
}

val twoHelloCont: (String => String) => String =
  twoCont bind (two =>
    helloCont bind (hello =>  // here it is unhappy to see Cont[String, String]
      return_(two.toString + hello)
      )
    )

Question: how would you implement infix bind in Scala, and why Haskell typesystem permits twoHelloC to compile? What do i miss here?

thanks


Solution

  • If you have questions what types were inferred in Haskell you can always ask :t ... in ghci

    ghci> :t twoHelloC
    twoHelloC :: ([Char] -> t) -> t
    ghci> :t fourC
    fourC :: (Integer -> t) -> t
    ghci> :t bind
    bind :: ((t1 -> t2) -> t3) -> (t1 -> t4 -> t2) -> t4 -> t3
    ghci> :t ret
    ret :: t1 -> (t1 -> t2) -> t2
    ghci> :t helloC
    helloC :: (String -> t) -> t
    ghci> :t twoC
    twoC :: (Integer -> t) -> t
    

    In Scala you should start with ordinary methods. Verbose translation is

    def twoC[A](out: Int => A): A = out(2)
    def helloC[A](out: String => A): A = out("hello")
    def ret[A1, A2](`val`: A1): (A1 => A2) => A2 =
      (out: A1 => A2) => out(`val`)
    def bind[A1, A2, A3, A4](inC: (A1 => A2) => A3)(fn: A1 => A4 => A2): A4 => A3 =
      (out: A4) => inC((inCval: A1) => fn(inCval)(out))
    def fourC[A]: (Int => A) => A =
      bind[Int, A, A, Int => A](twoC[A])((two: Int) => ret[Int, A](two * 2))
    def twoHelloC[A]: (String => A) => A =
      bind[Int, A, A, String => A](twoC[A])((two: Int) =>
        bind[String, A, A, String => A](helloC[A])((hello: String) =>
          ret[String, A](two.toString + hello)
        )
      )
    

    which can be shortened to

    def twoC[A](out: Int => A): A = out(2)
    def helloC[A](out: String => A): A = out("hello")
    def ret[A1, A2](`val`: A1): (A1 => A2) => A2 = out => out(`val`)
    def bind[A1, A2, A3, A4](inC: (A1 => A2) => A3)(fn: A1 => A4 => A2): A4 => A3 =
      out => inC(inCval => fn(inCval)(out))
    def fourC[A]: (Int => A) => A = bind(twoC[A])(two => ret(two * 2))
    def twoHelloC[A]: (String => A) => A =
      bind(twoC[A])(two =>
        bind(helloC[A])(hello =>
          ret(two.toString + hello)
        )
      )
    

    Now you can add extension, infix, Cont etc.

    Is is clear how to finish translation?