[Python-ideas] combining two threads: switch statements and inline functions

Steven D'Aprano steve at pearwood.info
Wed Feb 12 04:29:19 CET 2014


On Tue, Feb 11, 2014 at 03:05:53PM -0800, Bruce Leban wrote:
> What if we had the ability to write dictionaries with inline functions in
> them.

What's an inline function? I've asked in the inline function thread how 
it differs from a regular function with dynamic scoping, but haven't had 
a response yet. Since you're now extending the proposal, perhaps you can 
answer.

Is it a function that uses dynamic scoping instead of static scoping? A 
multi-line anonomous function? Some sort of closure? A thunk? Some of 
the above? In the previous discussion, it appears to be a regular 
function using dynamic scoping. Here it appears to be more kind of like 
a thunk, in that it makes up an expression.

https://en.wikipedia.org/wiki/Thunk_%28functional_programming%29


Looking at your example here:
 
> def sample(i, op, j):
>     switcher = {{
>        '-':: if i > j:
>                  return i - j
>              else:
>                  return j - i;;
>        '+':: return i + j;;
>     }}
>     return switcher[op]()


I think it is silly to limit these "inline functions" to only be placed 
inside dictionaries. What if you want one stand-alone? Or in a list?
Inserting them inside a dictionary should be merely a special case of 
the more general case, a multi-line "inline function" that is 
syntactically an expression. (Like lambda, only super-powered.)

So let's toss out the mutated dict syntax, and use a normal dict, 
assuming only some special syntax for something to delay computation 
until called later. I'm going to call it "thunk" rather than inline 
function, since to me that appears to be the closest match, and use the 
simplest syntax that might work. Consider this pseudo-code rather than 
my proposal for actual "thunk" syntax.


switcher = {
    # Receives i, j and op from the scope of the caller.
    '-': thunk if i > j:
                  return i - j
              else:
                  return j - i,
    '+': thunk return i + j,
    None: thunk raise OperatorError("unknown operator %s" % op),
    }



You then use these thunks something like this:

def sample(i, op, j):
    return switcher.get(op)()


It doesn't really buy you much benefit over normal functions with static 
(lexical) scoping:

def sample(i, op, j):
    return switcher.get(op)(i, op, j)


If there is a benefit to dynamic scoping, surely it needs to be more 
than saving a couple of explicit arguments? I would argue that the 
benefit of being able to explicitly read the arguments there in the 
function call far outweighs the cost of having to explicitly write the 
arguments.

Remember that dynamic scoping is rare in programming languages, because 
it is normally considered a poor idea. It's not hard to implement, but 
it is hard to use right. Personally, I'm very excited by the idea of 
dynamic scoping, but in my opinion it's in desperate need of a good 
use-case.

So what have we got here?

- The idea of dynamic scoping doesn't seem to gain us much, perhaps a 
  little less typing, but only at the cost of making calls less explicit 
  and hence harder to understand. (In other words, I'm still looking for
  a good use-case. This isn't it.)

- We're no closer to finding good syntax for a multi-line anonymous 
  function which can be used in expressions, regardless of what scoping 
  rules the function uses. My "thunk" syntax above doesn't really work 
  for me, it's just the first and most explicit syntax I thought of.

- But if we somehow solve the second problem, regardless of how the 
  functions are scoped, then we can trivially use it inside function 
  calls, dicts, sets, lists, attribute assignments, ... 

In other words:

* scoping and multi-line functions are orthogonal;

* dicts are a red herring;

* we're no closer to a multi-line lambda than we were 20 years ago.



-- 
Steven


More information about the Python-ideas mailing list