From 5f8bd98d7f4c05ea06d750122d4fef053db09cc3 Mon Sep 17 00:00:00 2001 From: Ralf Gommers Date: Thu, 16 Nov 2023 17:38:01 +0100 Subject: [PATCH 1/3] Add a design topic page on lazy vs. eager implementations Follow-up to gh-652, which added notes to the specifications for `__bool__` & co on this topic. --- spec/draft/design_topics/index.rst | 1 + spec/draft/design_topics/lazy_eager.rst | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 spec/draft/design_topics/lazy_eager.rst diff --git a/spec/draft/design_topics/index.rst b/spec/draft/design_topics/index.rst index fa26359a7..548eda90c 100644 --- a/spec/draft/design_topics/index.rst +++ b/spec/draft/design_topics/index.rst @@ -7,6 +7,7 @@ Design topics & constraints copies_views_and_mutation data_dependent_output_shapes + lazy_eager data_interchange device_support static_typing diff --git a/spec/draft/design_topics/lazy_eager.rst b/spec/draft/design_topics/lazy_eager.rst new file mode 100644 index 000000000..5b6ff7830 --- /dev/null +++ b/spec/draft/design_topics/lazy_eager.rst @@ -0,0 +1,23 @@ +.. _lazy-eager: + +Lazy vs. eager execution +======================== + +While the execution model for implementations is out of scope of this standard, there are a few aspects of lazy (or graph-based) execution as contrasted to eager execution that may have an impact on the prescribed semantics of individual APIs, and will therefore show up in the API specification. + +One important difference is data-dependent or value-dependent behavior, as described in :ref:`data-dependent-output-shapes`. Because such behavior is hard to implement, implementers may choose to omit such APIs from their library. + +Another difference is when the Python language itself prescribes that a specific type *must* be returned. For those cases, it is not possible to return a lazy/delayed kind of object to avoid computing a value. This is the case for five dunder methods: `__bool__`, `__int__`, `__float__`, `__complex__` and `__index__`. Each implementation has only two choices when one of these methods is called: + +1. Compute a value of the required type (a Python scalar of type `bool`, `int`, `float` or `complex`), or +2. Raise an exception. + +When an implementation is 100% lazy, for example when it serializes a computation graph, computing the value is not possible and hence such an implementation has no choice but to raise an exception. For a "mostly lazy" implementation, it may make sense to trigger execution instead - but it is not required to, both choices are valid. + +A common code construct where this happens is conditional logic, e.g.:: + + vals = compute_something() + if all(vals): + # The if-statement will make Python call the __bool__ method + # on the result of `all(vals)`. + do_something_else() From ca4cfca7e327a68268e3143fa8c7bc8ce5e415dc Mon Sep 17 00:00:00 2001 From: Ralf Gommers Date: Thu, 16 Nov 2023 22:00:15 +0100 Subject: [PATCH 2/3] Add a note on the absence of APIs to replace builtins for conditionals. --- spec/draft/design_topics/lazy_eager.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/draft/design_topics/lazy_eager.rst b/spec/draft/design_topics/lazy_eager.rst index 5b6ff7830..864f798d6 100644 --- a/spec/draft/design_topics/lazy_eager.rst +++ b/spec/draft/design_topics/lazy_eager.rst @@ -21,3 +21,5 @@ A common code construct where this happens is conditional logic, e.g.:: # The if-statement will make Python call the __bool__ method # on the result of `all(vals)`. do_something_else() + +Note that the API does not contain control flow constructs that would allow avoiding the implicit `__bool__` call in the example above. The only control flow-like function is `where`, but there's no function like `cond` to replace an `if`-statement. From 2a76a6ece2616bce37bd208d8dd943f0b491f04c Mon Sep 17 00:00:00 2001 From: Ralf Gommers Date: Fri, 17 Nov 2023 12:13:24 +0100 Subject: [PATCH 3/3] Add an "as of now" and line break the page. --- spec/draft/design_topics/lazy_eager.rst | 36 ++++++++++++++++++------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/spec/draft/design_topics/lazy_eager.rst b/spec/draft/design_topics/lazy_eager.rst index 864f798d6..63297ac73 100644 --- a/spec/draft/design_topics/lazy_eager.rst +++ b/spec/draft/design_topics/lazy_eager.rst @@ -3,16 +3,31 @@ Lazy vs. eager execution ======================== -While the execution model for implementations is out of scope of this standard, there are a few aspects of lazy (or graph-based) execution as contrasted to eager execution that may have an impact on the prescribed semantics of individual APIs, and will therefore show up in the API specification. - -One important difference is data-dependent or value-dependent behavior, as described in :ref:`data-dependent-output-shapes`. Because such behavior is hard to implement, implementers may choose to omit such APIs from their library. - -Another difference is when the Python language itself prescribes that a specific type *must* be returned. For those cases, it is not possible to return a lazy/delayed kind of object to avoid computing a value. This is the case for five dunder methods: `__bool__`, `__int__`, `__float__`, `__complex__` and `__index__`. Each implementation has only two choices when one of these methods is called: - -1. Compute a value of the required type (a Python scalar of type `bool`, `int`, `float` or `complex`), or +While the execution model for implementations is out of scope of this standard, +there are a few aspects of lazy (or graph-based) execution as contrasted to +eager execution that may have an impact on the prescribed semantics of +individual APIs, and will therefore show up in the API specification. + +One important difference is data-dependent or value-dependent behavior, as +described in :ref:`data-dependent-output-shapes`. Because such behavior is hard +to implement, implementers may choose to omit such APIs from their library. + +Another difference is when the Python language itself prescribes that a +specific type *must* be returned. For those cases, it is not possible to return +a lazy/delayed kind of object to avoid computing a value. This is the case for +five dunder methods: `__bool__`, `__int__`, `__float__`, `__complex__` and +`__index__`. Each implementation has only two choices when one of these methods +is called: + +1. Compute a value of the required type (a Python scalar of type `bool`, `int`, + `float` or `complex`), or 2. Raise an exception. -When an implementation is 100% lazy, for example when it serializes a computation graph, computing the value is not possible and hence such an implementation has no choice but to raise an exception. For a "mostly lazy" implementation, it may make sense to trigger execution instead - but it is not required to, both choices are valid. +When an implementation is 100% lazy, for example when it serializes a +computation graph, computing the value is not possible and hence such an +implementation has no choice but to raise an exception. For a "mostly lazy" +implementation, it may make sense to trigger execution instead - but it is not +required to, both choices are valid. A common code construct where this happens is conditional logic, e.g.:: @@ -22,4 +37,7 @@ A common code construct where this happens is conditional logic, e.g.:: # on the result of `all(vals)`. do_something_else() -Note that the API does not contain control flow constructs that would allow avoiding the implicit `__bool__` call in the example above. The only control flow-like function is `where`, but there's no function like `cond` to replace an `if`-statement. +Note that the API does not contain control flow constructs, as of now, that +would allow avoiding the implicit `__bool__` call in the example above. The +only control flow-like function is `where`, but there's no function like `cond` +to replace an `if`-statement.