Moka is still in an alpha stage; but that being said, I'd love to hear some feedback. Feel free to browse the code, it should be pretty straightforward to any python dev.
> def user_logged(users):
> return all(user.is_logged() for user)
Because the method lookup is done per instance, rather than assuming everything is the same class.
3. You're writing jquery in python
Python chose not to demand that all iterables implement a series of operators, but provides them as functions within a module. The rationale is that it is easier to add new functions within itertools, and there is far less to do to correctly implement the iterator protocol
You can see this in the "".join(foo) operator too. Instead of demanding all iterables support join, string takes an iterable as argument.
Making readable and maintainable python comes from using the existing idioms within the language and used within the community. Your proposed solution isn't readable, and it isn't pythonic.
What is with all the hate lately on anyone who dares to write Python with different semantics than employed by the stdlib builtins?
'You've invented your own incompatible dialect of python. '
It isn't a "dialect." You don't need a different parser. There are no macros. It is simply a container with slightly different semantics than the stdlib ones.
"You've broken composition, objects and the general design choices of python."
No those all still work and if you don't like his semantics don't use them. They are perfectly readable and easily understandable if you having a passing familiarity with the pattern he is using.
tl;dr : I don't see what the big deal is, people are allowed to write Python however they want.
No I did read your post. Actually I read all of them. My point is he didn't "break" composition. In isn't like you import this guy's library and suddenly function composition no longer works. Breaking isn't the right word. It is agressive and wrong on a semantic level.
You could say "You functional list does not properly support composition. Here are some problems." You posts were needlessly combative. I only spoke out because I have seen this kind of thing cropping up recently with respect to Python functional libraries.
"people are encouraged to write python other people understand."
As I said, the semantics of his library are reasonably clear on inspection doubly so if you read the docs.
EDIT:
I just re-read some of your other posts on this thread. You really do keep banging on "He made his own 'dialect.'" To clarify, no he didn't. Making a library isn't a dialect. A dialect needs to be a significant enough departure that you would actually need a different parser/interpreter. That is a "dialect." If you would like I can point you actual Python dialects. This is a library. Deal with it.
"the semantics of his library are reasonably clear on inspection"
does .remove() mutate in place or not ? depends on the constructor!
this would be an example of breaking composition - you can no longer use a function built for an immutable list on a mutable list. I am sure I explained this too.
a dialect is a style, in linguistic terms, if I speak a dialect of english, I still speak english.
if I needed a different parser/interpreter I am pretty sure that is the stage of 'new language' where new syntax and semantics are introduced.
the 'dialect' he is introducing is a arc/clojure inspired syntax for what /already/ exists in python.
thanks for the semantic pedantry! the long and the short of it is that if you want to write python, write python that looks like python. not like scheme or clojure.
as someone who gets paid to maintain shitty code, i'd rather people stuck to the existing idioms of the language, rather than blindly copy paste them from another language.
"if I needed a different parser/interpreter I am pretty sure that is the stage of 'new language' where new syntax and semantics are introduced.
the 'dialect' he is introducing is a arc/clojure inspired syntax for what /already/ exists in python."
No a dialect is another version of the language. For instance python3 is a dialect of python, just as python2 is. They are not 100% compatible.
"as someone who gets paid to maintain shitty code, i'd rather people stuck to the existing idioms of the language, rather than blindly copy paste them from another language."
It is not his problem you see a lot of shitty python. It is also not his problem if you want every one to stay stuck in the poor design choices that were made in the stdlib a long time ago. You can fight evolution of languages or you can embrace them. I don't see you advocating for "thou" in English ;-)
"the 'dialect' he is introducing is a arc/clojure inspired syntax for what /already/ exists in python."
"syntax" --> no thats is what is defined by the grammar of python. This is python syntax.
"a dialect is a style, in linguistic terms, if I speak a dialect of english, I still speak english."
No it is a mutation. But I agree with you if you speak a dialect you still speak the "language." Olde English is still English.
---
However my main point remains. You are needlessly combative in this thread. He made a library you attacked him like he insulted the Pope of Python. I am sorry you maintain shitty Python but you don't need to take you anger about that out on some poor guy on hacker news.
However, about the #1, I have to agree. I've added the saving() recently and had a bad taste about it. The right way to do it might be to make it mutable by default and only toggle it off in certain circumstances.
Please note that everything done by the community is still perfectly usable; in fact, it's even easier to use useful high-level functions.
I.e. map(str, range(1,10)) or List(range(1,10)).map(str); is syntactically different but does the same job.
#1 the correct way to do it is to have the methods perform the same action on mutable and immutable objects.
this is the only way to guarantee composition. you do not change the semantics of shared methods.
#2
for both of these examples you give 'to use whatever you want'
"".join(['a','b']) is how everyone else does it in python code.
it isn't about things in the community being usable within your library, it is about your library being /unusable/ within the community. You re-invent new and awkward ways to do standard things without standard idioms.
'i've just invented a whole bunch of new semantics for things so it will be readable'
readability is about /convention/. readable to whom? pushing your own love of jquery method chaining only serves to ostracise those already somewhat knowledgable within python.
I'm not sure why you created a fake account to answer this but thanks again for your time. You're right that it was built for my own taste using simultaneously other languages (clojure, arc and js for instance). At the end of the day, what's important is that it increases the quality of the code and this is my goal with Moka. This is still in an alpha stage, but I'll definitely tweak it based on good feedback (Like yours) Feel free to drop me a line: phzbox at gmail if you want to continue this discussion.
It's totally legitimate for you to write this according to your own taste and use it. I don't think it's bad for Python, or anything like that. You are not an idiot and I am sure you can write interesting and useful programs in Python.
But I do agree that it is not 'Pythonic' - except maybe in the trivial sense that it's written in Python.
I would be happy to go into more detail if you really want it.
give it a break. sure, we should be pythonic when we're programming at work and our code is likely to be maintained by others. but there's absolutely nothing wrong with pushing boundaries and learning by exploring and even - shock - getting things wrong.
you made some good points (particularly the fact that you lose dispatch by instance), but you don't need to keep jabbering away about the same points.
writing your own dialect of the language can be fun, but he is presenting his own style as 'pythonic' as opposed to the actual pythonic style built from functional composition - rather than method chaining.
I am banging on about a lot of the points because it seems very hard to explain to him that using clojure/arc/jquery styles is very very unpythonic.
you seem to go a long way to reinvent python builtins like reverse() any() all() enumerate(), itertools and functors. another python style you violate is that mutable methods return None in general.
from the outset you haven't made any attempt to learn python style. go and read the zen of python.
I only picked on a handful of examples, but wait! theres more - almost every example on your page has a way to do it in python. that other python developers use and understand.
#chaining example:
you say 'we believe chaining constructs are easier to read and maintain than deeply nested expressions.'
different methods have different magic attached: it isn't obvious from the outset why update takes named args but keep takes args are named operators
> List([1,2,3]).keep(gt=1)
becomes
> [x for x in [1,2,3] if x > 1]
# you reinvent all
> List(range(1,10)).all(lambda x: x < 100))
becomes
> all(x < 100 for x in range(1,10))
# 'compact' example
> List([None, 0, 2, []]).compact()
becomes
> [x for x in [None, 0 , 2, []] if x]
# 'list is empty' example
> List([]).empty()
becomes
> bool([])
# 'sort'
> List([5,3,1]).sort()
becomes
> sorted([5,3,1])
'uniq'
> List([1,1,2,3,2,1]).uniq().sort()
becomes
> collections.Counter([1,1,2,3,2,1])
for every example you give, there is an equivalent piece of python code to do it, designed in mind with the rest of python. the built in operations give you flexible control over the evaluation too - you can have generator expressions and list expressions. many iterable versions of the standard operators exist in itertools.
really, this is the least pythonic thing since ruby came out. it seems I can only spell this out to you by elaborating through your jquery library and presenting you with python code python developers understand.
please stop re-inventing python without trying to understand why it looks that way first.
Thanks for all the good points. To be honest, I'm not sure what you're trying to say. I've been coding in python for years and Moka was built from my annoyance using functional paradigms with the stdlib.
As you clearly showed, Python doesn't have an uniform syntax to deal with this paradigm. I.e. there are lots of different constructs and, as you said, common idioms or patterns. Have you already argued with a Java programmer saying that these 'Design Pattern' are just a limitation of the language.. whereas in Python you'd probably just use a simple Dict (or whatever)? I'm sure you did. I feel the same with the idioms and patterns.
See, in Clojure (And Arc, and even Ruby), there is an uniform syntax.. whereas in Python we've got itertools, list comprehension, builtins map/filter, random builtins functino such as sorted().
I have to agree with you that Moka is not Pythonic in the There's only one way to solve a problem as we add a new way. However, Moka was created because there was so much inconsistent alternatives..
However, Moka is Pythonic in how it behaves. God knows I could have use all nifty hacks to make it behaves magically.. but I chose to take the explicit route by overriding list/dict. I could have used string interpolation for function (See http://osteele.com/sources/javascript/functional/); but instead went the Pythonic way with standard lambda functions. Maybe you are right about the operator keywords shortcut (i.e. using List().keep(operator.eg) instead of List().keep(eg=); However, I still feel it was a way to make it even easier to integrate with existing tools from the stdlib.
Lastly, you said:
zen: flat is better than nested
> Dict(a=1, b=2).update(c=3).rem(lambda x, y: x=='a')
^^^^^^^^^^
This is not nested, this is chained.
This, however, is nested:
# Taken directly from the itertools stdlib page.
next(islice(iterable, n, None, default)
# Here, this is not nested.. this is chained:
def logged_user(self):
return (self.users
.keep(User.is_logged)
.keep(lambda u: u.is_active)
.map(lambda x: User.objects.get(id=x)))
Maybe I don't understand what you mean by "incompatible dialect". These are simple classes inheriting from list/dict. Basically, it simply add a couple methods to these objects. I.e. You can still use all the idioms/patterns; these are list/dict. I'm not creating a new paradigm, or writing a new language by tweaking the syntax. There's really nothing magic going down here. I.e. you can do
return List(..).update(..).update(..)
Instead of;
l = List(...)
l.update(...)
l.update(...)
return l
You can still use lisp comprehension, itertools, etc. In fact, I could pass a moka.List to your existing code and you wouldn't even notice it. (And you do, it's a mistake that I'd fix).
I do agree that I might have been overkill with some useless methods and I'm thinking about removing them.
(I.e. such as 'join' as it's really not needed)
A good source of inspiration: Ruby's Enumerable[1] and Scala's Iterable[2]. Toguether, they have one of the most complete functional collection methods library.
two small suggestions: change .saving() to .mutable() and .rem() to .remove() (or filter(), reversing the semantics, since that is already in python) (every other method is a full word).
also, maybe .mutable() would be better as a flag in the constructor. it makes no sense to use it in a chain (in fact, that could be confusing) and is the kind of thing you should probably fix on construction rather than changing later.
ps. it's not clear to me that this is better than list comprehensions, which already do much of what you have.
Thanks for the feedback; I agree with you. Here's the reasoning behind these odds choice..
List is-a list, and Dict is-a dict.. and thus, I'd like to make it possible to safely pass these objects to already existing code. If I rename .rem to .remove, I'd break that.
The reason for the chaining saving() is because it's useful to be able to toggle it. For instance:
EDIT: using the timeit module on these, the Moka version is about 18 times slower on my machine... not that this matters much if it's a much nicer way of expressing the program
The doc at http://www.phzbox.com/moka/index.html is pretty complete. The only thing I find is missing is a few words about efficiency. Does calling List(l) copy the list l for example?
Optimizing for performance is the next big step of Moka and I understand that it's the breaking point for lots of python developers. So, I'll make sure to work on that and provide realistic benchmarks.
As for the copy, yes it does as it tries to act like a builtin list as much as possible. (In fact, moka.List inherit from list). When it's in immutable state, a new list is returned at each step. If it's in mutable, self[:] = (..) is used.
This is indeed minimalist, there's less code in it than I'd expected.
The idioms it introduces do clash with Python ones, but on the other hand, I've been learning Clojure lately and my Python code has more comprehensions than previously. I'm not sure what to think.
It's fairly new but feel free to browse the code; it should be pretty straightforward to you. I've tried to provide good examples to make it easy to learn/use on phzbox.com/moka/.
Moka is still in an alpha stage; but that being said, I'd love to hear some feedback. Feel free to browse the code, it should be pretty straightforward to any python dev.