10 ms·
Write More Classes
- chewxy 14y agoYou can do the same with python's msgpack as the C# version Try this: packer = msgpack.Packer() serialized = packer.pack('stuff you wanna pack') unpacker = msgpack.Unpacker() unpacker.feed(serialized) print unpacker.unpack() I had originally used this, but then as my API scope extended to more than just using msgpack, having a common API interface for json (i.e. the .loads() and .dumps() method) was found to be more useful. And while I agree with most of the article, I don't think writing more classes is a one-size-fits-all solution. Classes IMO, only makes sense from a heavily OOP point of view.
- niggler 14y agoI dont think the OP was pointing to OOP when talking about classes. You can have classes without a meaningful class hierarchy that still conforms to the OP's desired programming style using protocols like the iterator protocol. A better way to phrase the OP's salient point while sidestepping the polemic of OOP is "Standardize and use protocols".
- the_mitsuhiko 14y ago> You can do the same with python's msgpack as the C# version You can't. The unpacker in msgpack for Python only reads full objects. The C# version lets me go into and out of streaming at will and even skip past objects without reading them into memory. The only thing the Python version of msgpack can do with the unpacker is buffering up bytes internally until an object is ready. That object however could still expand into a 10GB blob of data which I would then have to postprocess.
- splicer 14y agoPersonally, I find it more natural to use closures when parsing in Python, not classes. BTW, my day job frequently involves writing parser in C.
- notallama 14y agoif classes didn't have such a terribly verbose syntax in basically every language that has them, i'd be less opposed to using them. in java, c++, or c#, to add a variable to a class, you have to repeat its name 4 times. once to declare, once in the constructor parameters, and once on each side of the assignment. why am i writing the same thing 4 times for what should be a core part of the language? in haskell, you write it once (i'm not saying haskell's records are nice, but they got that part right). same with rust. and with a function, you write it once.
- benrhughes 14y agoFWIW, with C#'s object initialisation syntax you can cut that down to once too. eg public class Foo { public string Bar { get; set;} } var foo = new Foo { Bar = "Hey there" };
- Uchikoma 14y agoI've come to love Scala case classes: case class Foo(bar: String, foo: String = "hello") new Foo(bar = "Hey there") new Foo(foo = "Hey there", bar = "Hello") new Foo("Hey there")
- hythloday 14y agoCase classes, I believe, have a companion object with an apply method build for them, so you can get it down to: case class Foo(bar: String, foo: String = "hello") val f1 = Foo(bar = "Hey there") val f2 = Foo(foo = "Hey there", bar = "Hello") val f3 = Foo("Hey there")
- Uchikoma 14y agoYou're right!
- skrebbel 14y agoScala and TypeScript do it right, though.
- akaru 14y agoJesus, learn how to write before, you know, writing so much.
- Myrmornis 14y agoCan you translate those 11 words into German?
- redangstrom 14y agoSeems like he's asking for modularization so we can make special-case adjustments to libraries without completely monkey patching or rewriting them. Seems like a reasonable ask of a mature library. On the streaming verus resident working set argument... Most of what most programmers deal with doesn't have to scale to deal with huge streaming datasets, so it doesn't get the attention.
- mafro 14y agoCan anyone explain the statement about simplejson for me please? Some libraries manage to skip the token part. (I'm looking at you simplejson, a library that even with the best intentions in mind is impossible to teach stream processing)
- SoftwareMaven 14y agoWhen you are trying to parse extremely large documents or you only care about a small subset of a document, needing to load the entire parsed file into memory is problematic. Simplejson doesn't provide an API that allows for any other option. This is in contrast to something like an XML sax parser, that allows you to register for events like "a foobar element was loaded". You get the foobar element while all the other tokens are thrown out the window as soon as they are parsed. The complaint is that, somewhere, under the hood, simplejson is doing that token parsing, but because of their API, a user can't plug into it.
- masklinn 14y ago> The complaint is that, somewhere, under the hood, simplejson is doing that token parsing, but because of their API, a user can't plug into it. I read it exactly the other way around: simplejson skips an internal tokenization step, so even if you fork the library it is pretty much impossible to make it streaming, because there's no token stream to handle stream state.
- hyperpape 14y agoThat's more or less right, masklinn. To the above writers, look at the Python source (I can't vouch for how the C implementation does things). It's not beautiful, but it's also not hard to understand. The effective work is all done inside a big function. To customize it, you'd have to split that function into pieces and then glue it back together using a class or your own function coordinating the parts.
- comex 14y agoIMO, the Flask example is pushing it - considering how large a job rendering a template is, the number of customization points is probably appropriate, but at some point you're going to get lost in a rabbit hole of wrapper functions that call wrapper functions and end up with three equally appropriate levels you might hook into because the code was written to keep you from having to duplicate a single line of code from the library in your alternative implementation-- never mind how confusing that makes things to the casual debugger (who wants to get to the actual meaty code to see what's wrong with it). Flexibility is useful, but it must justify the loss of simplicity. But yes, in the JSON cases, having more flexibility than a single 'loads' is clearly justified.
- DeepDuh 14y agoSome note: When it comes to debugging I've rarely seen a better stacktrace output than the one Flask presents you in debug mode (provided by Werkzeug). It actually lets you open the damn sourcefile right there. Right now I'm (mis?)using Flask together with CouchDB to build a db application where you can dynamically define your entities, fields, code hooks and so on at runtime and using Flask for this has been great so far. Ronacher's attitude clearly shows through in how easy I could get Flask to behave in ways it was possibly never meant to (dynamically add view definitions, document mappings and so on). So Armin, if you read this, thank you, it was a joy spending time with your framework (and source code) over the weekend ;)!
- the_mitsuhiko 14y ago> IMO, the Flask example is pushing it - considering how large a job rendering a template is, the number of customization points is probably appropriate, but at some point you're going to get lost in a rabbit hole of wrapper functions that call wrapper functions The actual implementations do a bit more. I have pretty much overriden every single part of that at one point, if for no other reason than debugging. Some of those hooks were added later because people requested them.
- benatkin 14y agoI find that I like it when classes store configuration that's known at load time, rather than state. That way it's a lot like a Common Lisp program, except multiple programs can run in the same process, have different configurations, and communicate with each other.
- haberman 14y agoI think the real point the author is trying to make is: use modular, layered designs of composable components, instead of monolithic APIs that only have a single entry point. The single-entry-point model imposes costs and decisions onto the application that are hard to work around. I think this is a good point. I think that it's hard to get from there to "more classes are always better" though. More classes don't always make a design more flexible. You have to consciously design for flexibility and reusability, and even then it takes a lot of deep thought and hard work to get your interfaces right, such that they are truly reusable in practice.
- bhntr3 14y agoQuoting from the end of the article: "And our solution to monolithic code in Python are classes." So, I guess the question I ask as someone who doesn't write a ton of python code is: Is that true? And if so, why? Is it not possible to compose a layered API that doesn't rely on inheritance / classes in Python?
- dbaupp 14y agoOne can write Python in a procedural manner, and even in a functional way, so one can use the same techniques as are used in those paradigms (OO is probably more natural for Python though).
- w0utert 14y agoYou can also easily write component-based architectures in Python, for example using zope.interface/zope.component (ie: the modules only, not the full Zope CMS). For example the Trac bug tracker/wiki works like this, and for its purpose I think it's a perfect fit. It allows re-usable and pluggable components with declared, adaptable interfaces that are not restricted by some hierarchical taxonomy the framework designer came up with. At the end of the day there will always be different ways to build layered architectures using re-usable components, and classes are just one of them. I'm pretty sure the author of the article didn't intend it like that, but 'write more classes' doesn't sound like great advice for novice/inexperienced programmers.
- contingencies 14y agoWrite no classes! Joe Armstrong: "I think the lack of reusability comes in object-oriented languages, not in functional languages. Because the problem with object-oriented languages is they've got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle. If you have referentially transparent code, if you have pure functions-all the data comes in its input arguments and everything goes out and leaves no state behind-it's incredibly reusable. You can just reuse it here, there, and everywhere. When you want to use it in a different project, you just cut and paste this code into your new project. Programmers have been conned into using all these different programming languages and they've been conned into not using easy ways to connect programs together. The Unix pipe mechanism-A pipe B pipe C-is trivially easy to connect things together. Is that how programmers connect things together? No. They use APIs and they link them into the same memory space, which is appallingly difficult and isn't cross-language. If the language is in the same family it's OK-if they're imperative languages, that's fine. But suppose one is Prolog and the other is C. They have a completely different view of the world, how you handle memory. So you can't just link them together like that. You can't reuse things. There must be big commercial interests for whom it is very desirable that stuff won't work together." - Peter Seibel, Coders at Work: Reflections on the Craft of Programming
- skrebbel 14y agoNice quote, but did you read the article at all? Something else I want to mention: what's written above will most likely result in some sort of warmed up discussion in regards to object oriented programming versus something else. Or inheritance versus strategies. Or virtual methods versus method passing. Or whatever else hackernews finds worthy of a discussion this time around. All of that is entirely irrelevant to the point I'm making which is that monolithic pieces of code are a bad idea. And our solution to monolithic code in Python are classes. If your hammer of choice is Haskell then use whatever the equivalent in Haskell looks like. Just don't force me to fork your library because you decided a layered API is not something you want to expose to your user.
- contingencies 14y ago
- Ingaz 14y agoI could not understand how classes can help with JSON-example. It looks the same as XML SAX vs DOM: you feed NN-Mb to DOM-parser (SQLServer xml-datatype for example) and you have problems. No matter: classes or functions.
- cgopalan 14y agoBy default, I go with the inclination to put code in functions when I tackle a project, because most of my projects are just products and applications without a public API and testing or debugging the flow in a functional paradigm is much simpler. I can take a function and plug it in the interactive interpreter (in Python) and run it without needing to instantiate other state that's needed for the test. However, in certain cases, like where I need to write a public API, I have found that having classes as wrappers to the functionality helps it a bit. So really, the "stop-writing-classes-unless-you- absolutely-need-to" guideline still holds true for me.
- Uchikoma 14y agoAs Java was mentioned. The Java API is very nice, but there is a layer missing on top. The Java API was written with a early 90s mindset and got most users after 2000. This goes for most Java APIs. IO, Swing, ... The idea is to have LEGO building blocks (BufferedReader) you can plug together. But you need to plug them together all the time. The missing layer e.g. is IO.readIntoString(file). Apache IOUtils, StringUtils etc. fill in this layer for many Java APIs. The one API that is not powerful enough is Collection. There you have the top layer without the LEGO building blocks. Compare this to the Scala collection API which has the top layers and the building blocks. For a good API you need both, building blocks to tailer to your specific need (20% of the time) and an easy top layer (80%) to prevent writing the same stuff all the time.
- dm3 14y agoTo be fair, they have mostly fixed the IO API with Java 7 (NIO2). This covers various Files# helpers as well as a proper Path abstraction.
- Uchikoma 14y ago15 (?) years late ;-)
- RyanZAG 14y agoWhy does that matter? If version 1 is bad, we should never use any newer versions because version 1 was bad? The iPhone didn't support 3g in the first version, so we shouldn't use current iPhones?
- icebraining 14y agoIn some ways, yes. Plenty of people moved to other languages because of those handicaps, and they have no good reason to come back.
- RyanZAG 14y ago
- jakejake 14y agoI must be way out of touch because I had no idea there was an anti-class movement for which the OP has to argue for more classes?
- xentronium 14y agoHe mentions stop writing classes talk in the very beginning.
- mhd 14y agoWhich he doesn't really address, nor do the two opinions necessarily clash (at least not totally). Diederich's talk is mostly about avoiding to write superfluous classes. Quite often a simple module with one or two function suffices. His examples look quite different from those in the blog post. (Although I have to admit that I skimmed most of them, as I'm in serious eyeroll territory whenever I see the old Java IO API used as a bad example again.)
- xentronium 14y agoGood point. I think author doesn't actually care as much about classes per se as he does about interfaces and extension points. Complexity might get out of hand incredibly quickly, though.
- Locke1689 14y agoThis is a bad article. First, write nothing. If you can solve your problem without writing code (or even better, by deleting code), that is the best solution. Next, write code which fits your architecture. Sometimes functional composition is the best system for representing your computational structure. Tree operations, for example, are especially amenable to recursive function-based computation. Sometimes, when you are writing a state machine, for example, an object is the best possible representation. Objects are entirely focused around hidden-implementation finite state machines, and thus mirror your computation almost exactly. Funny how most people who have only practical training in a handful of languages and no programming language theory at all tend to advocate for the one style of programming that they know well. When all you have is a hammer... Note: The small paragraph at the end of this article seems to hedge by agreeing with me and essentially calling the reader to disregard what he previously wrote. If he had followed my step one he could have avoided writing the entire article. Think of the complexity saved!
- barrkel 14y agoFrom what you wrote, I'm not sure you read the article. It's not about objects vs functions; it's about hiding a lot of functionality behind an overly-simple interface, with particular attention paid to an example of a parser that takes a whole string rather than a streaming interface. What annoyed me most was his constant apparent conflation between bytes and characters. But I didn't really see much evidence of treating all the world like a nail.
- Locke1689 14y agoI know what he was trying to say, but I don't think that's actually what he said. What he should have said is that you should pick an interface that suits your computation. The problem was not that the interface was too simple, but that it didn't fit the computation. One can easily imagine an object-oriented interface for the same wrapper computation that also hides the true nature of the computation.
- lucian1900 14y agoI saw no conflation. Everything there is a byte.
- leppie 14y agoSo in Java, a parameter named 'filename' is automatically aliased to 'path'? Doh... static String readFirstLine(String filename) { try { BufferedReader br = new BufferedReader(new FileReader(path)); .... So people writes this everyday, yet still fail to do it correctly... Also, this article was written 1 day in the future. The future looks bleak to me...
- 10098 14y agoNo. Don't write classes. Write useful abstractions. It doesn't matter what the abstraction is (a class, a function, or something else you programming language supports)as long as it fits your current architecture and can be easily modified for possible future extensions.(I will agree with the author that one giant monolithic function is probably a bad abstraction).
- ankitml 14y ago+1, one of the best points in the discussion so far. :)
- dakimov 14y agoThe programming industry walks around in circles and there is no beacon in this darkness of ignorance. There is no professional culture and new generations of developers successfully forget all the experience previous generations have accumulated. Also, some piece of advice from a seasoned programmer to the web-programming-children: if you are not really a serious developer, if you write your freaking websitee on Django, or whatever a framework there is, you don't really need a methodology, because you are doing an easy task, you can write in whatever language/style/paradigm you like, even on Brainfuck. But please don't extrapolate your humble experience to the entire industry and don't tell people working on large complicated (real) projects how they must write code, because they have some experience you don't have.
- pemmigiwhoseit 14y agoDon't you think writing about it is a good to minimize the amount forgotten generation to generation? Even if they write something that is flat out wrong, someone will probably tell them and everyone might learn. Also I wouldn't be so quick to dismiss building websites on Django (or any other framework) as easy. I imagine many things are easier, but I bet many things are harder as well, I'd think you'd be surprised at how quickly "good" designs go to shit when business requirements change every two weeks and how this effects development.
- paganel 14y ago> But please don't extrapolate your humble experience to the entire industry and don't tell people working on large complicated (real) projects how they must write code, because they have some experience you don't have. Don't take this the wrong way, I mostly agree with your points regarding the missing "professional culture" among us programmers, just yesterday I sent an email out to our internal list urging my coworkers not to use one-letter variables anymore and explaining to them why that is bad, but when it comes to web projects somehow not being "real" programming I'm afraid you're wrong. It is true, some momentum was lost when the "open-data" mantra that was flying in the air around 2004-2005 gave way to today's walled gardens and one-page AJAXy apps, but there are still interesting things happening on the web.
- klibertp 14y ago
- fab13n 14y agoThere's a fairly effective rule of thumb: classes--or more accurately objects--are a way to cleanly encapsulate state. If you rely on a mutating state, you probably want an object/class. If not, a function is often better. Now you can bundle related functions together in a structure, but this structure is morally a module, not an object, let alone a class. Some languages will force you to encode those modules as classes / prototypes / singletons, but that's just a design pattern to circumvent a limitation of the language.
- INTPenis 14y agoA good example of classes in Python is the dnspython library. It's tasked with returning records from parsed zones and every single record is a class. I like it and I don't understand the first sentence of this blog post. Try not to put too much weight on what others tell you, make up your own mind. It's a classic human issue.
- ankitml 14y agoThe discussion on the thread has rotten to much extent, the article doesnt address different languages or paradigms, he just mentions about classes being better than block codes in python. Something more rudimentary, a valuable piece for quick-fix programmers, for hobby programmers. Classes are one way of abstractions. So he asks for using abstractions instead on no abstraction (block code) but if you have other ways of abstracting code, please go ahead. THis article is good for people with few / no tools, people who are still learning
- viraptor 14y agoI really don't like the way this was presented, even if I may agree with the idea underneath. It's not about classes at all. It's about better design, but even the examples are strange. Just from the JSON example: - Why do I need a class for streaming JSON - Python's got a perfectly good `yield` for returning tokens in such situations. - Why would I ever design the JSON library to be extendable at the tokenizer level? If you need serialiser / deserialiser, why not just provide a map of types to callbacks / callback as a parameter? Do you really want to extend JSON format itself? - The 2GB JSON example is just weird. If you care about such use cases, you a) most likely have a limit on data size at webserver level, b) use proper formats for handling that size of data (I really doubt there's no better data representation once you get to GB sizes). I see his point of view, but he's arguing for one single "hammer" solution, rather than arguing against the monolithic design. His story seems to present some weird back-story really: "I needed to make my data easier to handle, so I started automatically serialising objects into JSON, then they became huge so I have to start streaming them otherwise just parsing of them takes way too long".
- the_mitsuhiko 14y ago> - Why do I need a class for streaming JSON - Python's got a perfectly good `yield` for returning tokens in such situations. See the msgpack-cli example at the bottom. Say you have a function that returns a generator for tokens in Python. You would need another function that builds objects out of them. How do you customize how objects are being built? A class makes that simpler because each of the methods are extension points you can override. But yeah, a token stream would be much appreciated. > - Why would I ever design the JSON library to be extendable at the tokenizer level? For instance if you want to skip past objects. That's what we're doing for instance for unwanted input data. That implicitly also prevents hash collision DOS attacks because we never build hash tables for things that we don't want. It also gets rid of the suspension of execution when a garbage collector runs and cleans up a nested structure. I can make you tons of examples where the "build a tree, reject a tree" approach destroys an evented Python server. > - The 2GB JSON example is just weird. If you care about such use cases, you a) most likely have a limit on data size at webserver level, b) use proper formats for handling that size of data (I really doubt there's no better data representation once you get to GB sizes). Try using a streaming JSON API like Twitter's firehose. Most people just give up and use XML because there is SAX or if they go with JSON they newline delimit it because most libraries are horrible or useless for parsing streamed JSON.
- Offler 14y agoCould not agree more with the idea that people should write more well designed, OO code. This exact same issue is prevalent in JS land and it leads to people saying JS can't be used for real development, when in fact the problem is that the code they write is a mess and that's what's holding them back.
- wyuenho 14y agoPeople don't talk about this anymore for some reason, but I think both Jack and Armin are really just approaching API design in 2 different ways - top-down and bottom-up. The problem is, most people stick with the same approach through out and end up ignoring that programmers are mere mortals too, and they have human needs. Expanding on Armin's dichotomy, top-down designs like Python's open() or jquery plugins start with giving 70-80% of users APIs that are as simple as possible for their most frequent use cases while shielding them from the sausages underneath. Bottom-up designs like Java's standard library or POSIX start with LEGO building blocks that solve the most fundamental pieces of largely academic computer science problems and just give them out to their end users and expect them to be able to assemble Tower Defense by solving this LEGO puzzle first. The problem with sticking entirely to their 2 approaches is that you end up either ignoring power users or making healthy adults with normal IQs feel stupid. There is no reason you can't serve 100% of your user base by incrementally evolving your API approaches and provide 2 sets of APIs within the same library, with the top-down easy one shielding the bottom-up sausage factory that takes care of the meat grinding for you. Most API designers don't realize this and won't ever go there. Extremists and egoists with lots of opinions will spending hundreds of man years to promote their One True Way of doing things. They'll say things like "no leaky abstractions!" or "these enterprise people are just making things too complicated to create jobs!", when the simple truth is probably just that they don't understand how people think. Make your libraries easy to do things that are easy, but make hard things possible too.
- jessaustin 14y agoMake your libraries easy to do things that are easy, but make hard things possible too. IME Ronacher follows this dictum very well. I'd suggest that anyone who wants to see this try his Flask and Werkzeug packages. As for storing extra state, about which many here have complained, I've found it really helpful that I can set werkzeug.http._accept_re to my own RE when I want to do something weird with media types. That is state that the vast number of users won't need to touch, yet the fact that it exists makes life better for someone who does need it. I'm sure there are numerous other examples I haven't had to bother with yet. Would we really be better off if this RE had to be passed in every time we handled a request? (Although I would understand if you argued this value should be stored in an object not in a module. I haven't needed that yet.) On the other hand, routing to and handling resources with these packages is typically done with functions and decorators only, although the route decorators are methods of the application object. So Ronacher is not any sort of hardass about classes; he just does what works.
- pyeek 14y agoI think there is an aspect to this article that won't be understood unless you're really part of the Python community. For a while now, one of Python's selling points to users from other languages has been "you don't need to write all that code" (most likely directed at Java), more specifically you can simply use a function rather than having to define a class just to define static methods etc. Over time, this has grown into the mantra of "You don't need a class just use a function/module". A lot of people seem to follow this viewpoint blindly, so much so that they consider it to be "Pythonic"; the way you "should" write python. I think Armin's post is somewhat in line with the following post from the google testing blog. http://googletesting.blogspot.com/2008/12/static-methods-are-death-to-testability.html http://googletesting.blogspot.com/2008/12/static-methods-are...
- lucidguppy 14y agoPeople need to write better titles. When someone wrote "stop writing classes" the talk exactly when to write them. When you have an object with two methods and one of them is init. This whole discussion is absolutely wonderful and is what is great about the python community. Write classes when they facilitate code reuse - don't write taxonomies for the sake of it.
- iso-8859-1 14y agoThe Java would have been prettier if he had used the new try-with-resources from Java 7.
- ehutch79 14y agoI think the original video this is a response to, was really just saying 'A class should not be one method, that's a function'
- pekk 14y agoThat was only one of the things the video said. Overall, it was pointing out how overuse of classes was resulting in lots of extra complexity and additional lines of code, making programs harder to understand and maintain
- deleted 14y ago[deleted]
- jerf 14y agoIn Python, the distinction is less strong than it is in some other languages. Since a class can implement __call__, allowing instances to be directly called like a function, even if an API specifies a function, you can pass a class instance in with a suitable __call__. So using functions doesn't have to tie to to it being a function forever and ever in the future (or breaking reverse compatibility, the way it does in most other languages. This isn't a perfect answer to the objections, but the objection is at its most weak in Python.
- quasque 14y agoI must have missed something in my reading of the article - what is the relevance of "Condoms for Onions" to the essay?
- cwp 14y agoIs there anything to this debate that doesn't boil down to style? Python has a pretty decent object system. It also has first-class, nested functions with closures. You could write good OO code or good functional code according to your taste, and python libraries don't seem to strongly prefer one style over the other. I'm not terribly interested debating OO vs. functional in the general case, but I am interested in the pros and cons of each style in specific contexts. For example, Python's lack of variable declarations sometimes leads to bugs involving scoping. (I've run into this a couple of times myself). Does this quirk become more of an issue in heavily functional code? Are there other language quirks that become troublesome in heavily OO code? In what circumstances might one style be preferred over another?