[Python-ideas] How do you think about these language extensions?(Thautwarm)

?? ? twshere at outlook.com
Sat Aug 19 06:34:16 EDT 2017

Hi, all!

I want to reply many people and it might annoy you if I write multiple replies...
As a result, I write them all in one post.

Christopher Barker, Ph.D.

Hi, Dr. Christopher Barker Ph.D.

Just as what you said,

> parentheses aren't that bad, and as far as I can tell, this is just another
> way to call a function on the results of a function.

> The above is now spelled:

> list(map(lambda x: x+2, range(5)))

> which seems fine with me -- the only improvement I see is a more compact
> way to spell lambda. (though really, a list comp is considered more
> "pythonic" these days, yes?

> [x+2 for x in  range(5)]

> nicely, we have list comps and generator expressions, so we can avoid the
> list0 call.

I'll try to talk something about why I think we need this grammar, the
reasons for it are not just to remove parentheses.

Could you please think this way to define a variable:

>> var = expr() -> g1(_) if f(_) else g2(_)

which equals

>> test = f(expr())
>> var  = g1(test) if f(test) else g2(test)

which means that we have to use a temporary variable "test" to define "var".
I think the second example is a bit lengthy, isn't it?
The reason why I take this kind of grammar is that I can "flatten the programming logic".
In another words,I can clearly state what I mean to say in order of my thinking.

For example,

>> lambda x: f(g(x)) -> map(_, range(100))

The codes above means that I'm stressing on what(an action) I'm going to do on an object "range(100)".
However, sometimes the actions are not important, so if we want to stress on what we're going to do something on, we write this codes:

>> range(100) -> map( lambda x:f(g(x)), _ )

Additionally, coding with chaining expressions makes me feel like writing a poem (Although it's a little difficult to me :)

How do you think about writing the following codes?

>> someone -> dosomething( _, options=options) \
           -> is_meeting_some_conditions( _ )  \
           -> result1() if _ else result2()    where:
options = ...
result1 = lambda: ...
result2 = lambda: ...
def dosomething(obj, options) -> Any:

def is_meeting_some_conditions( event : Any ) -> bool :

In my opinion, it's quite readable and "smooth". To be honest, I think we can totolly do coding like chatting and it can be quite enjoyable.

However, I'm not sure whether '->' is a good choice, and it didn't lead to any conflics at all when I compiled the CPython source code. Moreover, It can be easily changed in Grammar/Grammar, so I think it not to be crucial.


> Also,  we need to remember that functions can take *args, **kwargs, etc,
> and can return a tuple of just about anything -- not sure how well that
> maps to the "pipe" model.

I think that using "pipe" model cannot be the right choice.

We don't need to worry about this problem if we use the grammar I've implemented yet :)

  >> (lambda x: (x%5, x) ) ->  max( range(99), key = _)
  >> 94

  >> def max_from_seq(*args): return max(args)
  >> [1,2,3] -> max_from_seq(*_)
  >> 3


David Mertz

I think what you mean to is partially correct, but auto-currying cannot be effective enough.

For sure you can do this in "compose" function.
>  ... -> ... -> ... -> map(lambda x:x+1,  _)

However, the evaluated result can be used as any argument of map and other callable objects.
>  ... -> ... -> ... -> map(_ , range(100))
>  ... -> ... -> ... -> min([1,2,3], key = _ )


Chris Angelico

To be honest, I'm in favor of the grammar you prefer, like "expr1 | expr2 | expr3".
However, it might be annoying that I should firstly define a lot of kinds of pipeline operators like Map, Reduce and so on.
As for whether to allow implicit first/last argument, it seems to be a good idea but two points are in the way:

1. We need to change almost all C functions related to expressions in the source code located at Python/ast.c,
while implementing the grammar I'm using now just needing nothing more than adding new C-function here.
2. Implicit methods makes it impossible to use following expressions.
  > ... -> func(some_var, some_var, _, some_eval(), some_key = _)

  In other words, implicit methods weaken the grammar,
  we need one more "where syntax" to do the same thing:

  >    some -> new_func where:
  new_func = lambda x: func(some_var, some_var, x, some_eval(), some_key = x)

Emmm... I'm not sure about that, how do you think about that?


Steven D'Aprano

Thank you very much for your reply, and it encouraged me a lot.

I've just read your most recent post, and it seems that you've suffered a lot from the parentheses, and so did I.

> Half of my key presses are moving backwards over code I've just written
> to insert a function call which is executed *after* what I wrote, but
> needs to be written *before* what I just wrote.

I couldn't agree more about what you've said here!!!

My opinions about "chaining and pipeline" could be found in my reply to Chris Barker,
sorry for that I could not repeat myself in the same post.

>> # where syntax
>>     from math import pi
>>     r = 1  # the radius
>>     h = 10 # the height
>>     S = (2*S_top + S_side) where:
>>         S_top  = pi*r**2
>>         S_side = C * h where:
>>             C = 2*pi*r

> This has been suggested a few times. The first time, I disliked it, but
> I've come across to seeing its value. I like it.

> I wonder: could we make the "where" clause delay evaluation until the
> entire block was compiled, so that we could write something like this:
>    S = (2*S_top + S_side) where:
>        S_top  = pi*r**2
>        S_side = C * h  # C is defined further on
>        C = 2*pi*r

> That's more how "where" is used mathematically.

 As far as I'm concerned, it's not sure to tell how about you opinion.
 The grammar you've just considered is quite Haskell-like, I think. And
 the reason why I want to use "where synatx" is to divide the programming logic clearly into different layers.

 For example, sometimes we just need to know that surface area of a cylinder is

   2*S_top + S_side

 If someone see the codes, he may not need to know how S_top and S_side are evaluated,getting
 a knowledge of what it means to is enough.
 And if you want to get more about how to evaluate S_side and S_top, just see
 the next "where syntax" and find the answers.

 Here is another example, about forward propagation in neural networks.

  # input_layer[i] : "np.ndarray[:]   " = np.array( ... )
  # weight[i]      : "np.ndarray[:][:]" = np.array( ... )

    output_layer[i] = activate(input_layer[i])  where:

    """ logic layer 1 """
    def activate( layer ):
    return activation[i](layer) # for example, activation[i] = lambda x:x

    input_layer[i] = forward(weight[i-1], output_layer[i-1].T) where:

    """ logic layer 2 """
    def forward(weight, output):
    # if it's a normal multi-layer perceptron.
    return np.matmul(weight, output.T)

  For some people, their works just need them to know that forward propagation of
  a neural network means that the output layer is generated from the input layer
  with some transformation.

  For some people who want to know what the transformation is, they can go to the next "where syntax",
  and find the definition of the transformation which named "activate".

  For people who want to know how neural networks works with multiple layers, they can find that
  the input layer is defined by

  last output_layer,
  last weight matrix
  how NN forwards.

  I think it a good way to use "where syntax" to deconstruct the programming logic, which
  can strengthen the readability a lot!

  And then I'm going to talk something about Pattern Matching, and transform them to regular Python to make it clear to understand.

>> Here is an example to use flowpython, which gives the permutations of a sequence.
>>     from copy import deepcopy
>>     permutations = .seq -> seq_seq where:
>>         condic+[] seq:
>>             case (a,  ) => seq_seq = [a,]
>>             case (a, b) => seq_seq = [[a,b],[b,a]]
>>             case (a,*b) =>
>>                 seq_seq = permutations(b) -> map(.x -> insertAll(x, a),  _) -> sum(_, []) where:
>>                      insertAll = . x, a -> ret where:
>>                          ret = [ deepcopy(x) -> _.insert(i, a) or _ for i in  (len(x) -> range(_+1))  ]

> I find that almost unreadable. Too many new features all at once, it's
> like trying to read a completely unfamiliar language.

> How would you translate that into regular Python?

This algorithm can be fixed a little because the second case is redundant. And here is the regular Python codes transformed
from the codes above.

from copy import deepcopy

def permutations(seq):
# the first case
(a, ) = seq
return [a ,]

# the third case (the second case is redundant)
def insertAll(x, a):
# insertAll([1,2,3], 0) -> [[0, 1, 2, 3], [1, 0, 2, 3], [1, 2, 0, 3], [1, 2, 3, 0]]
ret = []
for i in range( len(x) + 1 ):
tmp = deepcopy(x)
tmp.insert(i, a)
return ret

(a, *b) = seq
tmp     = permutations(b)
tmp     = map(lambda x : insertAll(x, a) , tmp)

return sum(tmp, []) # sum([[1,2,3], [-1,-2,-3]], []) -> [1,2,3,-1,-2,-3]

# no otherwise!

To be continue...(sorry for my lack of time



I'm sorry that I have to do some other works now and
didn't finished writing down all I want to say.

I'd like to continue replying the posts tomorrow , and it's
quite glad to discuss these topics with you all!!!

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20170819/dfe090a7/attachment-0001.html>

More information about the Python-ideas mailing list