Scala 3. 将 Haskell 中的 Continuation monad 示例转换为 Scala

huangapple go评论65阅读模式
英文:

Scala 3. Adapting Continuation monad example from Haskell to Scala

问题

学习Scala 3中的单子主题

在 https://jsdw.me/posts/haskell-cont-monad/ 上找到了可理解的Continuation单子的分解。

当我尝试将简单的代码转换成Scala时
```haskell
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

我可以编译并且可以很好地运行fourC(identity)。但是twoHelloC中的bind抱怨了Int/String类型不匹配。

我目前的bind实现如下:

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 =>  // 这里看到 Cont[String, String] 很不高兴
      return_(two.toString + hello)
      )
    )

问题:
你会如何在Scala中实现中缀bind,以及为什么Haskell类型系统允许twoHelloC编译?我在这里错过了什么?

谢谢


<details>
<summary>英文:</summary>

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
```haskell
twoC = \out -&gt; out 2
helloC = \out -&gt; out &quot;hello&quot;
ret val = \out -&gt; out val

inC `bind` fn = \out -&gt; inC (\inCval -&gt; (fn inCval) out)

fourC = twoC `bind` \two -&gt; ret (two*2)

twoHelloC = twoC `bind` \two -&gt;
              helloC `bind` \hello -&gt;
                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 =&gt; Int) =&gt; Int =
  out =&gt; out(2)

val helloCont: (String =&gt; String) =&gt; String =
  out =&gt; out(&quot;hello&quot;)

type Cont[X, R] = (X =&gt; R) =&gt; R

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

val twoHelloCont: (String =&gt; String) =&gt; String =
  twoCont bind (two =&gt;
    helloCont bind (hello =&gt;  // 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

得分: 3

In Scala, you can start with ordinary methods. Here is the translated code:

def twoC[A](out: Int => A): A = out(2)
def helloC[A](out: String => A): A = out("hello")
def ret[A1, A2](value: A1): (A1 => A2) => A2 = func => func(value)
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)
    )
  )

You can further enhance this code with extension, infix, Cont, and other features as needed. Is it clear how to finish the translation?

英文:

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

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

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

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

which can be shortened to

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

Now you can add extension, infix, Cont etc.

Is is clear how to finish translation?

huangapple
  • 本文由 发表于 2023年2月24日 14:05:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/75553113.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定