6 ms·
I'm Sick of This
- mpk 18y agoWhat, you mean this? ~ $ irb irb(main):001:0> Object.methods.size => 90 irb(main):002:0> require 'activesupport' => true irb(main):003:0> Object.methods.size => 177 [ugh, no preview..]
- misuba 18y agoUh... aren't we kind of burying the lede here? Ruby has macros now. For some limited definition of 'macros,' maybe, but screw it, I'm there. Thank you, Mr. Braithwaite!
- andreyf 18y agoI've heard this in person and now here, but I can't find info on it - care to link me to these macros you speak of?
- davidmathers 18y agohttp://github.com/raganwald/rewrite/tree/master http://github.com/raganwald/rewrite/tree/master
- raganwald 18y agoPossibly http://github.com/raganwald/rewrite_rails/tree/master http://github.com/raganwald/rewrite_rails/tree/master
- jrockway 18y agoInteresting. We have sort of solved this problem in Perl. When you want to add a method to a class, you do it by applying a role. If two roles conflict, like by adding a method with the same name, you have to resolve the conflict, or the class won't compile. (The class can rename methods, but you can also call the method with a name that will always work; $instance->Role::Name::method.) We can also apply roles at runtime. This means you can add a method to a single instance of a class. So Rails could add its version of sum to its own instances of Array, and Classifier could do the same. Then there would be no conflict, except when Classifier tries to add sum to the Rails array... but at least you get a fatal error message instead of unexpectedly wrong behavior. We can do better, though. The way around this problem is to implement (and CPAN) a role that adds a sum method to arrays, and to have both Rails and Classifier use that role. Then they can both share arrays, and when one tries to apply the sum role, the already-applied sum role will be used instead. They both get their sugar, the sugar is isolated to the smallest area possible (a single instance), and they can both confidently share those arrays-that-can-sum. And oh yeah, with Moose::Autobox, you can apply roles to Perl's native types (arrays, hashes, etc.), and call methods on them. So this is not "pie in the sky", it's an already-solved problem for us :) Edit: ContextL also provides an interesting solution, "layers". Each module can define their own layer, add their own sum method to it, and activate it inside themselves: (defclass array ...) (deflayer rails-sum ...) (define-layered-method sum :layer rails-sum ((self array)) ...) (deflayer classifier-sum ...) (define-layered-method sum :layer classifier-sum ((self array)) ...) (in-package :rails) (defun count-users (users-array) (with-active-layers (rails-sum) (sum users-array))) ; uses rails' sum method (in-package :classifier) (defun count-results (results-array) (with-active-layers (classifier-sum) (sum results-array))) ; users classifier's sum method (in-package :my-app) (defun broken-code (array) (with-active-layers (rails-sum classifier-sum) ;; ERROR, methods conflict )) This is a pretty good approach, but not as good as agreeing on semantics in advance.
- nkurz 18y ago"you can also call the method with a name that will always work; $instance->Role::Name::method." I'm coming at this from a Perl and C background, but I'm not really understanding the fuss. It seems obvious that diddling with base classes is not scalable unless you have have some strong social conventions. But why do it at all? For those not fluent in Perl's syntax, Jonathan's suggestion is equivalent to "$instance->Package::method()", which is in turn equivalent to "method($instance) from within a given Package. This seems foolproof. No one's followed up on it, but at a glance I don't see any downsides to this approach. I may be illustrating my ignorance (and I'm definitely ignorant of Ruby syntax), but why is there such strong desire to have array->sum() instead of sum(array)? Or as a utility function Utility::sum(array)? And is there no equivalent in Ruby to array->Utility::sum()?
- deleted 18y ago[deleted]
- raganwald 18y agoWe can also apply roles at runtime. This means you can add a method to a single instance of a class. So Rails could add its version of sum to its own instances of Array, and Classifier could do the same. This is possible with Ruby, but culturally rare. Quite possibly because (blamestorming mode=ON) Rails simply patches whatever it wants, whenever it wants and many people follow its lead. I personally avoid the practice because it is difficult to contain in Ruby. For example, if I am handed an Array it is easy to extend that instance with its own #sum method, and when I'm done with the array I can remove my own extension. However, what happens if, while I am still holding this extended array, I pass the array to some other method that wants to extend it with a #sum method? Same problem. Working on an instance method does reduce the scope of each modification and thus make collisions statistically less likely, so I am all for it. But at the same time, when I step back and look at the big picture, I yearn for something that solves the problem in a more fundamental way.
- jrockway 18y agoIt is interesting... Perl has such a reputation for being unmaintainable that Perl programmers today are very careful to spend extra effort making their code as maintainable as possible. I think a lot of practicing Ruby (and Python) programmers are under the impression that Ruby magically solved Perl's maintainability shortcomings, and so they don't invest any effort into writing maintainable code. The result is ... unmaintainable code :) It turns out that programmers write the unmaintainable code, not programming languages.
- eatenbyagrue 18y agoA lot of this is solved by namespaces in Ruby (Modules.) Unfortunately, developers don't really apply them consistently. A best practice should be to put everything in it's own namespace. Been playing around with .NET MVC and C#, and this is something it manages really well.
- andreyf 18y agoHow exactly are conflicting changes to global variables soved by namespaces?
- tjstankus 18y agoI don't think global variables were mentioned in the article. Do you mean the global (top-level) namespace? If so, then yeah, "Don't do that" is pretty much the solution.
- andreyf 18y agoSorry, maybe this isn't correct Ruby nomenclature, but I see Array simply as a global object, which can be modified, with rather obvious consequences. It's sad that I feel old enough to say this, but isn't this a problem that we solved a long time ago? "Don't do that" is one solution, Alan Kay's idea of "worlds" is another.
- mpk 18y agoThey aren't. But solid Ruby programmers use them this way and audit third-party code before using it and avoid it if it contains such code smell. One of the fun things about Ruby is that you can open up, override and chain just about anything. Apart from allowing for some interesting experiments, it also allows you to do things like monkey-patch faulty methods in libraries (including the std libs) at run-time while you wait for a sanctioned update to come out. This may or may not be a Good Thing, depending on who you are and what you're doing.
- tjstankus 18y agoYup, proper use of namespaces would solve the problem, in part. Supposedly Rails3 will be using namespaces quite a bit more than the current version of Rails. That will allow things like mounting apps inside other apps, aka slices. What I take away as the overall point of the article though is: monkey patch with care.
- andreyf 18y agoAlan Kay wrote a paper about a very elegant metaphor to solve this, it's called "Worlds": http://www.vpri.org/pdf/rn2008001_worlds.pdf You create a "world" for your Classifier gem, which (in a trivial implementation) makes local copies of any global objects when they're modified instead of actually changing them. ActiveSupport runs in yet another "world", and knows nothing about the "local" changes to the global variables Classifier made.
- jrockway 18y agoI don't think this is quite what Worlds are. Worlds would not allow you to have a data structure that is used by both Rails and Classifier. FWIW, ContextL is closer to the solution: http://common-lisp.net/project/closer/contextl.html http://common-lisp.net/project/closer/contextl.html But it's still better to just add a standard "array that does 'sum'" to the language, or for Rails and Classifier to cooperate.
- andreyf 18y agoWorlds would not allow you to have a data structure that is used by both Rails and Classifier. I might be misunderstanding it... but why not?
- arebop 18y agoIt seems to me that worlds permit sharing immutable data structures, and that Warth & Kay's operations on worlds do not permit sharing mutations done in parallel worlds. The Worlds paper you cited [http://www.vpri.org/pdf/rn2008001_worlds.pdf http://www.vpri.org/pdf/rn2008001_worlds.pdf] cites the COP overview paper [http://p-cos.net/documents/contextl-overview.pdf http://p-cos.net/documents/contextl-overview.pdf] mentioned near the top of the link jrockway gave. Warth & Kay say "Similarly, in order to support context-oriented programming (COP) [8], we may want to add a third key to our lookup table that identifies a context." So that gives me some confidence in my interpretation.
- stcredzero 18y agoCooperation is dandy if n = 2, but for n > 2, this can get cumbersome. Unfortunately method names like 'sum' are very attractive. A language/library designer shouldn't have to think of "all of the short or otherwise possibly overloaded nouns and verbs people might like to use, ever." Being able to say you are going to do a 'sum' which means X in this particular context is very powerful. This notion should also be unified with version control. (As in OS X, where a process only sees the versions of libraries that it is supposed to run against.)
- Locke 18y agoI'd almost blame this problem on Ruby not having its own standard Array#sum method (or more likely it should be a method on Enumerable?). I'm guessing a lot of projects add their own, so it's a natural source of conflict. Secondly, it seems like most conflicts like this occur between Rails and some other library. Rails, via ActiveSupport, adds a lot of extensions to core Ruby. I kind of wish it didn't. Framework or general purpose library developers should recognize that their library will likely be combined with many others and avoid conflicts by sticking to what's available in the standard, namespacing their code, etc. I like Ruby, but I'll readily admit it's not a perfect language. I'm sure someday a better Ruby-like language will come along. Hopefully, it'll be a language with better support for avoiding these kind of conflicts without removing support for open classes. I'm sure it's an interesting technical challenge.
- carbon8 18y agoRails, via ActiveSupport, adds a lot of extensions to core Ruby. I kind of wish it didn't. Apparently modular ActiveSupport is in the works: http://github.com/wycats/rails/commit/21c17e893fd8f5f7dc41df3bdb9e39a0b30e5a36 http://github.com/wycats/rails/commit/21c17e893fd8f5f7dc41df...
- warfangle 18y agoI'd blame this problem on gem writers monkeypatching base functionality. Why not use the Array.map and pass it a summing block? That way if you've got an array of things other than numbers, you can correctly utilize the strategy pattern to sum them....
- Locke 18y agoUsually, I think, people write sum with inject: [1,2,3].inject { |s,i| s + i } Which is presumably why sum doesn't exist.
- deleted 18y ago[deleted]
- 18y ago
- calambrac 18y agoI love how Scala solves this problem (or rather, provides the same functionality while avoiding the problem altogether): http://louisbotterill.blogspot.com/2009/02/scala-implicit-conversions.html http://louisbotterill.blogspot.com/2009/02/scala-implicit-co... http://www.codecommit.com/blog/scala/scala-for-java-refugees-part-6 http://www.codecommit.com/blog/scala/scala-for-java-refugees... Edit - more links, more detail: http://technically.us/code/x/the-awesomeness-of-scala-is-implicit http://technically.us/code/x/the-awesomeness-of-scala-is-imp... http://scala.sygneca.com/code/simplifying-jdbc http://scala.sygneca.com/code/simplifying-jdbc
- raganwald 18y agoOff topic: Thank-you HN for having a "Don't editorialize the article's headline" policy. Over on proggit this was submitted with the title Metaprogramming has its pluses and minuses. I feel that statement completely misses the point of the post. http://www.reddit.com/r/programming/comments/8ayr1/metaprogramming_has_its_pluses_and_minuses/c08qdd8 http://www.reddit.com/r/programming/comments/8ayr1/metaprogr...
- soundsop 18y agoUsually, I would agree, except in this case the title gives absolutely no indication as to the topic of the article. Putting the word metaprogramming somewhere in the title would have been better.
- raganwald 18y agoWell, I could say "I'm sick of this metaproggraming shit." But I'm not sick of the metaprogramming, I'm sick of the conflicts. Or there's "I'm sick of the conflicts between other people's metaproggraming shit." That's closer to it, but doesn't capture my disappointment in the lack of progress in writing better tools. So there's "I'm sick of the lack of really cool metaproggraming shit" which is actually what bothers me and has the informal profanity I was going for. So... Please feel free to fork my repo and retitle it I'm sick of the lack of really cool metaprogramming shit with my blessing.
- blasdel 18y agoHow about "I'm sick of Ruby's mediocre support for metaprogramming in-the-large"?
- scott_s 18y ago"Metaprogramming headaches: we've solved this before"
- tvon 18y agoThe blog post title is fine, it's the post on HN that could use a little context. I think that's the point of this thread, anyway.
- johnrob 18y agoJSON.js does something similar. It puts methods in the javascript object prototype for generating and parsing json strings. WTF? Is it so inconvenient to just expose two functions to do this? This was a problem because it broke some jquery functionality, and I ended up removing the portion of JSON.js that was updating the object prototype.
- nostrademons 18y agoIt was a big problem in the JavaScript world, until JQuery and YUI and Google came out and said "Thou shalt not modify Object.prototype" and started leading by example. But Prototype and Mootools and a bunch of smaller libraries still modify the prototypes of built-in objects, which is the main reason I won't go near those libraries. Unfortunate, since they're otherwise pretty good... I think new versions of JSON.js (json2.js) don't modify Object.prototype anymore - Crockford has certainly learned his lesson.
- koningrobot 18y agoOne of the neat things about JavaScript is that objects are just hashes/dictionaries. It turned out to be a really flexible way to do objects. Unfortunately, when you actually want to use an object as a dictionary, all the methods get in the way. If methods weren't owned by objects, there wouldn't be any such problem. A JavaScript with generic functions would be awesome. I've been thinking of implementing such a thing (and a couple of other things to fix JavaScript), but it's sort of pointless, seeing as Common Lisp already does many things right. The only thing JavaScript has going for it is that it runs in browsers. A full-fledged interpreter (in JavaScript) for another language would be too slow. Perhaps such a language should be compiled down to JavaScript using only very simple and fast text transformations. All this would be just for kicks, of course.
- tdavis 18y agoIt disgusts me on levels I cannot describe that this is standard operating procedure for ruby modules (gems, etc) and is the main reason I could never use it. Adding and replacing methods on builtin classes? Are these people out of their minds?! I don't understand it. I've used monkey patches once or twice and it always feels really dirty.
- raganwald 18y agoThe difference between us is this: You see something that sickens you and publicly blame and shame others for the mess. I see something sickening and I am trying to clean it up.
- tdavis 18y agoI commend you for that, though I have no desire to clean up the messes of others. What would you have me do? Learn Ruby and start rewriting modules? Teach proper metaprogramming to the ignorant masses? The first step to fixing a problem, in this case, is conving people they have one. I'm throwing my hat into that ring.
- raganwald 18y agoActually, I was just trying to pretend that I'm a kindly old wise man that everybody likes for his civic-minded attention to duty. In reality, I'm just as disgusted as you are and the post title says it all. It's true that Ruby should provide better tools. But it's also true that a lot of what goes on is avoidable and there's nothing wrong with you pointing that out.
- shiranaihito 18y agoa lot of what goes on is avoidable and there's nothing wrong with you pointing that out Alright, so why the "(you) publicly blame and shame others for the mess" then?
- plinkplonk 18y ago"The difference between us is this: You see something that sickens you and publicly blame and shame others for the mess. I see something sickening and I am trying to clean it up." I am not sure if I am reading this right, but there seems to be abit of a moralizing tone there. "The difference between us is you [ do something slightly slimy] , I [ do something very noble]". At least that's the way it reads to me. I am not sure in a discussion of a technical issue there is much of a difference in practice. Why is "this is sucky" not acceptable unless one has spent significant time trying to clean it up? Edit : Retracted after reading "Actually, I was just trying to pretend that I'm a kindly old wise man that everybody likes for his civic-minded attention to duty. In reality, I'm just as disgusted as you are and the post title says it all.". I thought I'll leave it up here as a warning not others not to read too much into something.
- jcapote 18y agoI'm curious as to how lisp has dealt with this for 40+ years
- silentbicycle 18y agoBy getting a reputation of being poorly suited to team development, unfortunately.
- gruseom 18y agoLisp's reputation has a tenuous connection to reality; even so, I see no connection between it and the problem being discussed here (name collisions) which doesn't come up in CL. If anyone does make that argument, I'd like to see it: it would appear to require even more obtuseness than usual.
- silentbicycle 18y agoI was thinking specifically of unintentional variable capture causing name collisions in non-hygienic Common Lisp macros. While people can certainly write macros correctly and avoid them, this takes experience, and minor mistakes can introduce some really subtle bugs. [edit: noted that I'm talking specifically about Common Lisp]
- arohner 18y agoIn Scheme, it's basically impossible to write a non-hygienic macro, and in Clojure, there are no name collisions and it takes extra work to variable capture.
- deleted 18y ago[deleted]
- gruseom 18y agoOh, that. That is one of those problems people talk about but no one has. While people can certainly write macros correctly and avoid them, this takes experience No, it doesn't. It's trivial to call gensym. Edit: I'm not saying that CL's way is the best, just that this question gets way more attention than it deserves.
- herdrick 18y agoWhat is naked recursion?
- raganwald 18y agoFunctions/methods that call themselves, as opposed to having a higher-level function that takes a function as an argument and does something recursive with it. http://github.com/raganwald/homoiconic/blob/master/2008-11-23/recursive_combinators.md#readme http://github.com/raganwald/homoiconic/blob/master/2008-11-2...
- herdrick 18y agoAh, so naked recursion is recursion. EDIT: I was going to criticize your idea that there is anything lacking in "naked recursion". It's a really powerful technique and in fact I just wrote a recursive function for production code an hour ago. Comment continued here: http://news.ycombinator.com/item?id=555714 http://news.ycombinator.com/item?id=555714