Real Positional Arguments or OO Currying

I don't know if this has been suggested before, or if this is outlandishly impossible (though I would be surprised if it was), so apologies in advance if so. I have on occasion come across a situation where I use/write a signature like this: def insert_x_into_y(x, y): ... or worse def insert_into(item, container): ... where, despite a driving idea of python syntax being readability in english, the function signature is distinctly not english. "I'll just go and insert into this item that container", is not only never said but is actually ambiguous in english. What would be really cool, is if python let you write function signatures like this: def insert_(item)_into_(container): ... where the arguments dispersed between the function name are positional only argument, and any key word arguments would have to go at the end. It would create a function that could be called as: insert_(1)_into_(my_list) or insert__into_(1, my_list) The purpose of allowing both should be obvious - so that the function can be referenced and called in other places. (Rather than just skipping the brackets the function call with only the end parentheses could have a special stand in character e.g. ., ?, !, _ or other if that was more preferred.) This sort of signature is particularly annoying for boolean checks like `isinstance` (N.B. I am _not_ suggesting changing any builtins), which one could wrap with: def is_(obj)_an_instance_of_(type): return isinstance(obj, type) For precedence in other languages, this is similar to curried functions in functional languages e.g Haskell, especially if each part of a function were to be callable, which would be up for debate. Allowing each part to be called would make sense if each "next" partial function were an attribute on the previous and what it returned, making it a sort of object oriented currying. Then the syntax could be with a `.`: def is_(obj)._an_instance_of_(type): ... is_(1)._an_instance_of_(int) is_._an_instance_of_(1,int)

On Mon, Oct 18, 2021 at 9:22 PM Mathew Elman <mathew.elman@ocado.com> wrote:
I don't know if this has been suggested before, or if this is outlandishly impossible (though I would be surprised if it was), so apologies in advance if so.
As stated, it basically is.... but the cool thing about crazy ideas is, there's often a very similar one that CAN be implemented! What you're basically asking for is a special type of operator. Python ships with operators like "*" meaning multiplication, where you write the operator between the two values. You can create your own using some very simple abuses of syntax and a nice library.
How about this: https://pypi.org/project/infix/ @div_infix def instance_of(obj, type): return isinstance(obj, type)
42 /instance_of/ int True
It's not precisely what you were asking about, but it works on all current versions of Python, and allows a lot of flexibility. ChrisA

That is interesting but is missing the point of what I am really getting at, in order to build a function of multiple args this way would require a lot of these "operators" that would need to be interchangeable, so I don't think what I am looking for is an operator. The key point being having a nice way to build up the signature with arguments dispersed as they are in an actual sentence in english, and being able to use it.

On Mon, 18 Oct 2021 at 11:20, Mathew Elman <mathew.elman@ocado.com> wrote:
If you care enough, you could create an API that looked like this: insert(1).into(my_list) The `insert` function would create an object that had an `into` method that did the actual work. Personally, I think that sort of API is taking things too far, and I wouldn't use it. Apart from anything else, it "steals" extremely common words like "insert" for your specific API. But if you want to do it, you can - without needing any change to Python.
If you want `insert__into_` as well, just do def insert__into(x, y): return insert(x).into(y) But why would you? It's ugly if spelled like that, and your whole argument is that the "interspersed arguments" form is better. If you just want to pass the function to something that expects "normal" argument conventions, lambda x,y: insert(x).into(y) does what you want.
I've never heard anyone else suggest anything like this, so you might want to consider that the annoyance you feel is not a common reaction...
Yes, that's something like what I'm suggesting. Given that this can be done already in Python, I don't think there's anything like enough justification for special language support for it. Paul

The point is that at the moment to set this sort of api up requires a lot of work, defeating 50% of the value i.e. to define a new function with the attribute access behaviour requires defining each individual function to return a middle step object that has the attribute of the next function, so you can't define a function in plain english. e.g. def insert_into(x, y): ... def insert(x): class Return: def into(self, y): return insert_into(x, y) return Return() insert.into = insert_into is a very long way to say: def insert(x)._into(y): ... and that is without the actual logic and for only 2 positional args.
But why would you? It's ugly if spelled like that, and your whole argument is that the "interspersed arguments" form is better. If you just want to pass the function to something that expects "normal" argument conventions, lambda x,y: insert(x).into(y) does what you want.
The point is so that in code that expects dynamically called functions or to be able to reference the function by name it needs to have a single name that follows backward compatible naming conventions. I would be happy with it being on the onus of the developer in question to add a wrapping function, less happy than if it was added by default but it would still be a saving (and could maybe be in a decorator or something).
I've never heard anyone else suggest anything like this, so you might want to consider that the annoyance you feel is not a common reaction...
I know lots of people that have had this reaction but just shrugged it off as "the way things are", which would seem like a good way to stagnate a language, so I thought I would ask.

What you want was popular in Cobol. That language has generally lost favor for current development, but lots of it is still around. Still, I don't want Python to try to be Cobol. I think the intellectual argument for "English syntax" failed, notwithstanding installed base. On Mon, Oct 18, 2021, 3:49 PM Mathew Elman <mathew.elman@ocado.com> wrote:

Still, I don't want Python to try to be Cobol.
I agree, I don't want Python to try and be Cobol, but that doesn't mean there aren't things to learn from Cobol regarding this, if this indeed something found there - I can't comment on that.
I think the intellectual argument for "English syntax" failed, notwithstanding installed base.
I see this argument used for python in this list (and in the wild) a lot i.e. that it should be readable in English. I am not saying that this is the be-all-end-all of python but it does matter. Also, this being syntax for defining OO curried functions, achieves the goal of more English like syntax while introducing a standard for curried functions in Python - which are of a lot of value in many situations, but usually require nasty syntax.

What you are describing is very, very dissimilar to currying. It's simply multi-argument functions with a different call syntax. Moreover, this hypothetical syntax would make no sense at all for 99% of the functions I write or call. There are a very small number of functions where a conceivable benefit night occur... And almost all of those are already covered by operators like `in` and `+`. It's not even close to worthwhile to have special syntax for rare cases. On Mon, Oct 18, 2021, 4:28 PM Mathew Elman <mathew.elman@ocado.com> wrote:

