6 ms·
Couldn't you just do something like this? (let ((reader (csv-reader "data.csv"))) (loop :for row = (reader) :while row :collect row[0] :into vals
by gruseom 15y ago
Couldn't you just do something like this?
(let ((reader (csv-reader "data.csv")))
(loop :for row = (reader) :while row
:collect row[0] :into vals
:collect row[1] :into avgs
:collect row[2] :into stds
:finally (list vals avgs stds)))
That's in the bastardized Lisp that I favour, but any language with closures should allow it. The point is, no classes.
I don't want to seem fanatical about this - your example is a fine use of objects. But unless I'm missing something, one can do just as fine without them, and with a little less code (since you don't have to define a class).
I (maybe!) have no problem with classes for things that, as you say, are concrete and have clear interfaces. But then, when things are concrete and have clear interfaces, there are a lot of nice ways to do things. Making things well-defined and clear in the first place is the hard part.
- scott_s 15y agoHonestly, I'm not sure if you can do that in Python - one of the side-effects of not writing classes in Python is that I'm also ignorant of the behind-the-scenes stuff that goes on with them. You may be able to get something close to what I wrote with a function that returns a closure, but I don't think you can get exactly the for x in y syntax. As explained in another post on HN's front page, "A Guide to Python's Magic Methods": http://www.rafekettler.com/magicmethods.html#sequence http://www.rafekettler.com/magicmethods.html#sequence, you need to define the __iter__(self) method on an object to support that style of iteration. (And that method needs to return an iterator object.) And that brings us back to the same point I already made, but I think it's so important that I'll say it again: the abstractions you choose to represent concepts in your program will be determined by what is easy in the language you're using.
- gruseom 15y agoAh I didn't realize Python's limitations (if it has them here) were part of the mix. I strongly agree with your general point and will go one further: this has a huge impact on the kind of system that over time gets developed in that language. It conditions what gets written, which conditions what's easy to do next and eventually what's possible to do next. By making different things easy, languages bias the programmer toward thinking different thoughts. I wish more people would figure this out: it would make discussion of these things much more interesting. (Instead, all we really do is comparison of code snippets in different languages, which kind of misses the whole point.)
- scott_s 15y agoSomething that the objects I had in mind have in common: they're resources. They fundamentally require a handle, and associated functions that accept that handle as a parameter. There already is a tight coupling between the resource handle and those functions. Such resources, off the top of my head: files, sockets, locks, condition variables.
- BrandonM 15y agoA function that yields instead of returning values results in an iterator. One does not need to define a class at all.
- empthought 15y agoCheck it out... def fibonacci_sequence(): a, b, c = 0, 0, 1 while True: a = b; b = c; c = a + b yield b >>> fibonacci_sequence() <generator object fibonacci_sequence at 0x108055550> >>> list(itertools.islice(fibonacci_sequence(), 0, 10)) [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] >>> for n in fibonacci_sequence(): print n if n > 13: break 1 1 2 3 5 8 13 21 >>>
- scott_s 15y agoYes, I had forgotten about generators, pjscott and jules provide great examples for the csv case above.
- icebraining 15y agoA closure is essentially just a class by another name and with slightly different syntax. Yeah, you can replace classes with them, but to what purpose except to be able to say "no classes"?
- barrkel 15y agoObjects are collections of closures that share state. As soon as your interface has more than one closure, you've more or less reinvented objects to solve that problem.
- gruseom 15y agoBut (a) classes demand names, and that's critical, and (b) I almost never need more than one closure. Oh, and (c) this is far from the organizing principle of the system.
- barrkel 15y agoDictionaries, lists, tuples and other structures also demand names. Getting away from classes does not free you from the need for names; and in a dynamically typed language, it requires even more attention to names - see my other comment http://news.ycombinator.com/item?id=3719056 http://news.ycombinator.com/item?id=3719056 I almost invariably find I need more than one closure down the road, outside of simple predicates handed off to algorithms. Even for such simple things as specifying a sink for output (out :: string -> ()), later on I find myself wanting a flush() routine. That's just my experience: problems grow thornier over time, and starting out with something more object-like adapts to that growth more gracefully than a closure does. I think a lot of the problem comes from the syntactic weight of classes. I've implemented closures in a commercial compiler; the actual implementation, when it uses captured state, is very similar to a class, and in fact the implementation method I chose behind the scenes was a class. So I tend to view closures (rather than simple function literals that don't capture state) as just a concise way of declaring and instantiating an object. I've had some success with blending the two. For example, a read-only collection facade is often implemented in .NET with ReadOnlyCollection<T>. But the normal way of implementing that thing is to pass off an IList<T>, which you may need to implement wholly if the logical read-only collection is not already a list. I think that's a complete waste of time; instead, I wrote a function that takes two closure arguments, getCount :: () -> int and getItem :: int -> T, and henceforth I can create a new collection with new behaviour without worrying about the "name" of the descendant, but with the benefits of a class for flexibility down the road.
- krig 15y agoThis is horribly off-topic, but perhaps relevant still: Can you share the details of this bastardized Lisp that you favour? Is it a lisp that is generally available, or one that you made up on the spot? I'm curious. I don't know the ins and outs of the loop macro, only that it has a lot of them, so perhaps this is simply Common Lisp and I need to keep reading my CL books, but the "row[0]" sugar makes me think that there is something else going on.
- gruseom 15y agoI made that example up (it's CL, except for row[0]), but it's the Lisp I want. I'd love to share the details because they're very specific and come directly from the application I'm writing. But we probably shouldn't go so far off-topic here. You're always welcome to email.