Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions proposals/0005-let-operator.mbt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Let Operator

* Proposal: [ME-0005](https://github.com/illusory0x0/moonbit-evolution/blob/0005-let-operator/proposals/0005-let-operator.mbt.md)
* Author: [illusory0x0](https://github.com/illusory0x0)
* Status: Under review
* Review and discussion: [Github issue](https://github.com/moonbitlang/moonbit-evolution/pull/6)

## Introduction

The introduction of binding operators can significantly reduce the depth of nesting when writing Monadic functions.

[quickcheck](https://github.com/moonbitlang/quickcheck/blob/main/src/gen.mbt#L141) and [simple_parserc](https://github.com/moonbit-community/simple_parserc/blob/master/src/monadic.mbt#L2)
rely heavily on Monadic functions, especially the quickcheck library.


## Motivation

Writing Monadic-related functions now would result in over-nested.

for example in [Parser Combinator](https://github.com/moonbit-community/elaboration_zoo.mbt/blob/master/src/03-holes/parser.mbt#L55-L139)

```moonbit
and let_ = fn(input : Input) -> (Raw, Input) raise ParseError {
let parser = symbol("let")
.discard_left(
name.bind(fn(x) {
symbol(":")
.discard_left(expr)
.bind(fn(a) {
symbol("=")
.discard_left(expr)
.bind(fn(t) {
symbol(";").discard_left(expr).map(fn(u) { RLet(x, a, t, u) })
})
})
}),
)
.label("let")
parser(input)
}
```

```moonbit
pub fn[A, B, C, D, E, F, G] liftA6(
ff : (A, B, C, D, E, F) -> G,
v : Gen[A],
w : Gen[B],
x : Gen[C],
y : Gen[D],
z : Gen[E],
u : Gen[F]
) -> Gen[G] {
v.bind(fn(a) {
w.bind(fn(b) {
x.bind(fn(c) {
y.bind(fn(d) {
z.bind(fn(e) { u.bind(fn(f) { pure(ff(a, b, c, d, e, f)) }) })
})
})
})
})
}
```

## Proposed solution

User define `T::op_bind` then we can use `let*` to introduce **binder**.


```moonbit
// define binding operator
pub fn[A, B] Parser::op_bind(x : Parser[A], f : (A) -> Parser[B]) -> Parser[B] {
fn(input) {
let (v, rest) = x(input)
f(v)(rest)
}
}

// using `let*` make the code more clear and simple!
and let_ = fn(input : Input) -> (Raw, Input) raise ParseError {
let parser = {
symbol("let") |> ignore
let* x = name
symbol(":") |> ignore
let* a = expr
symbol("=") |> ignore
let* t = expr
symbol(";") |> ignore
let* u = expr
pure(RLet(x,a,t,u))

}.label("let")
parser(input)
}
```