Another way to avoid clumsy lambdas, while adding new functionality
The tentatively proposed idea here is using dollar signed expressions to define 'bills'. A bill object is essentially an expression which can be evaluated any number of times, potentially in different scopes. The following expression [a bill literal] would be pointless, but would define a bill that always evaluates to 1. $1 So, eval($1)==1. Some better examples... * assign a bill to `a` so that `a` will evaluate to the value of the name `foo` any time that `a` is evaluated, in the scope of that evaluation a = $foo * as above, but always plus one a = $foo + 1 * make `a` a bill that evaluates to the value of the name `foo` at the time that `a` is evaluated, in that scope, plus the value of `bar` **at the time and in the scope of the assignment to `a`** a = $foo + bar Note. Similarly to mixing floats with ints, any expression that contains a bill evaluates to a bill, so if `a` is a bill, `b=a+1` makes `b` a bill too. Passing a bill to eval should be the obvious way to get the value. The point? It allows functions to accept bills to use internally. The function would specify any names the bill can reference in the function's API, like keywords. def f(b): # the bill arg `b` can reference `item` for item in something: if eval(b): return True f($item < 0) You could also use a function call, for example `$foo()` would evaluate to a bill that evaluates to a call to `foo` in the scope and at the time of any evaluation of the bill. I've no idea if this is even possible in Python, and have no hope of implementing it, but thought I'd share :)
Only problem is that it looks a tad perlish... On Tue, Mar 4, 2014 at 5:09 PM, Carl Smith <carl.input@gmail.com> wrote:
The tentatively proposed idea here is using dollar signed expressions to define 'bills'. A bill object is essentially an expression which can be evaluated any number of times, potentially in different scopes.
The following expression [a bill literal] would be pointless, but would define a bill that always evaluates to 1.
$1
So, eval($1)==1.
Some better examples...
* assign a bill to `a` so that `a` will evaluate to the value of the name `foo` any time that `a` is evaluated, in the scope of that evaluation
a = $foo
* as above, but always plus one
a = $foo + 1
* make `a` a bill that evaluates to the value of the name `foo` at the time that `a` is evaluated, in that scope, plus the value of `bar` **at the time and in the scope of the assignment to `a`**
a = $foo + bar
Note. Similarly to mixing floats with ints, any expression that contains a bill evaluates to a bill, so if `a` is a bill, `b=a+1` makes `b` a bill too. Passing a bill to eval should be the obvious way to get the value.
The point? It allows functions to accept bills to use internally. The function would specify any names the bill can reference in the function's API, like keywords.
def f(b): # the bill arg `b` can reference `item` for item in something: if eval(b): return True
f($item < 0)
You could also use a function call, for example `$foo()` would evaluate to a bill that evaluates to a call to `foo` in the scope and at the time of any evaluation of the bill.
I've no idea if this is even possible in Python, and have no hope of implementing it, but thought I'd share :)
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Ryan If anybody ever asks me why I prefer C++ to C, my answer will be simple: "It's becauseslejfp23(@#Q*(E*EIdc-SEGFAULT. Wait, I don't think that was nul-terminated."
On 04Mar2014 17:57, Ryan Gonzalez <rymg19@gmail.com> wrote:
Only problem is that it looks a tad perlish...
Of shellish. But worse, does nothing even remotely loke what that does in perl or shell (or basic or...). I was going to remark that in my mind I would always see: a = $foo + 1 and (1) mentally bind the "$" to "foo" alone, not treat it as a prefix to the whole expression and (2) expect "a" to evaluate right now, regardess. And then follow up that remark with a strong preference for forming the who expression inside a clear construct so you know at the outself that it is something for evaluation later. And then realised that is already spelt "lambda". I do take the point that this "bill" idea is meant to grant access to the local scope when used. Cheers, -- Cameron Simpson <cs@zip.com.au> Take a perfectly parallel uniform plank of constant density. Balance it exactly at its centre on a frictionnless pivot. Place the hog at a known distance from this pivot. Now add stones on the other end at exactly the same distance until the plank is perfectly horizontal as measured by a precision tiltmeter. Now guess the weight of the stones. - Bob Newhart on weighing a hog
On Tue, Mar 4, 2014 at 4:06 PM, Cameron Simpson <cs@zip.com.au> wrote:
On 04Mar2014 17:57, Ryan Gonzalez <rymg19@gmail.com> wrote:
Only problem is that it looks a tad perlish...Of shellish.
But worse, does nothing even remotely loke what that does in perl or shell (or basic or...).
I was going to remark that in my mind I would always see:
a = $foo + 1
and (1) mentally bind the "$" to "foo" alone, not treat it as a prefix to the whole expression and (2) expect "a" to evaluate right now, regardess.
I think I could be +0 for a bit different spelling that *is* actually shell-ish. I.e. as a way to handle snippets, this doesn't seem so bad: foo = 1 a = $(foo + 1) b = eval(a) * 6 c = $(foo + a/2) print(c, eval(c)) # <thunk object at NNNN> 2 print(a + 1) # TypeError, incompatible types for '+' operation, thunk and int That is, the whole "thunk" or really just "code-snippet" could be a shorthand way to refer to a segment of (dynamically scoped, basically) code we might use in different places. It's a lot like Ruby blocks, of course, but the difference is that it's not only in the final position of a call, but anywhere anything might be used or passed. On the other hand, this isn't *really* different from just putting snippets in strings like we can do now. The one difference I envision is that maybe these "thunks" would be completely resolved by an eval(), unlike a string. That is, 'c' is a thunk, but it's a thunk that contains another thunk. So the eval() function becomes a recursive-unthunk. Well, maybe I'm -0 rather than +0. I'm definitely -1 on the "thunks taint all expressions" approach suggested initially, which seems far too magical and implicit. -- Keeping medicines from the bloodstreams of the sick; food from the bellies of the hungry; books from the hands of the uneducated; technology from the underdeveloped; and putting advocates of freedom in prisons. Intellectual property is to the 21st century what the slave trade was to the 16th.
On 04Mar2014 17:23, David Mertz <mertz@gnosis.cx> wrote:
On Tue, Mar 4, 2014 at 4:06 PM, Cameron Simpson <cs@zip.com.au> wrote:
On 04Mar2014 17:57, Ryan Gonzalez <rymg19@gmail.com> wrote:
Only problem is that it looks a tad perlish...Of shellish.
But worse, does nothing even remotely loke what that does in perl or shell (or basic or...). [...] I think I could be +0 for a bit different spelling that *is* actually shell-ish. I.e. as a way to handle snippets, this doesn't seem so bad:
foo = 1 a = $(foo + 1)
Definitely nicer. Still irrationally uncomfortable about the "$" though. A thought, though it could break existing code (and nested tuples, alas): a = (( foo + 1 )) Still, what does this mean? a = 3 + (( foo + 1 )) I think that would need to be a syntax error, because I can't see it being anything except nonsense otherwise. That makes me think the (()) or $() is in the wrong place. Maybe: a := foo + 1 Hmm. hasn't that one already come up?
b = eval(a) * 6
This makes me unhappy. Eval parses a string and runs it, currently. I would _want_ to read that as "compute a, then eval() the result". I would prefer the occurence of "a" in an expression to cause it to be evaluated then, much as happens already. It does mean that there would be "silent" evaluation bombs lurking in expressions. For example, given a doubly linked list (to make the "reference" stuff glaring): node.right = node2 node2.left = node If node or node2 were thunks, this would be surprising to the reader. Of course, we can surprise the reader already with __getattr__ etc. But I think it would be better if: b = a just made "b" a reference to the same thunk as "a". Since I'm cavilling about "eval", how about just treating thunks like other functions (which is how we spell code to be run later presently) and have them be callables. So your "b = eval(a) * 6" would just be: b = a() * 6 That way it is glaringly obvious that "a" happens now, rather than earlier. Cheers, -- Cameron Simpson <cs@zip.com.au> Disease and deprivation stalk the land like two giant.... stalking things. - Rowan Atkinson, _Black Adder the Third_
On Tue, Mar 4, 2014 at 5:46 PM, Cameron Simpson <cs@zip.com.au> wrote:
foo = 1 a = $(foo + 1) Definitely nicer. Still irrationally uncomfortable about the "$" though. A thought, though it could break existing code (and nested tuples, alas):
a = (( foo + 1 ))
That looks like unresolvable ambiguity to me. I confess that I am more comfortable with '$(...)' because I'm one of those folks who actually likes bash, and uses that spelling often over there (where the meaning isn't the *same* as this, but is enough similar for the meaning to carry over)
Still, what does this mean? a = 3 + (( foo + 1 )) I think that would need to be a syntax error, because I can't see it being anything except nonsense otherwise.
In my example I made it a TypeError on the grounds that a thunk and and int aren't things that can be added together. That makes more sense to me than a SyntaxError, since a thunk is a perfectly good expression (or would be if the feature is added). Besides, you really need to allow thunks in expressions, since sometimes they make sense as values to operate on, e.g. (as I wrote before): print(foo, 'foo', $(foo)) # Using my bash-like syntax # 1 foo <thunk object ...> You could also, for example, have a type of object that knew how to operate on a thunk with operators: class Thunker(object): def __radd__(self, other): if isinstance(other, thunk): ... return something sensible ... thunker = Thunker() a = thunker + $(foo + bar)
b = eval(a) * 6 This makes me unhappy. Eval parses a string and runs it, currently. I would _want_ to read that as "compute a, then eval() the result".
b = a() * 6
I don't think I would want a thunk to be *exactly* a callable. That feels wrong to me. But I can see that overloading the meaning of eval() to operate on either a string or a thunk might feel odd. *Except* that eval() is *already* overloaded in a similar manner:
exp = compile("1+2", "<string>", "eval") exp, eval(exp) (<code object <module> at 0x100730ae0, file "<string>", line 1>, 3)
However, what if it was spelled differently from eval(), e.g.: b = unthunk(a) * 6 OK, I don't actually like that name, but you see what I mean that other names are perfectly possible. -- Keeping medicines from the bloodstreams of the sick; food from the bellies of the hungry; books from the hands of the uneducated; technology from the underdeveloped; and putting advocates of freedom in prisons. Intellectual property is to the 21st century what the slave trade was to the 16th.
On 04Mar2014 18:36, David Mertz <mertz@gnosis.cx> wrote:
On Tue, Mar 4, 2014 at 5:46 PM, Cameron Simpson <cs@zip.com.au> wrote:
foo = 1 a = $(foo + 1) Definitely nicer. Still irrationally uncomfortable about the "$" though. A thought, though it could break existing code (and nested tuples, alas):
a = (( foo + 1 ))
That looks like unresolvable ambiguity to me.
Me too.
I confess that I am more comfortable with '$(...)' because I'm one of those folks who actually likes bash, and uses that spelling often over there (where the meaning isn't the *same* as this, but is enough similar for the meaning to carry over)
I write a lot of shell scripts too.
Still, what does this mean? a = 3 + (( foo + 1 )) I think that would need to be a syntax error, because I can't see it being anything except nonsense otherwise.
In my example I made it a TypeError on the grounds that a thunk and and int aren't things that can be added together. That makes more sense to me than a SyntaxError, since a thunk is a perfectly good expression (or would be if the feature is added).
Besides, you really need to allow thunks in expressions, since sometimes they make sense as values to operate on, e.g. (as I wrote before):
print(foo, 'foo', $(foo)) # Using my bash-like syntax # 1 foo <thunk object ...>
You could also, for example, have a type of object that knew how to operate on a thunk with operators:
class Thunker(object): def __radd__(self, other): if isinstance(other, thunk): ... return something sensible ...
thunker = Thunker() a = thunker + $(foo + bar)
Hmm, yes. Ok. We should get TypeErrors for free except where some operator has been designed.
b = eval(a) * 6 This makes me unhappy. Eval parses a string and runs it, currently. I would _want_ to read that as "compute a, then eval() the result".
b = a() * 6
I don't think I would want a thunk to be *exactly* a callable. That feels wrong to me.
Can you be more precise? It seems like exactly what's going on, semanticly. Except that there's no notion of parameters.
But I can see that overloading the meaning of eval() to operate on either a string or a thunk might feel odd. *Except* that eval() is *already* overloaded in a similar manner:
exp = compile("1+2", "<string>", "eval") exp, eval(exp) (<code object <module> at 0x100730ae0, file "<string>", line 1>, 3)
Hmm. The lack of params makes eval a better match then.
However, what if it was spelled differently from eval(), e.g.:
b = unthunk(a) * 6
OK, I don't actually like that name, but you see what I mean that other names are perfectly possible.
How about: a.eval() Don't make a new public function, give thunks a method. Are we still intending thunks to be effectively a calling-scope closure? That seems subject to being fragile: you can define a thunk far from where it is called/evaled and therefore keeping it in sync with the user's scope is less solid. Cheers, -- Cameron Simpson <cs@zip.com.au> Steve is going for the pink ball - and for those of you who are watching in black and white, the pink is next to the green. - Snooker commentator 'Whispering' Ted Lowe
On Tue, Mar 4, 2014 at 7:49 PM, Cameron Simpson <cs@zip.com.au> wrote:
b = eval(a) * 6 This makes me unhappy. Eval parses a string and runs it, currently. I would _want_ to read that as "compute a, then eval() the result".
b = a() * 6
I don't think I would want a thunk to be *exactly* a callable. That feels wrong to me.
Can you be more precise? It seems like exactly what's going on, semanticly. Except that there's no notion of parameters.
As *I* am thinking of it, a "thunk" is really just like a C macro. Not even really like a Lisp macro. So it's not like a function in that it doesn't define a scope, doesn't have a call stack, etc. It's just "pretend I typed these other literal symbols here." Maybe I should stop calling the idea a thunk, and just call it a macro. However, I'm pulled in several directions here. On the one hand, if it really is essentially the same thing as a code object, I'm not convinced we actually need syntax for it. That is, what's really the point of having a = $(expr) # or `expr`, or `(expr), or c"expr" If it's simply a slightly shorter way of spelling: a = compile(expr, "<string>", "eval") One thing about a code object is that it is NOT callable. Which is to say, that it's not usable as a callback. If this other thing (thunk, macro, whatever) is callable, it's usable as a callback; albeit a callback with zero arguments, which again is of limited purpose, and not really that much shorter than lambda, e.g.: b = Button(text="click me", command=lambda: print("Clicked!")) Under the callable-thunk spelling, we might have: b = Button(text="click me", command=$(print("Clicked!"))) All of this is moving me towards the -0, or even -0.5 on my own idea (even though I like my spelling in principle, but I'm having trouble seeing why I actually need it). a.eval()
Don't make a new public function, give thunks a method.
Sure, that's a nice enough spelling, but what do we really get over code objects there? Actually, here's some short and trivial code in existing Python that seems to already get *everything* we've discussed in this thread:
class Thunk(object): ... def __init__(self, s): ... self.code = compile(s, "<thunk>", "eval") ... def __call__(self, **kws): ... return eval(self.code, globals(), kws) ... eval = __call__ ... a = Thunk("foo + bar") for foo, bar in enumerate(range(5,8)): ... a() ... 5 7 9 for foo, bar in enumerate(range(5,8)): ... a.eval() ... 5 7 9 a(foo=10, bar=20) 30
Other than using the '$' which I'm mixed about anyway, and saving two quote symbols, my Thunk class seems to be exactly what I had proposed (with Cameron's variations). -- Keeping medicines from the bloodstreams of the sick; food from the bellies of the hungry; books from the hands of the uneducated; technology from the underdeveloped; and putting advocates of freedom in prisons. Intellectual property is to the 21st century what the slave trade was to the 16th.
On Tue, Mar 04, 2014 at 10:54:30PM -0800, David Mertz wrote:
As *I* am thinking of it, a "thunk" is really just like a C macro. Not even really like a Lisp macro. So it's not like a function in that it doesn't define a scope, doesn't have a call stack, etc. It's just "pretend I typed these other literal symbols here." Maybe I should stop calling the idea a thunk, and just call it a macro.
I'm intrigued by this suggestion. I think that what I'm really after is two things: lazy evaluation, and dynamic scoping. I think a macro would give us both. But hasn't Guido said No Macros at some point?
However, I'm pulled in several directions here. On the one hand, if it really is essentially the same thing as a code object, I'm not convinced we actually need syntax for it. That is, what's really the point of having
a = $(expr) # or `expr`, or `(expr), or c"expr"
If it's simply a slightly shorter way of spelling:
a = compile(expr, "<string>", "eval")
You can't write it like that. You have to wrap the expression is quotes and turn it into a string: a = compile("expr", "<string>", "eval") which means you lose syntax highlighting. Perhaps the ability to get syntax highlighting is not sufficient to justify this idea. Another disadvantage: the ever-present temptation to pass a user-generated string to compile: a = compile(template % some_string, "<string>", "eval") If some_string came from an untrusted source, you are now the proud owner of a brand new code injection vulnerability. But with syntax, you cannot generate a thunk/macro except from code you write yourself. It's still code written by you, it's just that evaluation is delayed. -- Steven
On Wed, Mar 5, 2014 at 3:43 AM, Steven D'Aprano <steve@pearwood.info> wrote:
That is, what's really the point of having
a = $(expr) # or `expr`, or `(expr), or c"expr"
If it's simply a slightly shorter way of spelling:
a = compile(expr, "<string>", "eval")
You can't write it like that. You have to wrap the expression is quotes and turn it into a string:
a = compile("expr", "<string>", "eval")
True, I misspelled my example. Or maybe my 'expr' implicitly meant string_expr :-). But the basic point that the literal for a "thunk" is already essentially available with compile() remains.
which means you lose syntax highlighting. Perhaps the ability to get syntax highlighting is not sufficient to justify this idea.
There's no reason you even NEED to lose syntax highlighting. A code editor could perfectly well have a highlight rule that strings inside compile() calls get highlighted. Sure, that's a pretty special language mode, but there's nothing that an editor couldn't do in principle.
Another disadvantage: the ever-present temptation to pass a user-generated string to compile:
a = compile(template % some_string, "<string>", "eval")
If some_string came from an untrusted source, you are now the proud owner of a brand new code injection vulnerability. But with syntax, you cannot generate a thunk/macro except from code you write yourself. It's still code written by you, it's just that evaluation is delayed.
The literal hardly saves you from injection attacks. I could write this too under the proposed idea: foo = get_string_from_attacker() a = $(foo) b = a.eval() Now one can say "don't do that!" ... but that advice applies just as well to 'compile(unsafe_string, ...)' -- Keeping medicines from the bloodstreams of the sick; food from the bellies of the hungry; books from the hands of the uneducated; technology from the underdeveloped; and putting advocates of freedom in prisons. Intellectual property is to the 21st century what the slave trade was to the 16th.
On Thu, Mar 6, 2014 at 8:20 AM, David Mertz <mertz@gnosis.cx> wrote:
The literal hardly saves you from injection attacks. I could write this too under the proposed idea:
foo = get_string_from_attacker() a = $(foo) b = a.eval()
Now one can say "don't do that!" ... but that advice applies just as well to 'compile(unsafe_string, ...)'
That'll just be like doing: b = foo So it's still safe. That's the point. ChrisA
On Wed, Mar 5, 2014 at 1:31 PM, Chris Angelico <rosuav@gmail.com> wrote:
On Thu, Mar 6, 2014 at 8:20 AM, David Mertz <mertz@gnosis.cx> wrote:
The literal hardly saves you from injection attacks. I could write this too under the proposed idea:
foo = get_string_from_attacker() a = $(foo) b = a.eval()
Now one can say "don't do that!" ... but that advice applies just as well to 'compile(unsafe_string, ...)'
That'll just be like doing:
b = foo
So it's still safe. That's the point.
Doh! You are right. The literal does make it somewhat harder to shoot yourself in the foot with code injection, I had a thinko there. Still, advice in the docs not to do 'compile(untrusted_string, ...)' feels like it pretty much does what we actually need. -- Keeping medicines from the bloodstreams of the sick; food from the bellies of the hungry; books from the hands of the uneducated; technology from the underdeveloped; and putting advocates of freedom in prisons. Intellectual property is to the 21st century what the slave trade was to the 16th.
On Tue, Mar 4, 2014 at 8:36 PM, David Mertz <mertz@gnosis.cx> wrote:
On Tue, Mar 4, 2014 at 5:46 PM, Cameron Simpson <cs@zip.com.au> wrote:
foo = 1 a = $(foo + 1) Definitely nicer. Still irrationally uncomfortable about the "$" though. A thought, though it could break existing code (and nested tuples, alas):
a = (( foo + 1 ))
That looks like unresolvable ambiguity to me. I confess that I am more comfortable with '$(...)' because I'm one of those folks who actually likes bash, and uses that spelling often over there (where the meaning isn't the *same* as this, but is enough similar for the meaning to carry over)
But this is Python, which is 10x better. And besides, that syntax gives me GNU make nightmares.
Still, what does this mean?
a = 3 + (( foo + 1 )) I think that would need to be a syntax error, because I can't see it being anything except nonsense otherwise.
I see it as: Create an anonymous function object that adds foo to 1. Then, try and add 3 to that resulting object(which obviously would fail). It'd be kind of like: a = 3 + (lambda: foo+1) a.k.a: def myfunc(): return foo+1 a = 3+myfunc or(somewhat clearer in C++): SomeType a = 3 + [&]() { return foo+1; }; It's a bit more obvious of the error in the C++ example(or, at least to me). ...
-- Keeping medicines from the bloodstreams of the sick; food from the bellies of the hungry; books from the hands of the uneducated; technology from the underdeveloped; and putting advocates of freedom in prisons. Intellectual property is to the 21st century what the slave trade was to the 16th.
-- Ryan If anybody ever asks me why I prefer C++ to C, my answer will be simple: "It's becauseslejfp23(@#Q*(E*EIdc-SEGFAULT. Wait, I don't think that was nul-terminated."
The idea was never to start passing complex expressions around the place. It was just to allow a function to take an expression containing about one or two names, and evaluate the expression internally, so this...
func(lambda foo: foo > 0)
...can become...
func($foo > 0)
from macropy.quick_lambda import macros, f, _ map(f[_ + 1], [1, 2, 3]) [2, 3, 4]
From an external API, it seems exactly the same. From an internal point of view, you have to pass *foo* explicitly to the function you capture, but I'd argue that that's probably a good thing in the majority of cases. There are cases where you'd want to "evaluate the expression internally" to automagically inject identifiers into the scope of the expression, but those are few and far between.
On Wed, Mar 5, 2014 at 11:44 AM, Ryan Gonzalez <rymg19@gmail.com> wrote:
On Tue, Mar 4, 2014 at 8:36 PM, David Mertz <mertz@gnosis.cx> wrote:
On Tue, Mar 4, 2014 at 5:46 PM, Cameron Simpson <cs@zip.com.au> wrote:
foo = 1 a = $(foo + 1) Definitely nicer. Still irrationally uncomfortable about the "$" though. A thought, though it could break existing code (and nested tuples, alas):
a = (( foo + 1 ))
That looks like unresolvable ambiguity to me. I confess that I am more comfortable with '$(...)' because I'm one of those folks who actually likes bash, and uses that spelling often over there (where the meaning isn't the *same* as this, but is enough similar for the meaning to carry over)
But this is Python, which is 10x better. And besides, that syntax gives me GNU make nightmares.
Still, what does this mean?
a = 3 + (( foo + 1 )) I think that would need to be a syntax error, because I can't see it being anything except nonsense otherwise.
I see it as:
Create an anonymous function object that adds foo to 1. Then, try and add 3 to that resulting object(which obviously would fail). It'd be kind of like:
a = 3 + (lambda: foo+1)
a.k.a:
def myfunc(): return foo+1 a = 3+myfunc
or(somewhat clearer in C++):
SomeType a = 3 + [&]() { return foo+1; };
It's a bit more obvious of the error in the C++ example(or, at least to me).
...
-- Keeping medicines from the bloodstreams of the sick; food from the bellies of the hungry; books from the hands of the uneducated; technology from the underdeveloped; and putting advocates of freedom in prisons. Intellectual property is to the 21st century what the slave trade was to the 16th.
-- Ryan If anybody ever asks me why I prefer C++ to C, my answer will be simple: "It's becauseslejfp23(@#Q*(E*EIdc-SEGFAULT. Wait, I don't think that was nul-terminated."
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On 2014-03-05 01:46, Cameron Simpson wrote:
On 04Mar2014 17:23, David Mertz <mertz@gnosis.cx> wrote:
On Tue, Mar 4, 2014 at 4:06 PM, Cameron Simpson <cs@zip.com.au> wrote:
On 04Mar2014 17:57, Ryan Gonzalez <rymg19@gmail.com> wrote:
Only problem is that it looks a tad perlish...Of shellish.
But worse, does nothing even remotely loke what that does in perl or shell (or basic or...). [...] I think I could be +0 for a bit different spelling that *is* actually shell-ish. I.e. as a way to handle snippets, this doesn't seem so bad:
foo = 1 a = $(foo + 1)
Definitely nicer. Still irrationally uncomfortable about the "$" though.
A thought, though it could break existing code (and nested tuples, alas):
a = (( foo + 1 ))
That's not a tuple. It's equivalent to: a = foo + 1 [snip]
On 05Mar2014 04:06, MRAB <python@mrabarnett.plus.com> wrote:
On 2014-03-05 01:46, Cameron Simpson wrote:
a = $(foo + 1)
Definitely nicer. Still irrationally uncomfortable about the "$" though. A thought, though it could break existing code (and nested tuples, alas): a = (( foo + 1 ))
That's not a tuple. It's equivalent to: a = foo + 1
I know that. I should have said: though the below looks nice, in more complicated forms it fights with tuples, eg: ((1,2,3),(4,5,6)) (((1,2,3),(4,5,6))) :-( -- Cameron Simpson <cs@zip.com.au> Theoretical Physicist,N.:A physicist whose existence is postulated, to make the numbers balance but who is never actually observed in the laboratory.
On Wed, Mar 5, 2014 at 4:15 PM, Cameron Simpson <cs@zip.com.au> wrote:
On 05Mar2014 04:06, MRAB <python@mrabarnett.plus.com> wrote:
On 2014-03-05 01:46, Cameron Simpson wrote:
a = $(foo + 1)
Definitely nicer. Still irrationally uncomfortable about the "$" though. A thought, though it could break existing code (and nested tuples, alas): a = (( foo + 1 ))
That's not a tuple. It's equivalent to: a = foo + 1
I know that. I should have said: though the below looks nice, in more complicated forms it fights with tuples, eg:
((1,2,3),(4,5,6)) (((1,2,3),(4,5,6)))
Bikeshedding the syntax without expressing an opinion on the feature: Using {{ }} would be safer. The inner one might be interpreted as a dict or a set, but neither of those can go into a set, so it'd be illogical. Whether the similarity with C-style block delimiters is a good thing or a bad thing remains to be seen :) ChrisA
On Wed, Mar 05, 2014 at 12:46:11PM +1100, Cameron Simpson wrote:
That makes me think the (()) or $() is in the wrong place. Maybe:
a := foo + 1
No good. That implies that there is no way to have an anonymous thunk. You can do this: my_list = [x + 1, y + 2, # eager evaluation $(z + 3), # lazy evaluation ... ] but would have to write: temp := z + 3 my_list = [x + 1, y + 2, temp, ...] Similarly for passing a thunk into a function.
Since I'm cavilling about "eval", how about just treating thunks like other functions (which is how we spell code to be run later presently) and have them be callables.
In which case, what's the point? What makes them different from regular functions? -- Steven
* assign a bill to `a` so that `a` will evaluate to the value of the name `foo` any time that `a` is evaluated, in the scope of that evaluation
a = $foo
* as above, but always plus one
a = $foo + 1
* make `a` a bill that evaluates to the value of the name `foo` at the time that `a` is evaluated, in that scope, plus the value of `bar` **at the time and in the scope of the assignment to `a`**
a = $foo + bar
You can do all that programmatically. No need for magic operators. Check this module as a proof of concept: https://github.com/jbvsmo/funcbuilder foo = FuncBuilder() a = foo + 1 # Then evaluate a with a function call For it to do exactly what you want, you just need override the evaluation function (Funcbuilder.__call__) to load names from current frame locals or something. BTW, I don't recommend using that on real code because it is extremely experimental (unless you're a hipster... In this case, carry on).
On Mar 4, 2014, at 17:05, João Bernardo <jbvsmo@gmail.com> wrote:
* assign a bill to `a` so that `a` will evaluate to the value of the name `foo` any time that `a` is evaluated, in the scope of that evaluation
a = $foo
* as above, but always plus one
a = $foo + 1
* make `a` a bill that evaluates to the value of the name `foo` at the time that `a` is evaluated, in that scope, plus the value of `bar` **at the time and in the scope of the assignment to `a`**
a = $foo + bar
You can do all that programmatically. No need for magic operators. Check this module as a proof of concept: https://github.com/jbvsmo/funcbuilder
foo = FuncBuilder() a = foo + 1 # Then evaluate a with a function call
For it to do exactly what you want, you just need override the evaluation function (Funcbuilder.__call__) to load names from current frame locals or something. BTW, I don't recommend using that on real code because it is extremely experimental (unless you're a hipster... In this case, carry on).
I posted something similar in the first of the many improving-lambda threads this month, but it had a lot of little problems and one big one. The big one is that not every expression can be caught by operator overloading (foo[i] is fine, but lst[foo] isn't). And call expressions are one of the most important kinds of expression, but you can't catch that--or, rather, if you _do_ catch it, then you have no way to call the resulting object. The same idea in C++ doesn't have that last problem because C++ is statically typed, and you can use an implicit cast from autolambda object to function object to get from an object whose call operator builds a call expression to one whose call operator evaluates the expression. But that doesn't work in Python. I wrote a blog post that gets into this further, but I can't find the link from my phone. I included it in an earlier message in the thread.
On Tue, Mar 04, 2014 at 11:09:02PM +0000, Carl Smith wrote:
The tentatively proposed idea here is using dollar signed expressions to define 'bills'. A bill object is essentially an expression which can be evaluated any number of times, potentially in different scopes.
Please don't invent "cute" names for things which already have names. What you are describing could be considered a thunk, or a macro, or a lazily-evaluated expression, depending on the precise details of how it works. But calling it a "bill" just because it starts with a $ just makes me cringe.
The following expression [a bill literal] would be pointless, but would define a bill that always evaluates to 1.
$1
So, eval($1)==1.
My personal feeling here is that if you have to explicitly call eval on the "bill" to evaluate it, it's not worth doing. If we were happy with that, we've already got compile(), or we have functions. Or just eval a string directly. My feeling is that evaluating the expression needs to be implicit, performed at need rather than up front, rather in the same way that short-circuiting operators don't actually evaluate the expression unless needed: x or expr # expr is only evaluated if x is a falsey value Yes, I know, the Zen of Python says that "explicit is better than implicit", but the Zen is intended as guidelines, not thought-blockers. We have perfectly good explicit idioms for delaying computations (eval and functions), to make the thunk/macro/whatever worth doing it has to offer something different.
Some better examples...
* assign a bill to `a` so that `a` will evaluate to the value of the name `foo` any time that `a` is evaluated, in the scope of that evaluation
a = $foo
* as above, but always plus one
a = $foo + 1
* make `a` a bill that evaluates to the value of the name `foo` at the time that `a` is evaluated, in that scope, plus the value of `bar` **at the time and in the scope of the assignment to `a`**
a = $foo + bar
Hmmm. That implies that if you want an entire expression to have delayed evaluation, you have to tag everything with a sigil: a = $spam + $eggs - $spam*$eggs + $cheese*$spam I think it is better to have syntax with delimiters, to turn delayed evaluation ON and OFF, rather than having to tag each and every sub-expression: a = `spam + eggs - spam*eggs + cheese*spam` (disclaimer: using ` ` is just for the sake of illustration.)
Note. Similarly to mixing floats with ints, any expression that contains a bill evaluates to a bill, so if `a` is a bill, `b=a+1` makes `b` a bill too. Passing a bill to eval should be the obvious way to get the value.
The point? It allows functions to accept bills to use internally. The function would specify any names the bill can reference in the function's API, like keywords.
Well, this contradicts your previous point. If any expression that contains a "bill" is a "bill", then so is func(bill). So given that, your example here:
def f(b): # the bill arg `b` can reference `item` for item in something: if eval(b): return True
f($item < 0)
would actually need to be written as: eval(f($item < 0)) -- Steven
participants (10)
-
Andrew Barnert -
Cameron Simpson -
Carl Smith -
Chris Angelico -
David Mertz -
Haoyi Li -
João Bernardo -
MRAB -
Ryan Gonzalez -
Steven D'Aprano