Apologies in advance if this turns out to be a dumb question because it saeems to obvious not to have been thought of, but do you really need let and with? Isn't let just the degenerate case of with? It reminds me a little of the . and -> dereferencing operators in C; if the compiler knows enough to complain, it should know enough to just fix it for me.
let is a convenience for binding one variable; it allows you to leave out the parentheses of with. Do you need them both? No. But they're nice. (After all, you don't need either; both are equivalent to function calls.)
... initially, it would seem that you can distinguish the two by whether there's a ( following the with, but you might get into trouble:
(let (x y) mylist
(pr x y)
where let is followed by a (. mmm. I'm a still quite unfamiliar with arc or lisp. Can anyone give an example in which interchanging the "let" for a "with" or visa versa changes the meaning of the function?
You yourself gave an example. The fact that Arc allows destructuring (that is, writing (let (a b) lst ...)) means that the presence of a list is not an adequate indicator of whether you're letting or withing. And without that, how do you know when you're done defining variables? If you don't parenthesize them, they could go on forever. The only other option is to remove the implicit do in let/with; that way, only the last statement would be run, and the first n would be variable bindings; to run more than one, you would use do.
That said, it might be good to remove the implicit 'do anyways, since as pg has said, an explicit 'do highlights non-functional code. Combining 'let and 'with would just be a convenient side effect.
P.S. If I am not mistaken I think this would also allow you to do destructuring as well...
This is actually fairly simple to write (first defining butlast for convenience):
(def butlast (seq)
" Returns every element of `seq' but the last one.
See also [[last]] [[cut]] "
(cut seq 0 -1))
(mac letn parms
" Simultaneously ssigns the given (unparenthesized) local variables in the
one-statement body.
See also [[let]] [[with]] [[letns]]"
`(with ,(butlast parms) ,(last parms)))
(mac letns parms
" Sequentially assigns the given (unparenthesized) local variables in the
one-statement body.
See also [[let]] [[with]] [[letn]]"
`(withs ,(butlast parms) ,(last parms)))
Then letn is like with, but unparenthesized, and letns is like withs, but unparenthesized. (letn = "let n variables".) And yes, destructuring works.
Heck no. We can do this ourselves. Remember, the textual transformation to transform let's is just that: a textual translation. It should be possible to create an automated translation tool (based off raymyers' treeparse) that will handle this for us.
Let the old version of 'let be Arc2Let, and the proposed new let be Arc2.7Let. Let the old version of 'with and 'withs be Arc2With and Arc2Withs, respectively. We need to determine if each Arc2Let in the source is composed of a single expression in the body. If it is, we leave it as-is. If it isn't, we simply replace it with Arc2.7As.
For each Arc2With we determine if the body is composed of a single expression. If it is, we replace it with Arc2.7Let, removing the parens around the Arc2With bindings. If it isn't, we leave it as-is. Ditto for Arc2Withs, replacing it with Arc2.7Lets.
We define an expression simply as a sequence of whitespace, base-expression, and whitespace. We define whitespace as being either a comment (either #||# or ;) or ordinary whitespace.
A base-expression is simply a symbol, a number, a character, a string, a quote-expression, a comma and comma-at expression, or a list. A quote-expression is simply the ' quote or ` backquote character followed by maybe whitespace, followed by an expression; comma and comma-at are defined similarly. A list is composed of an opening parenthesis followed by many expressions, followed by a closing parens.
We can determine if a 'let form has several expressions by defining two variants of a 'let form. An Arc2Let is composed of ( <maybe whitespace> let <whitespace> <expression> <expression> <expression> [many <expression>]), and that we have to transform to Arc2.7As (by filtering out the let expression using treeparse 'filt). An Arc2.7LetCompatible is composed of just (let <expression> <expression> <expression>), which we do not transform.
----
Of course, this does represent a veritable fork of the Arc code ^^.
I would leave with and withs alone, so that we have the option of the implicit do (also because it makes it easier to implement given/Anarki-let :P). And why not use a code-tree-walker if we want to do this—isn't that the point of Lisp?
Right, comments. Just a little important, aren't they? :P
You raise a good point... it's the same number of parentheses either way. But in that case, why not just have let and lets (as given(s)), and be done with it?
Edit: as an aside, given that pg has said that he'll modify Arc as if there's nobody else programming in it, and that he does not appear to be using Anarki, eventually when Arc3 does come around, it is very possible that Anarki will be incompatible with Arc3. We may very well need to build a converter program in the future to transform Arc2-base Anarki to Arc3-base Anarki, so my abstract nonsense may very well be necessary in the future.
I've been programming with given(s) for a little while now, and I really like it. Can't say why I'm so vehement, but it's definitely a Good Thing™. Thank you for bringing this up again (and a "thank you" to aidenn0, if he/she is still reading these fora, for suggesting this in the first place).
An interesting bit about 'givens is that it makes functional programming in an imperative style almost seamless:
(givens f (car n)
v f!v
_ (prn v) ; v isn't being set properly in some cases for some reason...
l (combinatorics f v)
_ (prn l) ; debugprint
(is l 'undef))
The difference between given and let/with is that you cannot have more than one statement in the body of the given. For instance:
arc> (given (a b) (list 1 2)
(prn "Here")
(pr a b))
Error: "Can't understand fn arg list \"Here\""
What's happening here is that given is trying to bind (prn "Here") to (pr a b), but (naturally) can't bind a string; it takes the first n args, where n is the largest even number less than or equal to the number of arguments provided, and interprets them as variables. You must, therefore, write
arc> (given (a b) (list 1 2)
(do
(prn "Here")
(pr a b))
Here
121
.
Also, I highly recommend switching to Anarki. It's got bugfixes and enhancements galore (and even runs on the newest version of mzscheme).
It's the only "drawback", if such it is. I feel that since you need extra parentheses for either the body or the variables, it's better to wrap the body in a do, since that's less common.