lmfit / asteval

minimalistic evaluator of python expression using ast module
https://lmfit.github.io/asteval
MIT License
183 stars 41 forks source link

A basic non-closure form of lambda expressions. #91

Closed fusiongyro closed 2 years ago

fusiongyro commented 3 years ago

The attached fix sort of shows a direction for this. The problem is that these lambdas are not closures, so for instance: (lambda x: lambda y: x+y)(4)(5) does not work, because while the outer lambda correctly returns a Procedure, the binding of 4 to x is lost.

I'm not entirely sure the right way to solve this, but I figured if I showed you the code it may be obvious to you.

codecov-commenter commented 3 years ago

Codecov Report

Merging #91 (68bdddf) into master (65886d0) will increase coverage by 0.06%. The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #91      +/-   ##
==========================================
+ Coverage   92.06%   92.13%   +0.06%     
==========================================
  Files           4        4              
  Lines         744      750       +6     
==========================================
+ Hits          685      691       +6     
  Misses         59       59              
Impacted Files Coverage Δ
asteval/asteval.py 93.07% <100.00%> (+0.07%) :arrow_up:

Continue to review full report at Codecov.

Legend - Click here to learn more Δ = absolute <relative> (impact), ø = not affected, ? = missing data Powered by Codecov. Last update 65886d0...68bdddf. Read the comment docs.

newville commented 3 years ago

@fusiongyro Hm, not sure I actually understand the implementation. Why is on_functiondef using on_lambda? What is the collections.abc.Iterable thing doing?

fusiongyro commented 3 years ago

My reasoning is that a defined function is just like an anonymous function, only with a name. This is slightly untrue in Python, since a named function can have a more complex body and arguments. The Iterable thing is a way of testing whether I have a list of items in the body or just a single item. I'm never quite sure what the right way to do that kind of type sniffing is.

newville commented 3 years ago

@fusiongyro

I think a function can have additional nodes compared to a lambda, no?

re Iterables: shouldn't a list of items in the body return that list? Like, I think that probably should not be a special case -- just evaluate the nodes, no? But: I might be missing something too....

It may be that supporting lambda would require Procedure.__call__() to return the result of evaluating the body, instead of returning None. Maybe Procedure needs a flag to tell it to return the result of its body or something.

fusiongyro commented 3 years ago

Yes, in fact the body of an ast.Lambda is a single ast.Expr, whereas for a function definition it is a list of ast.Exprs. Also, unless there is a ast.Return there is no return from a function, whereas there is from a Lambda. So there are some differences both in the AST structure and in the evaluation of the lambda and a function definition. The binding problem is the big one from an implementation point of view though.

I'm not entirely sure what a clean way to handle all of this is, but I'll see if I figure anything out tonight.

newville commented 2 years ago

@fusiongyro we're at about 10 months since there was any activity here. Can we agree that it would be reasonable to close at the "inactive for one-year" mark?

fusiongyro commented 2 years ago

Yes, sorry!