What you are describing is very, very dissimilar to currying. It's simply multi-argument functions with a different call syntax.
It is almost identical to currying, the only differences are: 1. the intermediate return being an object with an attribute (rather than a new function) that you call. 2. the names of the attributes from 1 (which aren't a thing otherwise) are declared when defining the initial function
It's not even close to worthwhile to have special syntax for rare cases.
It would make sense for a huge number of functions, its just not a natural way to consider writing them because the syntax doesn't exist e.g. almost any boolean function makes sense this way.

I kind of like this idea. I wrote this curry helper module as a proof of concept of how to implement it in Python, today, without having to add features: https://gist.github.com/Ricyteach/b290849da903135a1ed5cce9b161b8c9 Using that, you can write code like this: from typing import Any @curry_helper(suffixes=["into"]) def insert(x: Any, y: list): y.append(x) item = 1 container = [] insert(item).into(container) assert container == [item] @curry_helper(suffixes=["an_instance_of_"]) def is_(obj, cls): return isinstance(obj, cls) obj = 1 assert is_(obj).an_instance_of_(int) the API could be adjusted in all sorts of ways, but I don't think the need to apply a decorator with a list of suffixes like this is too bad. --- Ricky. "I've never met a Kentucky man who wasn't either thinking about going home or actually going home." - Happy Chandler

This is interesting, it's not as nice as native syntax support could have been but a decorator like this is also pretty nice. I think I would bikeshed this so the decorator was the full dot separated signature, e.g. @curry_helper('insert.into') def insert(x: Any, y: list): def y.append(x) but other than that, I think this is probably the closest thing I'll get out of this, so thanks.

Yeah if you change the API to be a bit more powerful, you could build all kinds of ideas on top of the basic kernel of idea... Here's some others I was shooting around with a python-eque buddy of mine this morning: @curry_helper def is_(x): ... # elsewhere @is_.add_predicate def a_number(x): return isinstance(x, float) @is_.add_predicate def an_instance_of(x, y): return isinstance(x, y) Then you can say: is_(x).a_number() is_(x).an_instance_of(y) Or you could also do: @curry_helper def insert(arg): ... # elsewhere @insert.add_predicate def into(obj, cls): if not isinstance(obj, cls): raise TypeError(f"{type(obj).__qualname__}") @insert.into.add_predicate(obj='m', cls=MutableMapping) def mapping(k, v, m): if k in m: raise KeyError(f"{k!r} already exists") m[k] = v @insert.add_predicate(obj='s', cls=MutableSequence) def seq(x, s): ... # elsewhere @insert.seq.add_predicate(x='x', s='s') def at(x, s, i): s.insert(i, x) The idea there would be to provide a way to pass arguments to parent functions, and then: insert(x).into.seq(s).at(i) insert(k, v).into.mapping(m) I would never use any of this in a serious application, but it's still fun. --- Ricky. "I've never met a Kentucky man who wasn't either thinking about going home or actually going home." - Happy Chandler On Mon, Oct 18, 2021 at 12:27 PM Mathew Elman <mathew.elman@ocado.com> wrote:

I think what's being discussed in this thread is variations on the fluent interface design pattern (https://en.wikipedia.org/wiki/Fluent_interface). Personally, I've found myself reaching for it several times in library code I've written where it makes sense. For example, I've written a set of gmail API bindings where composing a message is done like this: gmail = Gmail(...) gmail.draft.to("some_address@domain.com").subject("Some Subject").body.as_html("<title>markup goes here</title>").send() For a more compelling use-case, I've written a fluent CRON scheduler similar to the schedule library (https://pypi.org/project/schedule/), where all of these are examples of valid schedules: with Schedule(...) as schedule: schedule.every(3).months.and_(7).days.and_(12).hours.and_(30).minutes.do(some_func) schedule.every.tuesday.at(6).do(some_func) schedule.every.month.on_the(1).and_(7).at(23, 59).do(some_func) schedule.every.year.in_.march.and_.august.and_.november.on.saturday.and_.sunday.at(12).and_(20, 30).starting(datetime.today()).ending(datetime(2022, 1, 1)).do(some_func) The key design goal here for me was ease of reading and writing schedules. Obviously, this is far easier to read and understand than a cron string to anyone who isn't already intimately familiar with the cron format, but I also wrote it in such a way that as you write your schedule you only ever get valid autocompletions from your IDE for the particular node you are at (which was quite tricky to implement because you can often skip nodes altogether). Here is a demo: https://gfycat.com/thickbrowndorado (watch it in HD or it will be pixellated) Ricky's curry helper decorator is cool, but for me the drawback that makes it a dealbreaker is that it is too dynamic for IDE's to understand. I write a lot of library code and find myself routinely foregoing metaprogramming and runtime attribute/function/class generation in favor of statically declared code so that my consumers will receive assistance from their editors. I guess my thoughts on this thread are that design patterns are only design patterns until languages adopt them as first-class features. For example, no one would call subroutines a design pattern in modern languages, but in the days of goto-based programming, that's exactly what functions were. The addition of syntactic sugar changed them from a design-pattern to a core language construct. So the real question is whether there is an appetite to formally adopt fluent programming as a first-class feature in python by providing supporting syntax to make library code like the above easier to write. From the answers so far that seems unlikely. Personally, I like the idea in theory but I wouldn't want the function call syntax to change. At most I'd just want to add new syntactic sugar to make such constructs easier *to write* (in the simple cases). But I don't really have any suggestions off the top of my head as to what such a syntax might look like. On Mon, Oct 18, 2021 at 5:39 PM Chris Angelico <rosuav@gmail.com> wrote:

On Mon, Oct 18, 2021 at 2:13 PM Matt del Valle <matthewgdv@gmail.com> wrote:
I'll go further: the IDE would be rendered nearly useless! And this is probably the biggest single drawback-- if part of the motivation behind such a fluent style is to help programmers understand the code they are writing more quickly as they write it (seems to me to be the goal anyway), if you also break all the help provided by the IDE in the process, you haven't attained that coal no matter how nifty your cool new fluent code style is. --- Ricky. "I've never met a Kentucky man who wasn't either thinking about going home or actually going home." - Happy Chandler

On Mon, Oct 18, 2021 at 6:15 PM Matt del Valle <matthewgdv@gmail.com> wrote:
I use fluent programming quite often, and have since before I started using Python in 1998. I never really did Smalltalk, but a language called XBase++ (and some related family of language) used this. Maybe Object Pascal too (my memories are dimming). In Pandas, I think the style is very productive and generally clarifies what I might be trying to express as a data scientist. In contrast, I find that scheduler example painful and almost unreadable. Crontab seems completely clear and obvious to me. Yes, I've used crontab, it's not a new thing to me. A key difference between Pandas and the micro-examples is simply HOW MUCH transformation they do. In Pandas, I'll use fluent methods, but they generally completely reshape (or filter, sort, etc) an entire DataFrame. Moreover, in Pandas, each chained method is usually parameterized with a variety of arguments, often named arguments. In examples like the crontab lines, or like: insert(x).into.seq(s).at(i)
Each new method is just a tiny modification of a single "attribute" of the operation. If you want configurations like that, *parameters* are 1000x better. E.g.: insert(seq=s, val=x, pos=i) I mean, hopefully this is actually a method of the sequence, but supposing that it's not for some obscure reason on some obscure data structure. That explicitly describes what each value is doing, and it does it in a way that doesn't look awful to Python eyes. Better still, of course, is just: seq.insert(x, pos=i)

On Mon, 18 Oct 2021 at 12:49, Mathew Elman <mathew.elman@ocado.com> wrote:
The point is that at the moment to set this sort of api up requires a lot of work, defeating 50% of the value i.e. to define a new function with the attribute access behaviour requires defining each individual function to return a middle step object that has the attribute of the next function, so you can't define a function in plain english.
Adding a new feature to a language is *even more* work, though. The reason (one of the reasons) we'd add a feature to the language is because it would get used so often that the repeated saving outweighs the initial (and ongoing) cost of the language feature.
The point is so that in code that expects dynamically called functions or to be able to reference the function by name it needs to have a single name that follows backward compatible naming conventions. I would be happy with it being on the onus of the developer in question to add a wrapping function, less happy than if it was added by default but it would still be a saving (and could maybe be in a decorator or something).
So the automatic definition of the extra name is purely because there's no other way to pass these types of function around as first-class objects? What about other aspects of first class functions - would you be able to introspect these new types of function? Extract the places in the name where the arguments can be interposed? If not, why not? How would a call like foo_(x)_bar(y) be parsed into the AST? Would the original form be recoverable (black, for example, would want this). Also, are the underscores part of the syntax? Is foo(x)bar(y) a single function call using your new syntax? If not, why not? You would be making underscores into special syntax otherwise.
I've never heard anyone else suggest anything like this, so you might want to consider that the annoyance you feel is not a common reaction...
I know lots of people that have had this reaction but just shrugged it off as "the way things are", which would seem like a good way to stagnate a language, so I thought I would ask.
There seem to be a lot of open design questions. Do you have any examples of prior art? Languages that implement this type of syntax, and otherwise have the sort of capabilities that Python does (as David Mertz noted, Cobol had this sort of "English like" syntax, but it didn't have first class function objects.
I see this argument used for python in this list (and in the wild) a lot i.e. that it should be readable in English
That's an over-simplification, and TBH I suspect that most people using the argument know that. Python should be readable in the sense that it should have a generally natural looking syntax, use keywords that read naturally in English, and generally be accessible to people familiar with English. It does *not* mean that English word order must be adhered to, or that technical or abbreviated terms cannot be used (we use "def" rather than "define", and "class" means something very different from the non-computing meaning). Taking "readable in English" to its "logical" conclusion results in "ADD 7 TO X" rather than "x = x + 7" and if you think that a proposal to add that syntax to Python would be well-received, you've badly misjudged both the design principles of Python and the attitude of this mailing list... Paul

Paul Moore wrote: > On Mon, 18 Oct 2021 at 12:49, Mathew Elman mathew.elman@ocado.com wrote: > > The point is that at the moment to set this sort of api up requires a lot of work, defeating 50% of the value i.e. to define a new function with the attribute access behaviour requires defining each individual function to return a middle step object that has the attribute of the next function, so you can't define a function in plain english. > > Adding a new feature to a language is *even more* work, though. The > reason (one of the reasons) we'd add a feature to the language is > because it would get used so often that the repeated saving outweighs > the initial (and ongoing) cost of the language feature. It's more work for the internals of python yes, because it becomes part of python stdlib that you can define a function with `.` in the signature and that has a specific meaning as syntax sugar for what's below. But that doesn't mean it's more work across the board, given that the below would be necessary for every single function that wanted to have this sort of syntax. In every single code base that wanted to use it. Granted no one would use it right now, because right now the effort of doing the below it prohibitive and misses out on the easy to ready signature definition. > > def insert_into(x, y): > > ... > > def insert(x): > > class Return: > > def into(self, y): > > return insert_into(x, y) > > return Return() > > insert.into = insert_into > > is a very long way to say: > > def insert(x)._into(y): > > ... > > and that is without the actual logic and for only 2 positional args. > > But why would you? It's ugly if spelled like that, and your whole argument is that the "interspersed arguments" form is better. If you just want to pass the function to something that expects "normal" argument conventions, lambda x,y: insert(x).into(y) does what you want. > > > The point is so that in code that expects dynamically called functions or to be able to reference the function by name it needs to have a single name that follows backward compatible naming conventions. I would be happy with it being on the onus of the developer in question to add a wrapping function, less happy than if it was added by default but it would still be a saving (and could maybe be in a decorator or something). > > So the automatic definition of the extra name is purely because > there's no other way to pass these types of function around as > first-class objects? What about other aspects of first class functions > - would you be able to introspect these new types of function? It wouldn't need to be a new type of function, so yes of course you would be able to introspect these objects. The change does not require any real magic beyond the way you would do it now, except that it doubles the value of how you do it now by it being in a single neat function definition. > Extract the places in the name where the arguments can be interposed? If not, > why not? Assuming that it would just make each segment of the function name be a function with the next part as an attribute and have a return object yes you would be able to get where the arguments go in the signature. > How would a call like foo_(x)_bar(y) be parsed into the AST? Again, assuming the `.` syntax and a new "Return" class, it would be parse the same way `foo_(x)._bar(y)` would be. no magic needed > Would the original form be recoverable (black, for example, would want this). yes > Also, are the underscores part of the syntax? Is foo(x)bar(y) a single > function call using your new syntax? If not, why not? You would be > making underscores into special syntax otherwise. so, the underscores were a personal choice, because I separate words with underscores, but would not be necessary. Assuming (again) that it used a Return class for the intermediate returns with attributes for the next part, `foo(x)bar(y)` would not be an example, instead `foo(x).bar(y)` would be, which is using existing syntax and concepts. the main difference would be how this is initially defined, requiring only the presence of the `.` and interspersed args in the function signature. > > I've never heard anyone else suggest anything like this, so you might want to consider that the annoyance you feel is not a common reaction... > > I know lots of people that have had this reaction but just shrugged it off as "the way things are", which would seem like a good way to stagnate a language, so I thought I would ask. > > There seem to be a lot of open design questions. Do you have any > examples of prior art? Languages that implement this type of syntax, > and otherwise have the sort of capabilities that Python does (as David > Mertz noted, Cobol had this sort of "English like" syntax, but it > didn't have first class function objects. > > I see this argument used for python in this list (and in the wild) a lot i.e. that it should be readable in English > > That's an over-simplification, and TBH I suspect that most people > using the argument know that. Python should be readable in the sense > that it should have a generally natural looking syntax, use keywords > that read naturally in English, and generally be accessible to people > familiar with English. It does *not* mean that English word order must > be adhered to, or that technical or abbreviated terms cannot be used > (we use "def" rather than "define", and "class" means something very > different from the non-computing meaning). > Taking "readable in English" to its "logical" conclusion results in > "ADD 7 TO X" rather than "x = x + 7" and if you think that a proposal > to add that syntax to Python would be well-received, you've badly > misjudged both the design principles of Python and the attitude of no, I haven't misjudged it, I am not suggesting adding that at all. I think you misunderstand me. I am not saying that this is something broken about python, I am saying it would be cool, a nice to have, something that would make understanding some functions easier because the order of arguments is important but not obvious. When learning python, and even sometimes now, I have had to look at the implementation of a function in order to recall which order arguments should go.

On 10/18/21 6:23 AM, Mathew Elman wrote:
When learning python, and even sometimes now, I have had to look at the implementation of a function in order to recall which order arguments should go.
Seems like the docs should cover that (or even `help()`) -- and if not, then the parameter names could be better. -- ~Ethan~

Seems like the docs should cover that (or even `help()`) -- and if not, then the parameter names could be better.
It should, and normally does as do the parameters, but the best documentation should be the code itself, right? So the idea here would be to make it harder to not know the order than to forget, i.e. so you would check the docs for the more nuanced behaviour around kwargs or something of that nature.

On Mon, Oct 18, 2021 at 10:20:17AM -0000, Mathew Elman wrote:
despite a driving idea of python syntax being readability in english, the function signature is distinctly not english.
Python's syntax was not really modelled on English, as far as I can tell. It was (I think) modelled more on Pascal, Modula-2/3, C and most of all, ABC: https://www.artima.com/articles/the-making-of-python All of those languages (like most programming languages) use English keywords, but not English grammar. In Python's case, I think it is better to say that the language aims to read like executable pseudo-code, not English. If Guido is reading, he might like to step in and correct me, but as far as I know, the intent was never to make Python code read as English. There are languages that do that. The ultimate example of that is quite probably Inform7, a specialised game language that reads like this: Afterlife is a room. "Fluffy white clouds gather round you here in the afterlife." The Pearly Gates are a door in Afterlife. "The Pearly Gates - large, white, wrought-iron and splendidly monumental - stand above you." Heaven is a room. The Gates are above the Afterlife and below Heaven. Yes, that is actual source code, not documentation, taken from the Inform website. http://inform7.com/ Inform 7 is specialised for making text games, but a more general purpose English-like languague comes from the XTalk family of languages, starting with Apple's Hypertalk in the 1990s. In XTalk languages, we can write English-like statements like these: put the date into field "Today" get the second line of todo_list put prefix before the third word of it get the number of words of password if it < 10 then answer "Your password is too weak" add seven to the name of button id 21 put any word of field 1 after the middle word of field 2 Aside from mentioning that "field" here refers to editable text entry fields in the GUI, I probably don't have to explain what any of those lines do. I have a soft spot in my heart for Hypertalk and its GUI builder, Hypercard, so I completely understand your desire to write code with a more English-like syntax. If my memory is correct, in Hypertalk you could take any function of one argument and write it as either of these forms: function(argument) the function of argument So it is not a huge step to imagine a new syntax that looks like your example: insert 1 into container That's practically the same as Hypertalk's "put ... into ...". (Hypertalk also supported put...after and put...before.) So what you are asking for is certainly *possible*. But I do not think it is a good fit for Python's syntax. I love Hypertalk's syntax, but it does not fit well with Python's existing syntax. English-like code and Python code are both great, but a mix of the two in the same file would be like some sort of horrible surgical experiment gone wrong. https://imgur.com/dOWVRkn -- Steve

On 2021-10-18 15:58, Steven D'Aprano wrote:
I once had to do something in Appletalk. If you wanted to refer to the title of a window you would say "title of window" or "window's title". So, how would you refer to the program's title? If it borrowed from Smalltalk it would be "title of windows of self". That didn't work. If it borrowed from C++ it would be "title of windows of this". No luck. After a bit more guesswork I tried "title of window of me". That worked. Alternatively, you could have "me's window's title". Not "my", but "me's". I felt that it was trying too much to be like English whist not being English, so it wasn't as clear what was actually legal. I think that a programming language needs to be different enough from a natural language to remind you that it's a programming language and that the computer isn't that smart. The computer will do as you say, not as you mean.

I agree entirely, that's why I am not suggesting putting spaces and using magic keywords or 's, the proposed change is very limited in that regard and is focused on making positional arguments be positional. The proposed change in behaviour is to allow `.` in a function signature declaration/definition: def foo(arg1).bar(arg2): ... foo(0).bar(1) == foo.bar(0,1) this doesn't look like english at all, but facilitates more language like / naturally readable function signatures. A broader option, that I have seen before but can't find the thread, is to allow `.` in a signature as sugar for defining a function as an attribute / in the scope of something else i.e. def foo.function(...): ... is equivalent to def function(...): ... foo.function = function If that were allowed, then it would be possible to sort of get the syntax I am talking about.

Not really, I'm not trying to suggest a fully English like language be incepted into python, my main point here was that there are cases where the order of arguments is important and being able to have actually positional arguments would go a long way to improving knowing intuitively that order. e.g. I would have to check if `my_list.insert(1, 0)` meant insert 1 at 0 or 0 at 1. but `my_list.insert(0).at_index(1)` would make it really easy to remember, even if you rarely called it that way.

On 10/18/21 3:20 AM, Mathew Elman wrote:
Ugh, that is horrible.
Actually, that looks fine -- the previous example was too verbose.
where, despite a driving idea of python syntax being [readable] in english, the function signature is distinctly not english.
Where did you get that idea? "executable psuedo-code" is a far cry from English.
Why wouldn't you just use `my_list.insert(1) ?
Sorry, way too verbose for me. It is definitely possible to be *too* explicit. While I agree it would be cool (and having written what I thought were some pretty cool things, like not needing to include the value for an enum member), in practice cool and get quite irritating. -- ~Ethan~

On Mon, Oct 18, 2021 at 9:22 PM Mathew Elman <mathew.elman@ocado.com> wrote:
I don't know if this has been suggested before, or if this is outlandishly impossible (though I would be surprised if it was), so apologies in advance if so.
As stated, it basically is.... but the cool thing about crazy ideas is, there's often a very similar one that CAN be implemented! What you're basically asking for is a special type of operator. Python ships with operators like "*" meaning multiplication, where you write the operator between the two values. You can create your own using some very simple abuses of syntax and a nice library.
How about this: https://pypi.org/project/infix/ @div_infix def instance_of(obj, type): return isinstance(obj, type)
42 /instance_of/ int True
It's not precisely what you were asking about, but it works on all current versions of Python, and allows a lot of flexibility. ChrisA

That is interesting but is missing the point of what I am really getting at, in order to build a function of multiple args this way would require a lot of these "operators" that would need to be interchangeable, so I don't think what I am looking for is an operator. The key point being having a nice way to build up the signature with arguments dispersed as they are in an actual sentence in english, and being able to use it.

On Mon, 18 Oct 2021 at 11:20, Mathew Elman <mathew.elman@ocado.com> wrote:
If you care enough, you could create an API that looked like this: insert(1).into(my_list) The `insert` function would create an object that had an `into` method that did the actual work. Personally, I think that sort of API is taking things too far, and I wouldn't use it. Apart from anything else, it "steals" extremely common words like "insert" for your specific API. But if you want to do it, you can - without needing any change to Python.
If you want `insert__into_` as well, just do def insert__into(x, y): return insert(x).into(y) But why would you? It's ugly if spelled like that, and your whole argument is that the "interspersed arguments" form is better. If you just want to pass the function to something that expects "normal" argument conventions, lambda x,y: insert(x).into(y) does what you want.
I've never heard anyone else suggest anything like this, so you might want to consider that the annoyance you feel is not a common reaction...
Yes, that's something like what I'm suggesting. Given that this can be done already in Python, I don't think there's anything like enough justification for special language support for it. Paul

The point is that at the moment to set this sort of api up requires a lot of work, defeating 50% of the value i.e. to define a new function with the attribute access behaviour requires defining each individual function to return a middle step object that has the attribute of the next function, so you can't define a function in plain english. e.g. def insert_into(x, y): ... def insert(x): class Return: def into(self, y): return insert_into(x, y) return Return() insert.into = insert_into is a very long way to say: def insert(x)._into(y): ... and that is without the actual logic and for only 2 positional args.
But why would you? It's ugly if spelled like that, and your whole argument is that the "interspersed arguments" form is better. If you just want to pass the function to something that expects "normal" argument conventions, lambda x,y: insert(x).into(y) does what you want.
The point is so that in code that expects dynamically called functions or to be able to reference the function by name it needs to have a single name that follows backward compatible naming conventions. I would be happy with it being on the onus of the developer in question to add a wrapping function, less happy than if it was added by default but it would still be a saving (and could maybe be in a decorator or something).
I've never heard anyone else suggest anything like this, so you might want to consider that the annoyance you feel is not a common reaction...
I know lots of people that have had this reaction but just shrugged it off as "the way things are", which would seem like a good way to stagnate a language, so I thought I would ask.

What you want was popular in Cobol. That language has generally lost favor for current development, but lots of it is still around. Still, I don't want Python to try to be Cobol. I think the intellectual argument for "English syntax" failed, notwithstanding installed base. On Mon, Oct 18, 2021, 3:49 PM Mathew Elman <mathew.elman@ocado.com> wrote:

Still, I don't want Python to try to be Cobol.
I agree, I don't want Python to try and be Cobol, but that doesn't mean there aren't things to learn from Cobol regarding this, if this indeed something found there - I can't comment on that.
I think the intellectual argument for "English syntax" failed, notwithstanding installed base.
I see this argument used for python in this list (and in the wild) a lot i.e. that it should be readable in English. I am not saying that this is the be-all-end-all of python but it does matter. Also, this being syntax for defining OO curried functions, achieves the goal of more English like syntax while introducing a standard for curried functions in Python - which are of a lot of value in many situations, but usually require nasty syntax.

What you are describing is very, very dissimilar to currying. It's simply multi-argument functions with a different call syntax. Moreover, this hypothetical syntax would make no sense at all for 99% of the functions I write or call. There are a very small number of functions where a conceivable benefit night occur... And almost all of those are already covered by operators like `in` and `+`. It's not even close to worthwhile to have special syntax for rare cases. On Mon, Oct 18, 2021, 4:28 PM Mathew Elman <mathew.elman@ocado.com> wrote:

What you are describing is very, very dissimilar to currying. It's simply multi-argument functions with a different call syntax.
It is almost identical to currying, the only differences are: 1. the intermediate return being an object with an attribute (rather than a new function) that you call. 2. the names of the attributes from 1 (which aren't a thing otherwise) are declared when defining the initial function
It's not even close to worthwhile to have special syntax for rare cases.
It would make sense for a huge number of functions, its just not a natural way to consider writing them because the syntax doesn't exist e.g. almost any boolean function makes sense this way.

I kind of like this idea. I wrote this curry helper module as a proof of concept of how to implement it in Python, today, without having to add features: https://gist.github.com/Ricyteach/b290849da903135a1ed5cce9b161b8c9 Using that, you can write code like this: from typing import Any @curry_helper(suffixes=["into"]) def insert(x: Any, y: list): y.append(x) item = 1 container = [] insert(item).into(container) assert container == [item] @curry_helper(suffixes=["an_instance_of_"]) def is_(obj, cls): return isinstance(obj, cls) obj = 1 assert is_(obj).an_instance_of_(int) the API could be adjusted in all sorts of ways, but I don't think the need to apply a decorator with a list of suffixes like this is too bad. --- Ricky. "I've never met a Kentucky man who wasn't either thinking about going home or actually going home." - Happy Chandler

This is interesting, it's not as nice as native syntax support could have been but a decorator like this is also pretty nice. I think I would bikeshed this so the decorator was the full dot separated signature, e.g. @curry_helper('insert.into') def insert(x: Any, y: list): def y.append(x) but other than that, I think this is probably the closest thing I'll get out of this, so thanks.

Yeah if you change the API to be a bit more powerful, you could build all kinds of ideas on top of the basic kernel of idea... Here's some others I was shooting around with a python-eque buddy of mine this morning: @curry_helper def is_(x): ... # elsewhere @is_.add_predicate def a_number(x): return isinstance(x, float) @is_.add_predicate def an_instance_of(x, y): return isinstance(x, y) Then you can say: is_(x).a_number() is_(x).an_instance_of(y) Or you could also do: @curry_helper def insert(arg): ... # elsewhere @insert.add_predicate def into(obj, cls): if not isinstance(obj, cls): raise TypeError(f"{type(obj).__qualname__}") @insert.into.add_predicate(obj='m', cls=MutableMapping) def mapping(k, v, m): if k in m: raise KeyError(f"{k!r} already exists") m[k] = v @insert.add_predicate(obj='s', cls=MutableSequence) def seq(x, s): ... # elsewhere @insert.seq.add_predicate(x='x', s='s') def at(x, s, i): s.insert(i, x) The idea there would be to provide a way to pass arguments to parent functions, and then: insert(x).into.seq(s).at(i) insert(k, v).into.mapping(m) I would never use any of this in a serious application, but it's still fun. --- Ricky. "I've never met a Kentucky man who wasn't either thinking about going home or actually going home." - Happy Chandler On Mon, Oct 18, 2021 at 12:27 PM Mathew Elman <mathew.elman@ocado.com> wrote:

I think what's being discussed in this thread is variations on the fluent interface design pattern (https://en.wikipedia.org/wiki/Fluent_interface). Personally, I've found myself reaching for it several times in library code I've written where it makes sense. For example, I've written a set of gmail API bindings where composing a message is done like this: gmail = Gmail(...) gmail.draft.to("some_address@domain.com").subject("Some Subject").body.as_html("<title>markup goes here</title>").send() For a more compelling use-case, I've written a fluent CRON scheduler similar to the schedule library (https://pypi.org/project/schedule/), where all of these are examples of valid schedules: with Schedule(...) as schedule: schedule.every(3).months.and_(7).days.and_(12).hours.and_(30).minutes.do(some_func) schedule.every.tuesday.at(6).do(some_func) schedule.every.month.on_the(1).and_(7).at(23, 59).do(some_func) schedule.every.year.in_.march.and_.august.and_.november.on.saturday.and_.sunday.at(12).and_(20, 30).starting(datetime.today()).ending(datetime(2022, 1, 1)).do(some_func) The key design goal here for me was ease of reading and writing schedules. Obviously, this is far easier to read and understand than a cron string to anyone who isn't already intimately familiar with the cron format, but I also wrote it in such a way that as you write your schedule you only ever get valid autocompletions from your IDE for the particular node you are at (which was quite tricky to implement because you can often skip nodes altogether). Here is a demo: https://gfycat.com/thickbrowndorado (watch it in HD or it will be pixellated) Ricky's curry helper decorator is cool, but for me the drawback that makes it a dealbreaker is that it is too dynamic for IDE's to understand. I write a lot of library code and find myself routinely foregoing metaprogramming and runtime attribute/function/class generation in favor of statically declared code so that my consumers will receive assistance from their editors. I guess my thoughts on this thread are that design patterns are only design patterns until languages adopt them as first-class features. For example, no one would call subroutines a design pattern in modern languages, but in the days of goto-based programming, that's exactly what functions were. The addition of syntactic sugar changed them from a design-pattern to a core language construct. So the real question is whether there is an appetite to formally adopt fluent programming as a first-class feature in python by providing supporting syntax to make library code like the above easier to write. From the answers so far that seems unlikely. Personally, I like the idea in theory but I wouldn't want the function call syntax to change. At most I'd just want to add new syntactic sugar to make such constructs easier *to write* (in the simple cases). But I don't really have any suggestions off the top of my head as to what such a syntax might look like. On Mon, Oct 18, 2021 at 5:39 PM Chris Angelico <rosuav@gmail.com> wrote:

On Mon, Oct 18, 2021 at 2:13 PM Matt del Valle <matthewgdv@gmail.com> wrote:
I'll go further: the IDE would be rendered nearly useless! And this is probably the biggest single drawback-- if part of the motivation behind such a fluent style is to help programmers understand the code they are writing more quickly as they write it (seems to me to be the goal anyway), if you also break all the help provided by the IDE in the process, you haven't attained that coal no matter how nifty your cool new fluent code style is. --- Ricky. "I've never met a Kentucky man who wasn't either thinking about going home or actually going home." - Happy Chandler

On Mon, Oct 18, 2021 at 6:15 PM Matt del Valle <matthewgdv@gmail.com> wrote:
I use fluent programming quite often, and have since before I started using Python in 1998. I never really did Smalltalk, but a language called XBase++ (and some related family of language) used this. Maybe Object Pascal too (my memories are dimming). In Pandas, I think the style is very productive and generally clarifies what I might be trying to express as a data scientist. In contrast, I find that scheduler example painful and almost unreadable. Crontab seems completely clear and obvious to me. Yes, I've used crontab, it's not a new thing to me. A key difference between Pandas and the micro-examples is simply HOW MUCH transformation they do. In Pandas, I'll use fluent methods, but they generally completely reshape (or filter, sort, etc) an entire DataFrame. Moreover, in Pandas, each chained method is usually parameterized with a variety of arguments, often named arguments. In examples like the crontab lines, or like: insert(x).into.seq(s).at(i)
Each new method is just a tiny modification of a single "attribute" of the operation. If you want configurations like that, *parameters* are 1000x better. E.g.: insert(seq=s, val=x, pos=i) I mean, hopefully this is actually a method of the sequence, but supposing that it's not for some obscure reason on some obscure data structure. That explicitly describes what each value is doing, and it does it in a way that doesn't look awful to Python eyes. Better still, of course, is just: seq.insert(x, pos=i)

On Mon, 18 Oct 2021 at 12:49, Mathew Elman <mathew.elman@ocado.com> wrote:
The point is that at the moment to set this sort of api up requires a lot of work, defeating 50% of the value i.e. to define a new function with the attribute access behaviour requires defining each individual function to return a middle step object that has the attribute of the next function, so you can't define a function in plain english.
Adding a new feature to a language is *even more* work, though. The reason (one of the reasons) we'd add a feature to the language is because it would get used so often that the repeated saving outweighs the initial (and ongoing) cost of the language feature.
The point is so that in code that expects dynamically called functions or to be able to reference the function by name it needs to have a single name that follows backward compatible naming conventions. I would be happy with it being on the onus of the developer in question to add a wrapping function, less happy than if it was added by default but it would still be a saving (and could maybe be in a decorator or something).
So the automatic definition of the extra name is purely because there's no other way to pass these types of function around as first-class objects? What about other aspects of first class functions - would you be able to introspect these new types of function? Extract the places in the name where the arguments can be interposed? If not, why not? How would a call like foo_(x)_bar(y) be parsed into the AST? Would the original form be recoverable (black, for example, would want this). Also, are the underscores part of the syntax? Is foo(x)bar(y) a single function call using your new syntax? If not, why not? You would be making underscores into special syntax otherwise.
I've never heard anyone else suggest anything like this, so you might want to consider that the annoyance you feel is not a common reaction...
I know lots of people that have had this reaction but just shrugged it off as "the way things are", which would seem like a good way to stagnate a language, so I thought I would ask.
There seem to be a lot of open design questions. Do you have any examples of prior art? Languages that implement this type of syntax, and otherwise have the sort of capabilities that Python does (as David Mertz noted, Cobol had this sort of "English like" syntax, but it didn't have first class function objects.
I see this argument used for python in this list (and in the wild) a lot i.e. that it should be readable in English
That's an over-simplification, and TBH I suspect that most people using the argument know that. Python should be readable in the sense that it should have a generally natural looking syntax, use keywords that read naturally in English, and generally be accessible to people familiar with English. It does *not* mean that English word order must be adhered to, or that technical or abbreviated terms cannot be used (we use "def" rather than "define", and "class" means something very different from the non-computing meaning). Taking "readable in English" to its "logical" conclusion results in "ADD 7 TO X" rather than "x = x + 7" and if you think that a proposal to add that syntax to Python would be well-received, you've badly misjudged both the design principles of Python and the attitude of this mailing list... Paul

Paul Moore wrote: > On Mon, 18 Oct 2021 at 12:49, Mathew Elman mathew.elman@ocado.com wrote: > > The point is that at the moment to set this sort of api up requires a lot of work, defeating 50% of the value i.e. to define a new function with the attribute access behaviour requires defining each individual function to return a middle step object that has the attribute of the next function, so you can't define a function in plain english. > > Adding a new feature to a language is *even more* work, though. The > reason (one of the reasons) we'd add a feature to the language is > because it would get used so often that the repeated saving outweighs > the initial (and ongoing) cost of the language feature. It's more work for the internals of python yes, because it becomes part of python stdlib that you can define a function with `.` in the signature and that has a specific meaning as syntax sugar for what's below. But that doesn't mean it's more work across the board, given that the below would be necessary for every single function that wanted to have this sort of syntax. In every single code base that wanted to use it. Granted no one would use it right now, because right now the effort of doing the below it prohibitive and misses out on the easy to ready signature definition. > > def insert_into(x, y): > > ... > > def insert(x): > > class Return: > > def into(self, y): > > return insert_into(x, y) > > return Return() > > insert.into = insert_into > > is a very long way to say: > > def insert(x)._into(y): > > ... > > and that is without the actual logic and for only 2 positional args. > > But why would you? It's ugly if spelled like that, and your whole argument is that the "interspersed arguments" form is better. If you just want to pass the function to something that expects "normal" argument conventions, lambda x,y: insert(x).into(y) does what you want. > > > The point is so that in code that expects dynamically called functions or to be able to reference the function by name it needs to have a single name that follows backward compatible naming conventions. I would be happy with it being on the onus of the developer in question to add a wrapping function, less happy than if it was added by default but it would still be a saving (and could maybe be in a decorator or something). > > So the automatic definition of the extra name is purely because > there's no other way to pass these types of function around as > first-class objects? What about other aspects of first class functions > - would you be able to introspect these new types of function? It wouldn't need to be a new type of function, so yes of course you would be able to introspect these objects. The change does not require any real magic beyond the way you would do it now, except that it doubles the value of how you do it now by it being in a single neat function definition. > Extract the places in the name where the arguments can be interposed? If not, > why not? Assuming that it would just make each segment of the function name be a function with the next part as an attribute and have a return object yes you would be able to get where the arguments go in the signature. > How would a call like foo_(x)_bar(y) be parsed into the AST? Again, assuming the `.` syntax and a new "Return" class, it would be parse the same way `foo_(x)._bar(y)` would be. no magic needed > Would the original form be recoverable (black, for example, would want this). yes > Also, are the underscores part of the syntax? Is foo(x)bar(y) a single > function call using your new syntax? If not, why not? You would be > making underscores into special syntax otherwise. so, the underscores were a personal choice, because I separate words with underscores, but would not be necessary. Assuming (again) that it used a Return class for the intermediate returns with attributes for the next part, `foo(x)bar(y)` would not be an example, instead `foo(x).bar(y)` would be, which is using existing syntax and concepts. the main difference would be how this is initially defined, requiring only the presence of the `.` and interspersed args in the function signature. > > I've never heard anyone else suggest anything like this, so you might want to consider that the annoyance you feel is not a common reaction... > > I know lots of people that have had this reaction but just shrugged it off as "the way things are", which would seem like a good way to stagnate a language, so I thought I would ask. > > There seem to be a lot of open design questions. Do you have any > examples of prior art? Languages that implement this type of syntax, > and otherwise have the sort of capabilities that Python does (as David > Mertz noted, Cobol had this sort of "English like" syntax, but it > didn't have first class function objects. > > I see this argument used for python in this list (and in the wild) a lot i.e. that it should be readable in English > > That's an over-simplification, and TBH I suspect that most people > using the argument know that. Python should be readable in the sense > that it should have a generally natural looking syntax, use keywords > that read naturally in English, and generally be accessible to people > familiar with English. It does *not* mean that English word order must > be adhered to, or that technical or abbreviated terms cannot be used > (we use "def" rather than "define", and "class" means something very > different from the non-computing meaning). > Taking "readable in English" to its "logical" conclusion results in > "ADD 7 TO X" rather than "x = x + 7" and if you think that a proposal > to add that syntax to Python would be well-received, you've badly > misjudged both the design principles of Python and the attitude of no, I haven't misjudged it, I am not suggesting adding that at all. I think you misunderstand me. I am not saying that this is something broken about python, I am saying it would be cool, a nice to have, something that would make understanding some functions easier because the order of arguments is important but not obvious. When learning python, and even sometimes now, I have had to look at the implementation of a function in order to recall which order arguments should go.

On 10/18/21 6:23 AM, Mathew Elman wrote:
When learning python, and even sometimes now, I have had to look at the implementation of a function in order to recall which order arguments should go.
Seems like the docs should cover that (or even `help()`) -- and if not, then the parameter names could be better. -- ~Ethan~

Seems like the docs should cover that (or even `help()`) -- and if not, then the parameter names could be better.
It should, and normally does as do the parameters, but the best documentation should be the code itself, right? So the idea here would be to make it harder to not know the order than to forget, i.e. so you would check the docs for the more nuanced behaviour around kwargs or something of that nature.

On Mon, Oct 18, 2021 at 10:20:17AM -0000, Mathew Elman wrote:
despite a driving idea of python syntax being readability in english, the function signature is distinctly not english.
Python's syntax was not really modelled on English, as far as I can tell. It was (I think) modelled more on Pascal, Modula-2/3, C and most of all, ABC: https://www.artima.com/articles/the-making-of-python All of those languages (like most programming languages) use English keywords, but not English grammar. In Python's case, I think it is better to say that the language aims to read like executable pseudo-code, not English. If Guido is reading, he might like to step in and correct me, but as far as I know, the intent was never to make Python code read as English. There are languages that do that. The ultimate example of that is quite probably Inform7, a specialised game language that reads like this: Afterlife is a room. "Fluffy white clouds gather round you here in the afterlife." The Pearly Gates are a door in Afterlife. "The Pearly Gates - large, white, wrought-iron and splendidly monumental - stand above you." Heaven is a room. The Gates are above the Afterlife and below Heaven. Yes, that is actual source code, not documentation, taken from the Inform website. http://inform7.com/ Inform 7 is specialised for making text games, but a more general purpose English-like languague comes from the XTalk family of languages, starting with Apple's Hypertalk in the 1990s. In XTalk languages, we can write English-like statements like these: put the date into field "Today" get the second line of todo_list put prefix before the third word of it get the number of words of password if it < 10 then answer "Your password is too weak" add seven to the name of button id 21 put any word of field 1 after the middle word of field 2 Aside from mentioning that "field" here refers to editable text entry fields in the GUI, I probably don't have to explain what any of those lines do. I have a soft spot in my heart for Hypertalk and its GUI builder, Hypercard, so I completely understand your desire to write code with a more English-like syntax. If my memory is correct, in Hypertalk you could take any function of one argument and write it as either of these forms: function(argument) the function of argument So it is not a huge step to imagine a new syntax that looks like your example: insert 1 into container That's practically the same as Hypertalk's "put ... into ...". (Hypertalk also supported put...after and put...before.) So what you are asking for is certainly *possible*. But I do not think it is a good fit for Python's syntax. I love Hypertalk's syntax, but it does not fit well with Python's existing syntax. English-like code and Python code are both great, but a mix of the two in the same file would be like some sort of horrible surgical experiment gone wrong. https://imgur.com/dOWVRkn -- Steve

On 2021-10-18 15:58, Steven D'Aprano wrote:
I once had to do something in Appletalk. If you wanted to refer to the title of a window you would say "title of window" or "window's title". So, how would you refer to the program's title? If it borrowed from Smalltalk it would be "title of windows of self". That didn't work. If it borrowed from C++ it would be "title of windows of this". No luck. After a bit more guesswork I tried "title of window of me". That worked. Alternatively, you could have "me's window's title". Not "my", but "me's". I felt that it was trying too much to be like English whist not being English, so it wasn't as clear what was actually legal. I think that a programming language needs to be different enough from a natural language to remind you that it's a programming language and that the computer isn't that smart. The computer will do as you say, not as you mean.

I agree entirely, that's why I am not suggesting putting spaces and using magic keywords or 's, the proposed change is very limited in that regard and is focused on making positional arguments be positional. The proposed change in behaviour is to allow `.` in a function signature declaration/definition: def foo(arg1).bar(arg2): ... foo(0).bar(1) == foo.bar(0,1) this doesn't look like english at all, but facilitates more language like / naturally readable function signatures. A broader option, that I have seen before but can't find the thread, is to allow `.` in a signature as sugar for defining a function as an attribute / in the scope of something else i.e. def foo.function(...): ... is equivalent to def function(...): ... foo.function = function If that were allowed, then it would be possible to sort of get the syntax I am talking about.

Not really, I'm not trying to suggest a fully English like language be incepted into python, my main point here was that there are cases where the order of arguments is important and being able to have actually positional arguments would go a long way to improving knowing intuitively that order. e.g. I would have to check if `my_list.insert(1, 0)` meant insert 1 at 0 or 0 at 1. but `my_list.insert(0).at_index(1)` would make it really easy to remember, even if you rarely called it that way.

On 10/18/21 3:20 AM, Mathew Elman wrote:
Ugh, that is horrible.
Actually, that looks fine -- the previous example was too verbose.
where, despite a driving idea of python syntax being [readable] in english, the function signature is distinctly not english.
Where did you get that idea? "executable psuedo-code" is a far cry from English.
Why wouldn't you just use `my_list.insert(1) ?
Sorry, way too verbose for me. It is definitely possible to be *too* explicit. While I agree it would be cool (and having written what I thought were some pretty cool things, like not needing to include the value for an enum member), in practice cool and get quite irritating. -- ~Ethan~
participants (9)
-
Chris Angelico
-
David Mertz, Ph.D.
-
Ethan Furman
-
Mathew Elman
-
Matt del Valle
-
MRAB
-
Paul Moore
-
Ricky Teachey
-
Steven D'Aprano