If they add anything to Python, it should be the ability to do functional-style programming without hassle. Right now it's almost impossible to compose functions in an inline style such as used in functional programming languages. Yes, there's lambda, but it doesn't extend into multiple lines, and applying a function to a bunch of lambdas directly leads to line-width overflow. Even JavaScript has better support for functional-style programming. Perhaps Guido should spend a year writing Haskell :)
Haskell has always officially inspired Python tooling. But Python does things in it's own way, and they are not random, they are the result of an opinion.
First class citizen functions, short lambdas, comprehension lists, generators, map(), filter(), itertools, operator and functools are quite a rich toolbox already. But you won't have more. It's a choice.
The idea is to have enough to be productive, and not enough to be dogmatic. The experience of Guido, and it's one that I share, is that too much functional tooling drives a style that favors expressive writing at the expense of ease of reading.
It's not by chance that LISP and Haskell are considered hard languages to get into, while Python is considered easy to start with.
It has a cost, since no language is perfect, but that's the path this language follows and requesting a snake to fly will only bring you disappointments.
Python tries to strike the balance between the importance of a rich expressiveness and the non negotiable necessity of keeping the code readable: you read a line much more often that you write it, after all. It's a key philosophy of the language. It shaped and will shape numerous decisions around it.
This PEP is a perfect example : it tooks years for the concept to be integrated in Python, and the last debate about this concrete implementation took months. The result is a carefully crafted feature with a lot of details to discourage abuse and remove the needs for pondering when to use it or not.
> The experience of Guido, and it's one that I share, is that too much functional tooling drives a style that favors expressive writing at the expense of ease of reading.
This is absolutely dead on accurate. As a Clojure developer, using one of the most expressive -- dare I say, artistic -- programming languages ever created, I can say that I am totally in the zone writing code which is elegant and terse and really packs a punch, does clever things.... and then just a few days later, it is very hard for my own brain to parse my own code and figure out what it does.
For each line of code you write once, it will be read dozens of times by you or others. Code is for reading. Languages that get this right make things a lot easier for everyone.
you are right that python is an opinionated choice, but that particularly chosen philosophy is what people disagree with. the philosophy is somewhat stubborn as well.
and i think you are leaving out functional languages which share python's readability, if not surpass it, while remaining much more expressive. that's f# and ocaml.
f#, in my opinion, is superior in everyway to python and subsumes python's abilities of readability, easiness, oop, and scripting, while greatly raising the ceiling of possibility. it's criminally underused, especially in areas where python has been chosen.
and i disagree lisp is harder to get into. racket is just as easy to learn as python, if not easier, due to its regularity. the how to code / systematic program design course on edX and the book how to design programs showcases this.
I heard good things about F# and I did like C#, so I wanted to give an honest look at your arguments.
The first thing I checked is your very vocal assurance that F# is a better scripting language than Python. That seemed very weird to me, after all it's Python strong point. Since I script a lot, I looked for the most popular F# lib to parse script arguments.
open Argu
type CLIArguments =
| Working_Directory of path:string
| Listener of host:string * port:int
| Data of base64:byte[]
| Port of tcp_port:int
| Log_Level of level:int
| Detach
with
interface IArgParserTemplate with
member s.Usage =
match s with
| Working_Directory _ -> "specify a working directory."
| Listener _ -> "specify a listener (hostname : port)."
| Data _ -> "binary data in base64 encoding."
| Port _ -> "specify a primary port."
| Log_Level _ -> "set the log level."
| Detach _ -> "detach daemon from console."
let parser = ArgumentParser.Create<CLIArguments>(programName = "gadget.exe")
let results = parser.Parse [| "--detach" ; "--listener" ; "localhost" ; "8080" |]
printfn "%A" results.GetAllResults();;
The same thing with click, the Python most popular solution, is 11 lines, and it's shaped around almost only regular calls and parameters:
import click as cli, base64, urllib.parse as url
@cli.command("gadget.exe")
@cli.option('--working-directory', help='specify a working directory.', type=cli.File('rb'))
@cli.option('--listener', help="specify a listener (hostname : port)", type=url.urlparse)
@cli.option('--data', help='binary data in base64 encoding.', type=base64.b64decode)
@cli.option('--port', help='"specify a working directory.', type=cli.File('rb'))
@cli.option('--log-level', help='set the log level.', type=int)
@cli.option('--detach', is_flag=True, help='detach daemon from console')
def hello(**kwargs):
print(kwargs)
hello(["--detach", "--listener", "localhost:8080"])
I have a hard time finding the motivation to look for the truth behind your other arguments after that.
this is interesting, because if i didn't read your commentary, these examples seem to favor f#. :)
what are your objections? what is the dense symbology?
discriminated unions (what the CLIArguments is) are very simple to define and understand. the usage member nearly uses the simplest possible pattern matching available. pattern matching is a staple of functional languages. it's a case statement in its simplest use but is so much more in general.
these two things are the bread and butter of f#. they may take a modicum more initial effort than simple function calls, but it pays off in readability and expandability. it seems python takes the easy route. it makes things apparently simple at first but difficult in the long run.
i know both languages, to a degree, and find the python hard to read. it's also faking types, which is kind of funny. the f# code is fully typed.
lines of code is meaningless to me here because the f# has better delineation of concepts here.
and lastly, there's actually no reason why you couldn't write an f# library to behave like the python one here. that is not true the other way around. that's the power of f#'s multi-paradigm nature.
I'm not who you replied to but I have the same reaction. The F# version doesn't even include the actual arguments, and the capitalised and underscored versions are repeated twice. There should be no need for pattern matching or discriminated unions here. The Python version doesn't seem to be faking types, but specifying constructors.
The Python version ultimately appeals to stringly-typed maps. This is often pretty challenging to debug, especially in modern environments where things tend to be launched via container managers "at a distance" with container managers.
Also, click is weird because it wants to take over traditional function syntax and "under the covers" rewrite them. Compared to a much simpler Args -> Map kind of construction, this is a great example of how Python introduces unneeded complexity and prefers to create huge spelunking expeditions into its poorly-explained function invocation syntax & semantics. The PEP we're all commenting around is another great example of that approach. It's too bad Python's community is often more interested in novel uses of Python's semantics than actually readable, reusable concepts.
The irony is other "deep in their own waters" approaches produce stuff that's much more readable than click's without also being some kind of solve-the-universe's-problems black-box. Python dooms itself to that because of its refusal to embrace more composable primitives. They'll always end up with completing big-bang projects that don't play well together. Examples available upon request.
> The F# version doesn't even include the actual arguments, and the capitalised and underscored versions are repeated twice.
i don’t know what you mean. actual argumennts? do you mean like “—working directory” or the values passed by them? i am actually not familiar with this library, but it seems the former is handled by the library from the discriminated union constructor names and the latter are right there in the constructors.
and what do you mena there’s no need? that seems rather arbitrary. it’s a way to represent your data explicitly with types, i.e., is type-driven development.
i can’t further defend this library because i have never used it, but i see no confusion here and don’t even understand the complaints. it seems to be “this is different than how i think in python, so it’s no good”.
Huh. I normally complain about how functional languages seem to be designed to be easy to write the first time but super hard to read and modify. However, in this case, the F# code seems very clear, significantly moreso than the Python code.
I agree with you. I'm actually a big fan of both Haskell and F#, and use Haskell especially for a lot of my hobby projects. However the idea that either of them make a better scripting language than Python seems laughable to me.
i didn’t say haskell. f# has explicit support for scripting in the form of its fsx file format. how is f# as a scripting language laughable? f# even has access to the powershell api through .net.
>"I spent 20m halfheartedly looking for an example I could use to discredit a stranger online. Every programming language but python is wrong."
This is a really uncharitable interpretation of what he did. It was specifically mentioned as a scripting language, and parsing command line arguments is frequently necessary for scripts.
He then compared the hello world examples for the two most popular command line argument libraries. This is hardly going out of his way to find some bizarre corner case examples - it's the primary example for one of the most important libraries for the advocated use case. He never said anything about any programming languages besides Python and F#.
You can disagree with him without twisting his argument around.
The two examples parse exactly the same set of arguments, so your claim that the python one ignores complexity seems naive, whether or not this was an intentional oversight im unsure of. Or perhaps it's just significantly simpler ;)
My problems with the F# example:
1. It internally rewrites the argument names. "Data" becomes "--data". Who knows how I can get a short flag (-d) in f# land.
2. You don't declare things at once. You define all the arguments the. You define all the descriptions, etc. This isn't the fault of the language necessarily, but this API is bad. It's highly repetitive (each modification to an argument requires a new match expression it seems, that's gross).
3. It's unclear if and how this is extensible. I can continue to decorate main to add more args (for example if I wrap an existing tool and define additional flags), in python in an obvious way. This is not obvious in F#. This compounds #2.
Your objection about kwargs isn't particularly compelling. Kwargs are exactly as "stringly" typed as method names, which is exactly how you're doing modifications in F# (or Java or anything else).
And density of the python is trivial to fix by adding newlines.
As an aside, I'm the third (or fourth?) to tell you that your responses appear incredibly disingenuous. And that you are the one acting dismissively, not the post you originally responded to. I don't see any universe where going to the effort of comparing the two languages in question in a reasonable benchmark and sharing it is anything approximately dismissive.
In advance, I apologize for fisking your post. I did it simply because I wanted to provide a lot more information than I think you're interested in, and this is the best typesetting option I have.
> The two examples parse exactly the same set of arguments, so your claim that the python one ignores complexity seems naive, whether or not this was an intentional oversight im unsure of. Or perhaps it's just significantly simpler ;)
You ignored that bit of my post. I said the F# one hints at how to do more complex things. The click example (I use click all the time, by the way) has completely different methods for introducing other constructs or doing custom logic. The F# version just uses lambdas (a sore spot on Python) to do this.
> 1. It internally rewrites the argument names. "Data" becomes "--data". Who knows how I can get a short flag (-d) in f# land.
You'd annotate it. This is why I said the original example offer was poorly researched. They literally didn't scroll down less than 2 screen widths. You didn't even bother to check either. You'd add an annotation to the delclaration such as: [<AltCommandLine("-d")>]
> 2. You don't declare things at once. You define all the arguments the. You define all the descriptions, etc. This isn't the fault of the language necessarily, but this API is bad. It's highly repetitive (each modification to an argument requires a new match expression it seems, that's gross).
You seem to have chopped this up editing it, but I think the gist is that there is a repeated value here in the match. You seem to think that's bad because of DRY principles and because it's easy to make an error.
It is not easy to make an error. F# is statically typed and checked. It'd be an awful decision in a language with no static correctness checking, like Python. In F#, the compiler will flag you with a non-exhaustive pattern match error and tell you which values you're missing! You can read more about that here: https://fsharpforfunandprofit.com/posts/correctness-exhausti...
As for the decls, this is fine. Not only does the compiler have your back, but it's quite normal to have a fair amount of separation between your main loop and your arg parsing. In fact, you almost certainly have it in F# due to the way args are loaded. You'll usually have a separate file containing those data decls so that you can use them without importing your main loop (F# has a bit of ugliness here imo; modules have a defined linear load error and you can't import code in complex graphs). Remember, unlike a stringly-typed map you'e getting back a strongly typed, bespoke discriminated unions that contains exactly the values you expect.
If you'd prefer map-key syntax instead of a typechecked function map (because you hate autocomplete and static correctness) to provide your usage strings... then it's trivial to do that in F#. F#'s documentation is awesome at avoiding incidentals and focusing on the task at hand though.
> 3. It's unclear if and how this is extensible. I can continue to decorate main to add more args (for example if I wrap an existing tool and define additional flags), in python in an obvious way. This is not obvious in F#. This compounds #2.
Since you don't know F#, is it that surprising it's unclear? But anyways...
You don't want it to be extensible. You want to edit the struct and then also edit the error handling and specifications. You want to give the compiler a chance to catch your errors. If you remove an option, the compiler will tell you every place you need to change it. If you add one, nothing will break except your usage map and your error handler (and possibly in extremely weird custom parse cases that can come up, but don't custom parse over unquoted strings and you're fine).
> Your objection about kwargs isn't particularly compelling. Kwargs are exactly as "stringly" typed as method names, which is exactly how you're doing modifications in F# (or Java or anything else).
I think you misunderstand how this works. Incorrect java method names are a compile time error. In Python a missing kwarg is a runtime error. In some cases, a python linter can catch it via heuristics. That's not always the case, particularly across library or communication boundaries.
> And density of the python is trivial to fix by adding newlines.
The same could be done for the F# variant by removing them. That doesn't mean it's a good idea.
> As an aside, I'm the third (or fourth?) to tell you that your responses appear incredibly disingenuous.
I don't think I've been at all dishonest about my intentions. I don't like Python, I think this new PEP makes an already bad, I think the justification for it is bad, and everything in Python would be better without it. What am I being "disingenuous" about? Am I engaging with you dishonestly?
> And that you are the one acting dismissively, not the post you originally responded to.
I bad attempt at dismissing F# by refusing to even read one tutorial page by a Python user in 2018 does not deserve anything but derision. Your attempt to defend it without even checking to see what the author did is not quite so bad, so I'm being more civil with this response, but I think it's in a similar kind of Pythonista conversation that goes, "I don't know and I don't want to know, because Python is fine."
> I don't see any universe where going to the effort of comparing the two languages in question in a reasonable benchmark and sharing it is anything approximately dismissive.
Well hopefully this post has helped illustrate some of the considerations that make that comparison more reasonable? I genuinely mean that! I could have skipped over or not explained the part about why the separation doesn't matter, how F#'s compiler has a linear load order, or why it's not problematic (and in fact desirable and autocomplete-assisted) for a bit of repetition.
I think it's a mistake to approach language comparisons looking for ways to dismiss it until you've written at least a few trivial programs in said language. I've written tons of Python and a fair sum of F#. And honestly, I'm not a huge fan of Argu as a library compared to something like optparse-applicative from Haskell. But it's much more serviceable than this comparison implies. I particularly dislike Python's solution because it's a series of kwarg invocations with signature overlap. I've been burned by this in the past, and I dislike the, "here is the secret method that does EXACTLY what you want" approach that Python decorator patterns usually demand.
I think I set myself up for it, so no apology needed :) Hopefully its alright if I do the same in kind.
> The click example (I use click all the time, by the way) has completely different methods for introducing other constructs or doing custom logic. The F# version just uses lambdas (a sore spot on Python) to do this
Does it? It certainly makes strong use of pre-defined functions, but those are just functions and one can provide your own functions (even lambdas!) instead.
>You seem to think that's bad because of DRY principles and because it's easy to make an error.
Not because its easy to make an error, but because its unnecessarily verbose. Consider some variants on the same invocation, in relatively handwaved python-y, java-y, and f#-y syntax:
@flag('--flag', '-f',
type=validator,
help='a flag description')
f = new Flag().name('flag').short('f')
.validator(aValidator)
.description('a flag description);
type f =
| [<AltCommandLine("-f")>] Flag of validator
with
interface IArgParserTemplate with
member this.Usage =
match this with
| Flag _ -> "a flag description"
This isn't a knock on F# (necessarily), I'm specifically arguing that the API for the flag validator was badly designed. There's no obvious reason that argu should require Usage to be defined by a match block instead of part of the construction of the flag itself. Especially when the IArgParserTemplate interface must be implemented by all flags. They even have a special annotation to opt out of that interface after implementing it, `Hidden`. (This may be a bit ranty, but you can reduce this to "why isn't Description also an annotation"? (at which point you're doing with annotations exactly what Java is doing with method chaining or python with kwargs).
>The same could be done for the F# variant by removing them. That doesn't mean it's a good idea.
My point is that adding newlines is a totally reasonable thing to do if you feel that the python example is overly terse. Its a trivial, straightforward, and often recommended fix.
>I think you misunderstand how this works.
Perhaps I misunderstood. I was responding to the objection that two kwargs had similar names and therefore this was a wart. Such an issue can't be caught at compile time (since the code will be valid either way).
>What am I being "disingenuous" about? Am I engaging with you dishonestly?
Perhaps "unfairly assuming that everyone else is engaging with the same intent that you are" is a better way to put it. I see no signal that the so called "attempt to dismiss it" was such. I think that a much more reasonable way of interpreting it is that "From a cursory analysis, your unsubstantiated claim that F# is obviously superior to python in every way doesn't appear to pan out, can you better substantiate it?" (which they never did).
>Well hopefully this post has helped illustrate some of the considerations that make that comparison more reasonable?
Not particularly. Certainly those are features that might be useful in some contexts, but they're of dubious value in the context of scripting. And the cost is a parsing syntax that is both more verbose and more magical (again, it apparently name-mangles things internally for you just for funsies).
You brought up optparse-applicative, and it looks much more reasonable. In fact, minus haskell's love of inventing extra operators (<>, <$>, <*>), it actually closely resembles what the others are doing (and that the definition of an flag has a semigroup of arguments very closely resembles a set of keyword args for setting flag options). If F# used optparse-applicative, we wouldn't be having this conversation.
Python has explicitly discouraged functional-style for a long time, I don't think that's going to change. And frankly, the whole language is quite, I'd even say extremely, mutable; I'm not sure a functional syntax works well with that.
The problem is, python list comprehensions are very... specific. Add to that the brokenness of if...else and you get a very quirky and specific sublanguage that is hard for outsiders to read. And this is before we take into account the locals/scope/clojure discussion in the link and the PEP (which I frankly did not bother to read.)
On the other hand, you have a concept which translates easily to any language with first-class functions and lambdas. Even the syntax stays the same among languages which use "f(x,y)" for function evaluation and parameter passing.
/* This post is for those occasions when a list comprehension style is advocated over a functional style, which I know was not necessarily what you were doing in your comment. But I think the two points are valid enough on their own. */
It's a very opinionated statement on my part. `if COND then TRUE-CASE else FALSE-CASE` is the correct form to use, in my opinion. Python uses `TRUE-CASE if COND else FALSE-CASE`.
> > Python uses `TRUE-CASE if COND else FALSE-CASE`.
> And this is wrong -- this is not how Python if statements work.
Huh? Didn't you yourself say I was not talking about if statements:
> What you are talking about is a different kind of expression, similar to a ternary operator. It is not the same as if...else
In any case, I'm talking about the case that goes `TRUE-CASE if COND else FALSE-CASE`, as can be deduced from my typing `TRUE-CASE if COND else FALSE-CASE`.
> Even JavaScript has better support for functional-style programming.
Which shouldn't be that surprising considering originally Netscape were going to port Scheme to their browser before choosing to create a new scripting language with "Java-like syntax" (you can argue amongst yourselves just how Java-like the syntax really is).
Probably no better off as I cannot see many frontend developers taking to Scheme like they had with Javascript. So we'd still have eventually ended up with something nasty just to appease the lowest common denominator. Here's a horrible thought: maybe VBScript would have become the de facto standard instead?
You have to bare in mind that we - on HN - are inside a niche bubble and thus the kind of developers that read HN are very different to the kind of developers in many of the web shops outside of the big tech hubs like Silicon Valley and London. There are a hell of a lot of frontend devs who do still like to keep Javascript at arms length and a lot of "web developers" who are basically just Wordpress themers. I know this for a fact because I've worked with a great many of them before moving closer to London. These people are obviously still competent at what they do since not ever website nor development job requires "superstars" and the web framework - for all it's faults - offers a low enough barrier to entry that anyone can throw a page together. Including even those who don't do dev for a living. In fact one of the reasons the web took off was because of how easy it was for anyone to throw together a homepage.
Had Scheme been Netscapes scripting language instead of Javascript then I could easily see many of the less dedicated developers and hobbyists getting frustrated at S-expressions and such like. I mean I love functional programming but even I cannot deny that the learning curve is steeper and S-expressions are less readable (at least to an untrained eye) than Javascript is.
So my point was if Javascript didn't exist then I suspect there would be enough demand to either dumbdown / bastardise Scheme, or implement another scripting language which was more hobbyist friendly (also hence the VBScript quip).
A scripting language isn't necessary if you just want to throw a page together. It's only really necessary for today's web-as-an-application-platform model. If the front-end language were Scheme we would simply see fewer (but in all likelihood, better) web apps.
That's a massive oversimplification and you know it.
+ Just because something isnt "necessary" it doesn't mean it doesn't add value. The problem is just sites that make JS a requirement rather than an optional feature enhancement.
+ Youre talking about stuff from a too recent perspective.
Eg Before CSS came into its own, JS was the only reliable way to do mouse over effects (which can add a lot to usability even on regular web pages)
+ Just because JS is abused on current news sites, blogs and other sites that are really just static pages, it doesn't mean that Scheme wouldn't have been abused in the same way.
+ You also wouldn't see fewer developers writing frontend code. They would just use a transpiler (like we see with TypeScript et al) except instead of compiling from a stricter language (in TypeScripts case) it would transpile from a lazier language into a stricter one.
+ Or instead of the previous point (though more likely as well as) you'd still have a non-scheme language in the browser. Possibly even VBScript. Or maybe something derived from Perl. But I guess at least we wouldn't have a language monopoly on browser scripting languages.
Honestly though, I hate JavaScript just as much as you do. But let's not get carried away with our exaggerations :)
Just need something like blocks in Smalltalk. Wikipedia page on list comprehensions says Smalltalk-80 had list comprehensions and that was ~ 40 years ago.
Smalltalk also uses ":=" for assignment and "=" for comparison. In Pharo, VA and Dolphin at least does what this Python proposal does - return the value of the last expression.
Smalltalk (and for that matter Ruby) has weird feature that blocks and methods are different different concepts.
In my opinion the Python's explicit self argument is somehow cleaner approach than having distinct block and function/method types. You still need some kind of ugliness in order to implement super(), but for Python 3 that happens at compile time and the resulting syntax is reasonably sane.
As for the aforementioned method context issue CLOS/MOP takes interesting approach of macroexpanding the method definition into something like
Also of note is that in ST, there are no control structures, even if is implemented as method on Boolean instances which takes block argument, with true and false being instances of True resp. False with different implementations of #ifTrue: method.
Ain't going to happen. Python used to have lambdas, but removed all but the one-line variety.
The problem is that if you add blocks, then half of the added syntactic features the last decade is redundant as a block version would simply solve the problem a better. Which would create a lot of dead design, and that makes it a bad solution.
Are you maybe conflating blocks with chained iterator operations? Adding blocks to Python's current functional syntax would be pretty ugly. "foo.filter(bar).map(baz)" is nice even if you can only use named functions.
There are plenty of very good functional programming languages. If you prefer Haskell to Python, just go ahead and use Haskell. Python has a very specific design philosophy which is a focus of readability and the "preferably only one obvious way of doing it".
How a language with nested list and dict comprehensions can ever claim to focus on readability is beyond me, or look at typing and the mess that leaves behind. One obvious way of doing things while simultaneously mixing oop and functions arbitrarily in their standard library even providing camelCase and snake_case aliases(!) for parts of it. And why does the base64/b64encode return bytes and hashlib/hexdigest str?
Python is designed in such a strangly arbitrarily inconsistent, hypocritical and opinionated manner.
> even providing camelCase and snake_case aliases(!)
Sorry, but it's preposterous to bring up an implementation detail of the standard library that has long been in the process of being fixed. CamelCase was deprecated a long time ago in favour of snake_case; what is left of CC is for backward-compatibility and will eventually disappear. This is all documented.
The standard library is not the language, it's much messier and suffers from all sorts of issues that have nothing to do with the language itself.
You should probably look into the JVM or .net platforms, where the same libraries are available through multiple languages. For example .net have F# which is a very strong functional language, and the JVM have Scala and Clojure.
First of all you have indented 1 and False equally. Is that a typo? Or is it your opinion that the if should always consist of the if and the else branch without using the else keyword?
Secondly, if you want to return a value you need to use the return statement.
Also you wrote bar(foo) but foo was the name of the function, not the name of your parameter.
Yeah I considered that but it doesn’t make much sense to change Python to be like that.
It’s fine like that in Scheme and the other Lisps in part because well that’s the way they always did it, but it’s quite different from how it is and has been in Python.
If they want Lisp in Python they should look into Hy.
He/she was right, I was trying to write scheme python. I have spent quite some time writing python, but my sleep deprived brain wants to make everything into scheme :)
I don't know what you would lose by having if as an expression. It is easy to notice when it is used in expression context, and there is no extra computation that needs to be done.
It was sort of addressed with the trenary operator, but that quickly becomes ugly.
> I don't know what you would lose by having if as an expression
Well, if you turn if into an expression the way that you indicated then now you will also need the equivalent of the “begin” expression in order to be able to have multiple statements and/or expressions in either branch.
So then you are breaking backwards compatibility. Which makes it a non-starter from the get go.
And like I said there is also the fact that functions need the return keyword in Python if you want to return a value.
You could have the same looks as the regular if, but have it implicitly return a value. I was just too sleep deprived to be able to type out correct syntax on my phone.
Ruby returns the last thing in a method, which I feel is pretty sane.
I once wrote a (simple) compiler for a language exactly like that, i.e. LISP-type with indentation replacing parentheses. However, I found that this style becomes too complicated, because for long functions you can't easily see which parts have the same indentation. Perhaps special editor-support would help.
It feels to me that the use of lambda is pretty much discouraged in Python. The preferred way to dynamically create functions seems to be an inner def.