Arc Forumnew | comments | leaders | submitlogin
Multi-var function literal shorthand
4 points by Zak 6168 days ago | 25 comments
I like the bracket/underscore syntax for function literals, but I think it could be better. I propose designating a sigil for binding variables to arguments within a bracket expression. The variables would be bound in the order they're used:

  ([- $foo $bar $baz] 3 2 1)
  => 0


4 points by nex3 6168 days ago | link

I don't like the idea of binding in the order they're used - it seems like it would make a lot of uses impossible (at least without making it more verbose than a fn would have been).

It seems better and more concise to somehow specify the variables with numbers, presumably with some sort of prefix:

  ([- $1 $2 $3] 3 2 1)
  ([- \1 \2 \3] 3 2 1)
  ([- #1 #2 #3] 3 2 1)
We could even bind the prefix character itself to the second argument, to make the two-arg case even shorter:

  ([+ _ #] 2 1)

-----

3 points by Zak 6168 days ago | link

I thought about using numbers like that too, but when I posted this I was too tired to remember why that might be a better idea. You're right - it would make more sense to use numbers, and if you're writing a function that's long enough that numbers would make it hard to read, use fn. Zero-indexed numbers with sigils to designate the place in the arg list are better.

  ([- $1 $2 $0] 1 3 2])
translates to

  ((fn ($0 $1 $2)
      (- $1 $2 $0))  1 3 2))
I don't like binding the sigil by itself to the second arg - it's starting to look like Perl. I'd even be in favor of eliminating the underscore and only using the sigil-number notation, with the special case that the sigil by itself is arg 0.

-----

1 point by dreeves 6164 days ago | link

That's what mathematica does:

(#1 - #2 - #3)&[3,2,1]

with # equivalent to #1.

(By the way, mathematica is all s-expressions (or rather, equivalently, McCarthy's m-expressions) underneath. You can call FullForm on any expression to strip away the syntactic sugar, eg, FullForm[a+b] => Plus[a,b]. Note the f[a,b] instead of (f a b). A nice thing about the commas is that you can throw in infix wherever it's convenient, like If[x>2+2, B, b] instead of If[Greater[x,Plus[2,2]], B, b]. The best lisp solution I've seen for that is curly sweet-expressions: http://www.dwheeler.com/readable/ )

-----

1 point by jimbokun 6168 days ago | link

([- (_ 1) (_ 2) (_ 0)] '(1 3 2))

What about that?

-----

1 point by Zak 6167 days ago | link

Adding parens to make it more "lispy"? I don't see the point, really. Also, why are you passing it a quoted list instead of the actual values you want?

-----

1 point by jimbokun 6167 days ago | link

Because this actually works in the current version of Arc. Maybe not quite as concise as the proposed syntax, but probably as close as you can get without adding syntax (at least that I can think of).

So you need to weigh the cost of adding more syntax to the language for the savings of "$1" vs. "(_ 1)".

-----

1 point by nex3 6167 days ago | link

Note that the latter is more than twice the size of the former, and that you'd also need to put the arguments to [] into a list prior to passing them into the fn. I'd say $1 is a pretty big win in terms of conciseness.

-----

3 points by shiro 6167 days ago | link

You might get some ideas from the discussion done for srfi-26, which is a kind of Scheme's answer for this type of shorthand notation. http://srfi.schemers.org/srfi-26/mail-archive/maillist.html

    (cut + 1 <>)   is (lambda (x) (+ 1 x))
    (cut + <> <>)  is (lambda (x y) (+ x y))
The possibility of allowing changing argument orders by numbering them were discussed, but eventually dropped, since you can always fall back to lambda (arc's fn). So making it general would just increase complexity without gaining much more expressive power. At least that was the consensus there. (Of course you can argue otherwise)

-----

1 point by almkglor 6164 days ago | link

#|create a lambda that doubles the given number|#

(= double-number [+ _ _])

-----

4 points by pg 6168 days ago | link

I'm still uncertain what to do about this. One reason is that the two-variable case is much more common than all the rest put together. So if I could think of a very clean way to represent a fn of two vars, that might be better.

-----

3 points by febeling 6168 days ago | link

This looks like the obvious next step:

[+ _ __]

But then you would have trouble denying this unreadable ugliness:

[+ _ __ ___ ____]

-----

6 points by ndanger 6168 days ago | link

What about combing it with nex3's idea?

[+ _1 _2 _3 ...]

_1 could be an alias for _

[+ _ _2]

-----

2 points by greatness 6168 days ago | link

I was thinking that _ would be merely _0, ie _1 would be the second argument. But at this point that's not really the question, the question is how will the interpreter know how many arguments the function takes?

Maybe something like:

  [[[+ _ _1 _2]]]
Could be shorthand for:

  (fn (_ _1 _2) (+ _ _1 _2))
But, then, what if you want to use square bracket notation inside of that? For example, what if you want to create a two paramater function that creates a one paramater function?

  (fn (x y) [+ x y _])
would be impossible. Unless you use currying or something on these special ones. Incidentally, a curry function:

  (def curry (f . args)
    (fn args2 (apply f (join args args2))))
Then you could simply do:

  (curry + var1 var2)
Assuming you already have the variables. For smarter currying it'll need to be done by the interpreter.

-----

1 point by are 6168 days ago | link

In this expression:

[+ _1 _2 _3]

... the interpreter already knows that _2 is the second anonymous parameter to the function literal, without parsing the "2" token.

So you could instead have:

[+ _ _ _]

... and specify the index only if you actually want to use the parameter outside of the function literal (and there, _ would still be shorthand for _1).

And BTW, this whole thing should work with rest parameters, so that in:

[+ _ . _]

... _2 will denote a list.

-----

1 point by robbie 6168 days ago | link

"... the interpreter already knows that _2 is the second anonymous parameter to the function literal, without parsing the "2" token."

it could be the third parameter.

-----

1 point by are 6168 days ago | link

We may be talking past each other.

I'm just saying that if you have several anonymous parameters in the parameter list of the function literal, it is just as well that you represent them with the same token _within the parameter list_. The parameter list is ordered, so the interpreter knows which is which anyway. If you then want to refer to one of the anonymous parameters _outside of the parameter list_, you use a token WITH an index, to identify the position of the particular anonymous parameter in the parameter list.

-----

1 point by Xichekolas 6167 days ago | link

I thought the whole point of the bracketed function literal syntax was that you don't have a parameter list...

-----

1 point by nex3 6168 days ago | link

I thought about suggesting this as one of the prefix characters, but underscores just don't feel very prefixy.

-----

1 point by Xichekolas 6167 days ago | link

One thing about underscores is that they kind of stand out... but I agree, they are a bit weird. Other options could be (@, $, %, *, ?)

-----

1 point by chl 6168 days ago | link

In K, {x+y+z} equals {[a;b;c]a+b+c}.

-----

3 points by mec 6155 days ago | link

What about having [...] expand to (fn (_ . __) ...) so that it would still keep its simplicity and be identical when only passing one argument, but when adding multiple arguments you can access them through __.

-----

2 points by dreeves 6164 days ago | link

Proposal: double underscore, __, should mean the list of arguments.

  ([cons _ (cddr __)] 1 2 3) => (1 3)

-----

2 points by mec 6163 days ago | link

After thinking about it for a while this seems like the best approach. I really liked the suggestion to use '*' for the list but '__' seems the most natural after that.

-----

1 point by chrisdone 6166 days ago | link

I like the \n syntax, it reminds me of regexes and people are generally familiar with how that works.

    ([- \2 \1] 5 10)
    => 5

-----

1 point by cooldude127 6168 days ago | link

eww, those $'s remind me of php. but i agree, it would be nice if variables in these functions could be used more than once.

-----