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 bind
s 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
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?