如何在kind的monads中重复单子指令?

3

我知道我可以在Kind语言中依次运行单子指令,例如:

Test: _
  IO {
   IO.print(Nat.show(2))
   IO.print(Nat.show(3))
   IO.print(Nat.show(4))
  }


output:
2
3
4

但是是否可以像下面这样重复运行单子指令?

Test: _
  a = [2,1,3,4,5]
  IO {
    for num in a:
      IO.print(Nat.show(num))
  }

如果可能的话,我应该如何正确地做?

1
我不知道它是哪种类型的,但它是否提供递归? - Bergi
@Bergi 是的,它是一种功能型“证明编程”语言。 - Rheidne Achiles
1个回答

1

单子通常由两个运算符表示:

  return :: a -> m(a) // that encapulapse the value inside a effectful monad
  >>= :: m a -> (a -> m b) -> m b
  // the monadic laws are omitted

注意,bind操作符自然递归,一旦它能够组合两个单子甚至丢弃一个值,返回值可以被视为“基本情况”。
m >>= (\a -> ... >>= (\b -> ~ i have a and b, compose or discard? ~) >>= fixpoint)

你只需要生成那个序列,这很简单。例如,在Kind中,我们将monads表示为一对,它接受一个类型值并封装了一个多态类型。
type Monad <M: Type -> Type> {
  new(
    bind: <A: Type, B: Type> M<A> -> (A -> M<B>) -> M<B>
    pure: <A: Type> A -> M<A>
  )
}

在您的示例中,我们只需要触发效果并丢弃值,递归定义就足够了:
action (x : List<String>): IO(Unit)
  case x {
    nil : IO.end!(Unit.new) // base case but we are not worried about values here, just the effects
    cons : IO {
      IO.print(x.head) // print and discard the value
      action(x.tail) // fixpoint
    }
  }

test : IO(Unit)
  IO {
    let ls = ["2", "1", "3", "4", "5"]
    action(ls)
  }

你所了解的IO将通过一系列绑定进行desugaring!通常情况下,对于列表,它可以被泛化为Haskell库的mapM函数:
Monadic.forM(A : Type -> Type, B : Type,
  C : Type, m : Monad<A>, b : A(C), f : B -> A(C), x : List<A(B)>): A(C)
  case x {
    nil : b
    cons : 
      open m
      let k = App.Kaelin.App.mapM!!!(m, b, f, x.tail)
      let ac = m.bind!!(x.head, f)
      m.bind!!(ac, (c) k) // the >> operator
  } 

它自然地丢弃了这个值,最终我们可以这样做:

action2 (ls : List<String>): IO(Unit)
  let ls = [IO.end!(2), IO.end!(1), IO.end!(3), IO.end!(4), IO.end!(5)]
  Monadic.forM!!!(IO.monad, IO.end!(Unit.new), (b) IO.print(Nat.show(b)), ls)

所以,action2与action做的事情相同,但是只需要一行代码!当你需要组合值时,可以将其表示为单子折叠:
Monadic.foldM(A : Type -> Type, B : Type,
  C : Type, m : Monad<A>, b : A(C), f : B -> C -> A(C), x : List<A(B)>): A(C)
  case x {
    nil : b
    cons : 
      open m
      let k = Monadic.foldM!!!(m, b, f, x.tail)
      m.bind!!(x.head, (b) m.bind!!(k, (c) f(b, c)))
  } 

例如,假设您想在循环中请求用户输入一系列数字并对其求和,则只需调用foldM并与简单函数组合即可:
Monad.action3 : IO(Nat)
  let ls = [IO.get_line, IO.get_line, IO.get_line]
  Monadic.foldM!!!(IO.monad, IO.end!(0), 
      (b, c) IO {
        IO.end!(Nat.add(Nat.read(b), c))
      },
   ls)

test : IO(Unit)
  IO {
    get total = action3 
    IO.print(Nat.show(total))
  }

目前,Kind不支持typeclass,这使得事情变得有点啰嗦,但我认为未来可以考虑对forM循环语法进行新的支持。我们希望如此 :)


谢谢您提供这么详细的答案!如果在基础目录中有foldMforM,(或许还有forM的语法),那就太好了。 - Rheidne Achiles

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接