- Notifications
You must be signed in to change notification settings - Fork17
llllllllll/lazy_python
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
I will write this later...
lazy is a module for making pythonlazilyevaluated (kinda).
lazy runs under python 3.5 and 3.4.
Why not lazy?
I think lazy computation is pretty cool, I also think python is prettycool; combining them is double cool.
There are 3 means of using lazy code:
lazy_functionrun_lazy- IPython cell and line magics
lazy_function takes a python function and returns a new function that isthe lazy version. This can be used as a decorator.
Example:
@lazy_functiondeff(a,b):returna+b
Callingf(1, 2) will return athunk that will add 1 and 2 when itneeds to be strict. Doing anything with the returned thunk will keepchaining on more computations until it must be strictly evaluated.
Lazy functions allow for lexical closures also:
@lazy_functiondeff(a):defg(b):returna+breturng
When we callf(1) we will get back athunk like we would expect;however, this thunk is wrapping the functiong. Becauseg was createdin a lazy context, it will also be alazy_function implicitly. This meansthattype(f(1)(2)) isthunk; but,f(1)(2) == 3.
We can use strict to strictly evaluate parts of a lazy function, for example:
>>>@lazy_function...defno_strict():...print('test')...>>>strict(no_strict())
In this example, we never forced print, so we never saw the result of the call.Consider this function though:
>>>@lazy_function...defwith_strict():...strict(print('test'))...>>>strict(with_strict())test>>>result=with_strict()>>>strict(result)test
Here we can see how strict works inside of a lazy function.strict causesthe argument to be strictly evaluated, forcing the call to print. We can alsosee that just callingwith_strict is not enough to evaluate the function,we need to force a dependency on the result.
This is implemented at the bytecode level to frontload a large part of the costof using the lazy machinery. There is very little overhead at function calltime as most of the overhead was spent at function creation (definiton) time.
We can convert normal python into lazy python with therun_lazy functionwhich takes a string, the 'name', globals, and locals. This is likeexec oreval for lazy python. This will mutate the provided globals and locals sothat we can access the lazily evaluated code.
Example:
>>>code="""print('not lazy')strict(print('lazy'))""">>>run_lazy(code)lazy
This also uses the same bytecode manipulation aslazy_function so they willgive the same results.
If you have IPython installed, you may use the cell and line magic machinery towrite and evaluate lazy code. For example:
In [1]:fromlazyimportstrictIn [2]:%lazy2+2# line magic acts as an expressionOut[2]:4In [3]:type(_2)Out[3]:lazy._thunk.thunkIn [4]:%%lazy# cell magic is treated as a statement ...:print('lazy') ...:strict(print('strict')) ...:strict
At its core, lazy is just a way of converting expressions into a treeof deferred computation objects calledthunks. thunks wrap normalfunctions by not evaluating them until the value is needed. Athunkwrapped function can acceptthunks as arguments; this is how thetree is built. Some computations cannot be deferred because there is some statethat is needed to construct the thunk, or the python standard defines thereturn of some method to be a specific type. These are refered to as 'strictpoints'. Examples of strict points arestr andbool because the pythonstandard says that these functions must return an instance of their owntype. Most of these converters are strict; however, some other things arestrict because it solves recursion issues in the interpreter, like accessing__class__ on a thunk.
strict is actually a type that cannot be put into athunk. Forexample:
>>>type(thunk(strict,2))int
Notice that this is not a thunk, and has been strictly evaluated.
To create custom strict objects, you can subclassstrict. Thisprevents the object from getting wrapped in thunks allowing you tocreate strict data structures.
Objects may also define a__strict__ method that defines how tostrictly evalueate the object. For example, an object could be definedas:
classStrictFive(object):def__strict__(self):return5
This would makestrict(StrictFive()) return 5 instead of an instanceofStrictFive.
undefined is a value that cannot be strictly evaluated. It is useful as aplaceholder for computations.
We can imagineundefined in python as:
@thunk.fromexpr@Exception.__new__classundefined(Exception):def__strict__(self):raiseself
This object will raise an instance of itself when it is evaluated.This is presented as an equivalent definition, though it is actually in c tomake nicer stack traces.
Because the python spec says the__repr__ of an object must return astr, a call torepr must strictly evaluate the contents so thatwe can see what it is. The repl will implicitly callrepr on thingsto display them. We can see that this is a thunk by doing:
>>>a=thunk(operator.add,2,3)>>>type(a)lazy.thunk.thunk>>>a5
Again, because we need to compute something to represent it, the repl isa bad use case for this, and might make it appear at first like this isalways strict.
Um, what did you think it would do?
If we write:
@lazy_functiondeff(a,b):print('printing the sum of %s and %s'% (a,b))returna+b
Then there is no reason that the print call should be executed. Nocomputation depends on the results, so it is casually skipped.
The solution is to force a dependency:
@lazy_functiondeff(a,b):strict(print('printing the sum of %s and %s'% (a,b)))returna+b
strict is a function that is used to strictly evaluate things.Because the body of the function is interpreted as lazy python, thefunction call is converted into athunk, and therefore we canstrict it.
This is true forany side-effectful function call.
There are some cases where things MUST be strict based on the pythonlanguage spec. Because this is not really a new language, just anautomated way of writing really inefficient python, python's rules mustbe followed.
For example,__bool__,__int__, and other converters expect thatthe return type must be a the proper type. This counts as a place wherestrictness is needed1.
This might not be the case though, instead, I might have missedsomething and you are correct, it should be lazy. If you think I missedsomething, open an issue and I will try to address it as soon aspossible.
Sorry, you are using unmanaged state and lazy evaluation, you deservethis.thunks cache the normal form so that calling strict the secondtime will refer to the cached value. If this depended on some statefulfunction, then it will not work as intended.
The library is probably broken. This was written on a whim and I barelythought through the use cases.
Please open an issue and I will try to get back to you as soon aspossible.
- The function call for the constructor will be made lazy in the
LazyTransformer(likethunk(int, your_thunk)), so while thisis a place where strictness is needed, it can still be 'optimized'away.
About
I will write this later...
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Releases
Packages0
Uh oh!
There was an error while loading.Please reload this page.
Contributors4
Uh oh!
There was an error while loading.Please reload this page.