Scala 3. Adapting Continuation monad example from Haskell to Scala

125 Views Asked by At

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

1

There are 1 best solutions below

15
Dmytro Mitin On BEST ANSWER

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?