gilch / hebigo

蛇語(HEH-bee-go): An indentation-based skin for Hissp.
https://github.com/gilch/hissp
Mozilla Public License 2.0
24 stars 3 forks source link

implement !let #19

Closed gilch closed 5 years ago

gilch commented 5 years ago

How should it look in Hebigo? In Scheme it's like

(let ((foo 1)
      (bar 2))
  (frobnicate foo bar))

The same structure wrritten in Hebigo would be

!let:
    pass:
        foo: 1
        bar: 2
    frobnicate: foo bar

That doesn't look too bad, but I'd prefer to avoid using pass:. We could use a meaningful control word with arguments instead.

!let:
    :def:
        foo: 1
        bar: 2
    frobnicate: foo bar

Arc Lisp's let only binds one name.

!let: foo 1
    frobnicate: foo 2

This looks a lot cleaner, until you have to start nesting them.

!let: foo 1
    !let: bar 2
        frobnicate: foo bar

Still not that bad when there's only two, but indents can add up fast. I'd like to flatten them for the same reason we have elif in Python. Arc has with for this.

With multiple variables, there's also let* and letrec variants to consider. letrec looks hard without mutation. Clojure doesn't have it, but has letfn instead. But at that point you might just want a class.

Clojure has a powerful recursive destructuring syntax built into its let. Even with one binding pair, you could still bind multiple variables this way. Python has sequence destructuring only, but requires statements to do it. An example like

(x1, y1), (x2, y2) = rect
frobnicate(x1, x2, y1, y2)

Might look like

!let:
    pass:
        pass: x1 y1
        pass: x2 y2
    rect
    frobnicate: x1 x2 y1 y2

It's not so pretty without the brackets. With more meaningful control words,

!let: rect :as
    :,: :,: x1 y1
        :,: x2 y2
    frobnicate: x1 x2 y1 y2

More Clojurelike options

!let:
    foo :as
    :,: :,: x1 y1 :& rest
        :,: x2 y2
        :as all
    bar :as
    :=: "spam" food1  # associative destructuring
        "eggs" food2
    frobnicate: ...
gilch commented 5 years ago

Let has a bindings part and a body part. Scheme wraps the bindings part. Arc just has one binding, so no wrapping is required. With unpacking, maybe that's all we need. This makes it clear that it's let, and not let*.

!let: [1, 2]
    :,: foo bar
    frobnicate: foo bar

Or we could wrap the body part instead. Call it in like Haskell. This syntax could have let* semantics, which might be an important capability.

!let:
    foo 1
    bar 2
    :in: frobnicate: foo bar

Or we could assume the last expression is the body, and require an explicit !begin. Most of the statement macros haven't required this though.