5 ms·
Overall, a good feature set. I only see some utility for keyword arguments, admittedly, but I tend to write very functional code, so maybe other people will get
by donw 14y ago
Overall, a good feature set. I only see some utility for keyword arguments, admittedly, but I tend to write very functional code, so maybe other people will get more mileage.
Refinements, as I understand them, are a bad idea.
I understand the motivation, but this is not the way to go about it. There's now a new scope all over the place (the set of refinements applied to any given class). This means that the behavior of any given class now can depend heavily on context.
This makes optimization a bitch, for the same reason that it makes programming a bitch. I can now have two Strings, one Refined by ActiveRecord, the other Refined by Sequel, which expose a different set of incompatible methods.
- jacquesc 14y agoTotally agree about refinements. Hopefully the community pushes back enough on that experiment so it gets deprecated and removed. A better design approach to achieve this effect is wrapper objects (similar to jQuery and Underscore.js) where I can add functionality to existing objects. Ruby has everything we need for this already, and makes it quite easy to do.
- nnq 14y agoAren't refinements a "fixer feature", i.e. something 99% of developers should just ignore it until they really get into a problem with badly done monkeypatching, then use it to solve the problem and just move on? Why do people get so excited about them, both in a good and in a bad way? Just use them inside your library code and make sure people using your lib/framework can just pretend they don't exist, keep monkeypatching "chained inside" as your dirty little secret:P)
- tomstuart 14y agoCharles Nutter does a good job of explaining why you can't just ignore refinements: http://blog.headius.com/2012/11/refining-ruby.html http://blog.headius.com/2012/11/refining-ruby.html In summary, they makes the Ruby language harder to implement, and Ruby code harder to understand. You can't pretend they don't exist if their existence compromises the meaning of your code and the performance of your chosen Ruby implementation.
- vidarh 14y agoBecause they make efficient implementation of Ruby, which is already an utter nightmare, substantially harder.
- riffraff 14y agobut ruby culture is already one of modifying builtins, so using wrappers while arguably better, does not solve the issue which refinements solve.
- djur 14y agoThere's been a substantial retreat from modifying builtins in library code in the last 1-2 years anyway.
- pjungwir 14y agoRefinements worry me too. They make it a lot harder to read and understand code, or track down where strange behavior is coming from. I wish I could just ignore them, but unfortunately libraries like ActiveRecord probably won't. And then there are the performance problems. Since so many people dislike them, I wonder what the community response will be? Will JRuby just leave them unimplemented? That seems bad, but I'm not sure I'd mind too much. But that's not viable if it means Rails and other things won't work on JRuby.
- chao- 14y agoAgreed regarding refinements. I will personally stay away from them until some braver souls find best practices for them in long-term projects. Even then it will take some convincing. I have been awaiting Ruby keyword arguments for a very long time, but the first time I heard them brought up (relating to 2.0), the person explaining had some qualms with how they were implemented. For the life of me I cannot remember what bothered them, but if that rings a bell for anyone else, please chime in.
- ch0wn 14y agoThere's a really in-depth article on them that also covers concerns about the implementation: http://blog.headius.com/2012/11/refining-ruby.html http://blog.headius.com/2012/11/refining-ruby.html
- aphexairlines 14y agoAfter reading that article, I'm wondering: what's the value of Ruby refinements over dynamically-scoped monkey-patching? Taking the String.camelize example from the article, you could write in Perl: sub withCamelize (&) { my ($thunk) = @_; local *{'String::camelize'} = sub { ... }; $thunk->() } sub camelize_and_join { my ($str_ary) = @_; withCamelize { join ', ', map { $_->camelize } @$str_ary } } So you can probably already accomplish the same with Ruby's equivalent of 'local' for dynamic scoping.
- tsewlliw 14y agoWow, that reminds me of the classloader magic Eclipse/OSGi does so that plugins that depend on different versions of the same library can work together in the same JVM.
- aardvark179 14y agoThat kind of local class alteration is very different from the kind of tricks done in OSGi. In OSGi a class is the same no matter what context you're in, it's just that the context you're in affects the look up from class name to class. So all the caching or optimisations you might do against classes are still valid. Refinements mean things like method lookup are sensitive to the context and become much more fun.
- nnq 14y ago> but I tend to write very functional code A bit offtopic, but since you said that and you seem to be both fond of functional programming and in touch with latest ruby developments, you're probably a good person to ask: How the heck do you do "normal/proper" currying in Ruby? And how do you do it with keyword arguments? (I'm trying to compile a list of functional-idiom examples in popular dynamic language, and when I reached Ruby and currying, all the snippets I found made me go "WTF?!"...)
- tomstuart 14y agoRuby only has built-in support for currying procs: >> add = -> x, y { x + y } => #<Proc (lambda)> >> add.call(3, 5) => 8 >> add.call(3) ArgumentError: wrong number of arguments (1 for 2) >> curried_add = add.curry => #<Proc (lambda)> >> add_three = curried_add.call(3) => #<Proc (lambda)> >> add_three.call(5) => 8 If you want a no-brainer way of currying methods, you're SOL.
- donw 14y agoThat's pretty much it; the only other way is to build a function combinator from a Proc or Lambda: irb(main):004:0> multiply = -> a, b { a * b } => #<Proc:0x007fe2822029e0@(irb):4 (lambda)> irb(main):006:0> square = -> a { multiply.call(a, a) } => #<Proc:0x007fe2840746a8@(irb):6 (lambda)> irb(main):007:0> square.call(3) => 9
- jroesch 14y agoYou can do: "Hello".method(:split).to_proc.curry[""] => ['H', 'e', 'l', 'l', 'o'] If you want to even make the receiver an argument it would take a little more hoop jumping, but with a tiny monkey patch you can enable something like this: "Hello".curry.split[""]
- Jabbles 14y agoHave they removed any features?
- steveklabnik 14y agoNo, 2.0.0 is intended to be mostly backwards compatible with 1.9.3.
- rat87 14y ago> I understand the motivation, but this is not the way to go about it. There's now a new scope all over the place (the set of refinements applied to any given class). This means that the behavior of any given class now can depend heavily on context. The current behaviour makes the scope global, and one would simply overwrite the methods of the other, surely that is a worse thing, no?
- dgregd 14y ago> Refinements, as I understand them, are a bad idea. I agree. Some feature to debug monkey patching conflicts is needed. But do we really have to change methods call logic to achieve that goal? I would like to see some diagnostic feature one can just turn on/off. Something that just shows conflicts.