Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggestion: let-binding #46

Open
technomancy opened this issue Sep 5, 2016 · 7 comments
Open

Suggestion: let-binding #46

technomancy opened this issue Sep 5, 2016 · 7 comments

Comments

@technomancy
Copy link
Contributor

Currently let-binding works like Clojure instead of the traditional Scheme/CL style:

(let ((a b) (f)
      c 3)
      \return a, b, c)

In general I think this is nice; however, the difference between Clojure and l2l is that Clojure doesn't support multiple return values, so (let [[a b] (f)] ...) will destructure the return value of f into two locals. Since we have to support multiple return values, there is some ambiguity about whether binding to two locals would work with multiple values or whether it would destructure.

Personally I am of the opinion that a data structure on the left-hand side should correspond with destructuring a data structure on the right-hand side. So how would it be possible to bind to multiple values? I think it could be done by reintroducing Scheme-style let which has an extra level of parens around each binding group:

(let ((a b (f))
      (c (+ 32 1))
      ((d e) (list 1 2)))
  ...)

It is a little more syntactically noisy, but I believe it is more consistent and flexible if we want to allow the goals of both binding to multiple values and supporting destructuring of data structures.

What do you think?

@meric
Copy link
Owner

meric commented Sep 6, 2016

Hmm. The let-binding currently does not destructure -

(let ((a b) (f)
      c 3)
      \return a, b, c)

If (f), a function call, returns two values, they will be assigned to a, b respectively.

Do you think we ought to implement destructuring?

@technomancy
Copy link
Contributor Author

technomancy commented Sep 6, 2016 via email

@meric
Copy link
Owner

meric commented Sep 6, 2016

In that case, let's go with your syntax. Thinking only of Lua when I wrote the let-macro I did not think of destructuring. You're right it will make it much tidier.

@meric
Copy link
Owner

meric commented Sep 12, 2016

Perhaps, we could implement pattern matching:

(let (
      {a, b} {(f)})
      c (+ 32 1)
      (list d e) (list 1 2)
      {a=f} {a=1})
  ...)

The pattern matching would only work for list, vector, and plain tables.

We can define an interface for implementing pattern-matching for the let macro for other custom data structures

It looks like a good challenge...

@technomancy
Copy link
Contributor Author

I think I follow what you're saying here, but I would try to avoid the use of the term "pattern matching" for this as that already has a very specific meaning that could be very confusing because it overlaps with this in some ways: http://c2.com/cgi/wiki?PatternMatching

I think l2l should have traditional pattern matching too, but what you're describing in the snippet above is more commonly referred to as destructuring.

And it sounds like specifically you're saying that on line two if you want to bind to a function that returns multiple values, wrapping that in a table would be a better way to deal with the syntactic ambiguity than requiring every name/value pair to be delimited by parens a la Scheme's let form. Is that what you mean here? In that case I agree.

Naively it looks like this style would require more allocation overhead because it looks like a table is being constructed, but since that is statically determinable at compile time, it's easy to detect that case and bind directly to the values being returned so that the {(f)} table never actually exists.

@meric
Copy link
Owner

meric commented Sep 13, 2016

Ok. Yes that's exactly it.

meric added a commit that referenced this issue Nov 6, 2016
@meric
Copy link
Owner

meric commented Nov 6, 2016

The destructuring is only implemented for tables and list currently.

Example test cases:

function test_let0()
  local src = assert_exec_equal([[
    (let (
      {a, b, hello=c, world={f}} {1, 2, hello=4, world={5}}
      d 3
      e 4)
      \return a, b, c, d, e, f)
    ]],
    1, 2, 4, 3, 4, 5)
end

function test_let()
  local src = assert_exec_equal([[
    (let (
      {a, b, c, d, e} {unpack({1, 2, 3, 4, 5})}
      x {}
      {f} x)
      \return a, b, c, d, e)
    ]],
    1, 2, 3, 4, 5)
end

function test_let3()
  local src = assert_exec_equal([[
    @import iterator
    (let (
      (a b c) {1,2,3}
      (d e f) '(4 5 (+ 6 8))
      (g h i) `(4 5 ,(+ 6 8)))
      \return a, b, c, d, e, f, g, h, i)
    ]],
    1, 2, 3, 4, 5, 14, 4, 5, 14)
end

meric added a commit that referenced this issue Nov 6, 2016
meric added a commit that referenced this issue Nov 7, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants