[Posted at http://drnicwilliams.com/2006/09/28/new-magical-version-of-symbolto_proc/] Before the magic, letâ??s go through a Beginnerâ??s Guide to Mapping, then Advanced Guide to Symbol.to_proc, and THEN, the magical version. Its worth it. Its sexy, nifty AND magical all at once. * Beginnerâ??s Guide to Mapping >> list = [â??1â?², â??2â?², â??3â?²] => ["1â?³, "2â?³, "3â?³] >> list.map {|item| item.to_i} => [1, 2, 3] Here we're invoking to_i on each item of the list and returning the result into a new list. That's map/collect for you. * Advanced Guide to Symbol.to_proc After doing that a few times, you start wishing there was simpler syntax. Enter: Symbol.to_proc >> list.map &:to_i => [1, 2, 3] It works. Just enjoy it. (see article for links on why it works) * Magical version of Symbol.to_proc Quite frankly, thatâ??s still a lot of syntax. Plus, I normally forget to added parentheses around the &:to_i, and then latter I want to invoke another method on the result, so I need to add the parentheses which is a painâ?¦ anyway. I thought of something niftier and dare I say, more magical. How about this syntax: >> list.to_is => [1, 2, 3] By passing the plural version of a method, the array automagically performs the above mapping on itself using the singular version of the method. Sexy! And here's more examples: >> (1..10).to_a.to_ss => ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"] >> (1..10).to_a.days => [86400, 172800, 259200, 345600, 432000, 518400, 604800, 691200, 777600, 864000] >> [2,'two', :two].classes => [Fixnum, String, Symbol] >> [2,'two', :two].classes.names => ["Fixnum", "String", "Symbol"] >> [2,'two', :two].classes.names.lengths => [6, 6, 6] So much happy syntax in one place! I've got the library that gives you this syntax here: http://drnicwilliams.com/2006/09/28/new-magical-version-of-symbolto_proc/ (at the bottom) Nic
on 2006-09-28 15:16
on 2006-09-28 15:18
Note: the Symbol.to_proc is currently in the Rails libraries (active_support gem) but will be added into Ruby 1.9 one day in the future.
on 2006-09-28 15:47
Hi -- On Thu, 28 Sep 2006, Dr Nic wrote: >>> list.map &:to_i > => [1, 2, 3] > > It works. Just enjoy it. (see article for links on why it works) It's not my favorite construct; it's a bit ugly and terse in a bad way. You mentioned elsewhere that this is going to be in 1.9, though I remember Matz rejecting an RCR for: list.map(:to_i) and &:to_i seems worse :-) > => [6, 6, 6] > > So much happy syntax in one place! Actually the reason this is kind of cool is not the syntax but the semantics. It's much more expressive than any number of &:->()... things that have been proposed -- and looks better. (There may be some connection there too.) I wonder, though, whether it would be too easy for it to trample on other method names. I guess that's true of anything -- you'd just have to make sure you didn't have two conceptions of some plural thing. David
on 2006-09-28 16:00
> Actually the reason this is kind of cool is not the syntax but the > semantics. It's much more expressive than any number of &:->()... > things that have been proposed -- and looks better. (There may be > some connection there too.) Agreed - its very nice semantically too. It has much more meaning than .map(&:method_name) I think. > I wonder, though, whether it would be too easy for it to trample on > other method names. I guess that's true of anything -- you'd just > have to make sure you didn't have two conceptions of some plural > thing. The method_missing code will perform "super" first to ensure that any per-existing dynamic methods on the Array are evaluated first. Then it attempts to call the method on the collection items. If they return a NoMethodError then the original error object is returned. I think that's cleanest. But yes, its a buyer-beware situation. So - "Smart Buyers Only"!
on 2006-09-28 16:07
<snip> > > => ["Fixnum", "String", "Symbol"] > I wonder, though, whether it would be too easy for it to trample on > other method names. I guess that's true of anything -- you'd just > have to make sure you didn't have two conceptions of some plural > thing. I agree, *cool* and *somehow dangerous*, I would very conservatively propose a proxy method, similiar in spirit to BDD's rspec#is %w{the meaning of 42}.map.length => [3, too_long_to_count, 2, 2] or maybe not overload #map without params for that purpose? Just an idea of course ;) BTW Using English semantics for Ruby semantics does seem + interesting + avantguarde - intolerant - too dangerous - too early it is great people doing this, I would hate having it in the core though. Robert -- Deux choses sont infinies : l'univers et la bêtise humaine ; en ce qui concerne l'univers, je n'en ai pas acquis la certitude absolue. - Albert Einstein
on 2006-09-28 16:16
Hi -- On Thu, 28 Sep 2006, Robert Dober wrote: >> >>> [2,'two', :two].classes.names >> > > or maybe not overload #map without params for that purpose? I personally dislike "magic dot" stuff like that. True, it's not really magic, since map can return some kind of enumerator that knows about the array and is ready to do something to it. But it still has the look and feel of a kind of secret system for communicating what's wanted, rather than something that really makes sense when read left to right. That's why I prefer Dr. Nic's plurals thing, though it definitely has the disadvantage of entering into murky territority in terms of method naming. David
on 2006-09-28 16:24
Robert Dober wrote: > I agree, *cool* and *somehow dangerous*, I would very conservatively > propose > a proxy method, similiar in spirit to BDD's rspec#is > > %w{the meaning of 42}.map.length => [3, too_long_to_count, 2, 2] > > or maybe not overload #map without params for that purpose? I like this idea too - something that allows you to pick which iterator method to use (that is: select, each, map) Implementable syntax might be: %w{the meaning of 42}.map_length Where the Array.method_missing could parse it as: re = /(map|collect|select|each)_([\\w\\_]+)/ if (match = method.to_s.match(re)) puts match.inspect iterator, callmethod = match[1..2] puts [iterator, callmethod] return self.send(iterator) {|item| item.send callmethod} end Cute. I've added it to the library.
on 2006-09-28 16:30
Ahh. Removed trace and fixed the regular expression:
re = /(map|collect|select|each|reject)_([\\w\\_]+\\??)/
if (match = method.to_s.match(re))
iterator, callmethod = match[1..2]
return self.send(iterator) {|item| item.send callmethod}
end
Now supports reject
For example:
>> list = [nil, 1, 2, nil, 4]
=> [nil, 1, 2, nil, 4]
>> list.reject_nil?
=> [1, 2, 4]
Cool.
on 2006-09-28 16:54
On 9/28/06, dblack@wobblini.net <dblack@wobblini.net> wrote: > >> > => [86400, 172800, 259200, 345600, 432000, 518400, 604800, 691200, > >> Actually the reason this is kind of cool is not the syntax but the > > I agree, *cool* and *somehow dangerous*, I would very conservatively propose > wanted, rather than something that really makes sense when read left > to right. > > That's why I prefer Dr. Nic's plurals thing, though it definitely has > the disadvantage of entering into murky territority in terms of method > naming. > I like the idea of being able to treat messages like some kind of object and manipulating stuff with them somewhat like this, so just yesterday I made a mutated version of Symbol#to_proc, the "first class" message: class Message def initialize(message, *args) @message, @args = message, args end def call(x, *args) x.send @message, *(@args + args) end def to_proc lambda { |*args| call(*args) } end end if $0 == __FILE__ # Example puts [*0..100].select(&Message.new(:>, 50)).inspect puts [*0..100].inject(&Message.new(:+)) puts %w(Once upon a time).map(&Message.new(:upcase)).inspect puts Message.new(:index, /[aeiou]/, -3).call("hello") puts %w(1 2 3 4 5).map(&Message.new(:to_i)).map(&Message.new(:class)).inspect end $ ruby message.rb [51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100] 5050 ["ONCE", "UPON", "A", "TIME"] 4 [Fixnum, Fixnum, Fixnum, Fixnum, Fixnum] The syntax isn't as terse, but it does have the advantage of not interfering with existing methods (and you could just write a kernel method as a shortcut if you like).
on 2006-09-28 17:02
Hi -- On Thu, 28 Sep 2006, Dr Nic wrote: >> have to make sure you didn't have two conceptions of some plural >> thing. > > The method_missing code will perform "super" first to ensure that any > per-existing dynamic methods on the Array are evaluated first. Then it > attempts to call the method on the collection items. If they return a > NoMethodError then the original error object is returned. I think that's > cleanest. I'm not sure that's working the way you think it is. When you call super in method_missing, it's calling Object#method_missing, which really just lives to be overridden. (Or is there some other layer added in there somewhere?) Also, if a dynamic method of the same name has been defined on the array, then method_missing won't get called in the first place :-) David
on 2006-09-28 17:08
Simen Edvardsen wrote: > The syntax isn't as terse, but it does have the advantage of not > interfering with existing methods (and you could just write a kernel > method as a shortcut if you like). Which would look like: if $0 == __FILE__ # Example puts [*0..100].select(&message(:>, 50)).inspect puts [*0..100].inject(&message(:+)) puts %w(Once upon a time).map(&message(:upcase)).inspect puts message(:index, /[aeiou]/, -3).call("hello") puts %w(1 2 3 4 5).map(&message(:to_i)).map(&message(:class)).inspect end Not too shabby. But still I think terseness is the major advantage of the Symbol#to_proc or the Magic Dot notation (like that name btw). OTOH this might have other advantages. T.
on 2006-09-28 17:48
I just could not resist, I am still amazed - after 20 years of Ruby
experience 8-], how easily things can be done, when did I say Ty the
last
time Matz? Probably 20 years ago!!!
Ty Matz!!!
for those who like the notation, very, very lazily tested, but
amazingly
simple, probably rather slow etc....
--------------------- 8< ---------------------
module Enumerable
class MapProxy
def initialize obj
@obj = obj
end #def initialize obj
def method_missing name, *args, &blk
@obj.map do
| ele |
ele.send name, *args, &blk
end
end #def method_missing name, *args, &blk
end #class MapProxy
alias_method :aliased_map, :map
def map *args, &blk
return MapProxy.new( self ) if args.length.zero? && blk.nil?
aliased_map *args, &blk
end #def map *args, &blk
end #module Enumerable
---------------- 8< --------------------
Cheers
Robert
--
Deux choses sont infinies : l'univers et la bêtise humaine ; en ce qui
concerne l'univers, je n'en ai pas acquis la certitude absolue.
- Albert Einstein
on 2006-09-28 18:23
unknown wrote: > I'm not sure that's working the way you think it is. When you call > super in method_missing, it's calling Object#method_missing, which > really just lives to be overridden. (Or is there some other layer > added in there somewhere?) Also, if a dynamic method of the same name > has been defined on the array, then method_missing won't get called in > the first place :-) > David I've got it all bundled in a module, so it could be applied to another class that did have a meaningful method_missing, but for Array your point is valid. By "dynamic method" I mean methods like find_by_<attr_name> in ActiveRecord - methods that are accepted by the method_missing but don't exist, rather than methods that are dynamically added to the class at some point. Sorry if that's the wrong name for them. Having said all that, I don't think this is working for associations on ActiveRecords (which are just Arrays). I'm still investigating, but perhaps rails adds its own method_missing that is negating this one? Nic
on 2006-09-29 01:48
dblack@wobblini.net wrote: > > I personally dislike "magic dot" stuff like that. True, it's not > really magic, since map can return some kind of enumerator that knows > about the array and is ready to do something to it. But it still has > the look and feel of a kind of secret system for communicating what's > wanted, rather than something that really makes sense when read left > to right. > Agreed. That is similar to the reason I hate the flip-flop operator (syntax, not semantics) and even worse, junctions. Hal
on 2006-09-29 07:31
Hal Fulton wrote: > Agreed. That is similar to the reason I hate the flip-flop > operator (syntax, not semantics) and even worse, junctions. I understand the general feeling you express. But is the reason really just in the reading? I suspect there's more to it. What really lies at the core of this uncomfortable feeling? I think maybe is has more to do with comprehension of the general pattern. That is to ask, what is this "magic dot" notation really? Maybe if we understood it better we would not feel so uncomfortable with it --or at the very least, maybe we would truly understand why we rightly feel so. In thinking about it, it seems to me that "magic-dotting" is simply using a form of Object Adapter Design Pattern. (see http://en.wikipedia.org/wiki/Adapter_pattern). I think there is a tendency to over look this and think more in terms of retroactive application of the subsequent method call. That means were focusing on the ultimate action, and so it seems odd then b/c we are thinking of '.x.y' in 'obj.x.y' as a single message when clearly we see there are two messages actually being sent. That unsettles us. But as I suggest, if we see it for what it really is --an adapter, then it makes perfect sense, and doesn;t seem so magical after all (except that it still offers some nicely terse syntax). T.
on 2006-09-29 08:38
Trans wrote: > But as I suggest, > if we see it for what it really is --an adapter, then it makes perfect > sense, and doesn;t seem so magical after all (except that it still > offers some nicely terse syntax). I agree - it is very nice syntax. Nic
on 2006-09-29 09:15
On 9/29/06, Dr Nic <drnicwilliams@gmail.com> wrote: > > -- > Posted via http://www.ruby-forum.com/. > > I'll have a look at T's link but it is a truely interesting discussion. It is for the first time that I hear that very honorable members of the community dislike this feature. For me it was the most intreaging of Ruby at all (at the beginning) I recall my old Python code something.get_other().do_stuff().transform() Terrible, it is only now that I realize that the concept of writing code like this might be flawed, there are OTOH strong indications that it is not so flawed too. E.g. lots of programing conventions asking for methods to return objects. Now, to complicate things even more, very often that object is "self", personally I find the idea to return other objects, especially proxy objects to self, very intriguing, the idea came to me when I looked at the BDD video cast. Cheers Robert -- Deux choses sont infinies : l'univers et la bêtise humaine ; en ce qui concerne l'univers, je n'en ai pas acquis la certitude absolue. - Albert Einstein
on 2006-09-29 09:27
Robert Dober wrote: > On 9/29/06, Dr Nic <drnicwilliams@gmail.com> wrote: >> >> -- >> Posted via http://www.ruby-forum.com/. >> >> I'll have a look at T's link but it is a truely interesting discussion. You're quoting techniques need some practise! I don't think I said that :) > It is for the first time that I hear that very honorable members of the > community dislike this feature. > For me it was the most intreaging of Ruby at all (at the beginning) > I recall my old Python code > > something.get_other().do_stuff().transform() > > Terrible, it is only now that I realize that the concept of writing code > like this might be flawed, there are OTOH strong indications that it is > not > so flawed too. E.g. lots of programing conventions asking for methods to > return objects. > Now, to complicate things even more, very often that object is "self", > personally I find the idea to return other objects, especially proxy > objects > to self, very intriguing, the idea came to me when I looked at the BDD > video cast. In Smalltalk all methods return self unless they return something else, thus inferring that something.get_other().do_stuff().transform() (using Smalltalk syntax :) is common. In JQuery - the javascript library - everything is centered around a "jquery" object which holds 1+ dom objects in it. All methods on the jQuery object should return that same set of objects (via the jQuery object container) so that you can chain method calls. The idea of returning a proxy object (in the Magic Dot example of .map.lengths) was very nifty. It works the normal way AND works in the magical way. The more and more examples I see and concoct myself like this the more I am happy to give up the core assumptions of "object oriented programming" etc, and adopt "happy syntax programming". Syntax that reads nicely and makes sense as to what it will do and what it will return. Now if I could just get the code I originally wrote to work on ActiveRecord associations I'd be such a happy camper. I'm in love with that syntax. Nic
on 2006-09-29 09:28
Dr Nic wrote: > You're quoting techniques need some practise! I don't think I said that > :) My ability to spell needs some practise. "Your" instead of "You're"... doh!
on 2006-09-29 11:23
On 9/29/06, Dr Nic <drnicwilliams@gmail.com> wrote: > You're quoting techniques need some practise! I don't think I said that > :) Indeed, indeed, I deleted the "Trans wrote", I am very bad at that, sorry for the confusion > > so flawed too. E.g. lots of programing conventions asking for methods to > > In JQuery - the javascript library - everything is centered around a > "jquery" object which holds 1+ dom objects in it. All methods on the > jQuery object should return that same set of objects (via the jQuery > object container) so that you can chain method calls. > > The idea of returning a proxy object (in the Magic Dot example of > .map.lengths) was very nifty. It works the normal way AND works in the > magical way. Thx ;) credits go to BDD of course The more and more examples I see and concoct myself like this the more I > am happy to give up the core assumptions of "object oriented > programming" etc, and adopt "happy syntax programming". Syntax that > reads nicely and makes sense as to what it will do and what it will > return. You are heading for the future, I'll try to keep up with your ideas. Now if I could just get the code I originally wrote to work on > ActiveRecord associations I'd be such a happy camper. I'm in love with > that syntax. > > Nic > > -- > Posted via http://www.ruby-forum.com/. > > Robert -- Deux choses sont infinies : l'univers et la bêtise humaine ; en ce qui concerne l'univers, je n'en ai pas acquis la certitude absolue. - Albert Einstein
on 2006-09-29 12:56
Hi -- On Fri, 29 Sep 2006, Dr Nic wrote: > Trans wrote: >> But as I suggest, >> if we see it for what it really is --an adapter, then it makes perfect >> sense, and doesn;t seem so magical after all (except that it still >> offers some nicely terse syntax). > > I agree - it is very nice syntax. The ship has clearly sailed on the magic dot, but for the record let me explain what I don't like about it. My problem with something like this: x.should.be.equal.to(y) (and I don't mean to parody RSpec; that's a made-up example but I think it's pretty close) is that it uses method calling *syntax* for method and/or argument name *semantics*. To my eye it's basically just clustering a bunch of words together that pertain to what's going on, and connecting them with dots in a kind of imitation of spaces or underscores. It's very hard to know what "x.should.be" returns. I can figure it out, of course. But it has a tone of "don't worry about what's actually happening; just read the dot-connected words like a string", which makes me uncomfortable. (Actually I think RSpec itself now allows underscores to formulate method names.) I do understand how the "magic dot" works. It works just like every other dot :-) I just think there are better-fitted ways to do most of these things. array.lengths, for example :-) Anyway, just to save some bandwidth: * I really do understand how it works :-) * I do know that all method chaining has a bit of this quality. * I'm not trying or expecting to stop anyone from doing it; I'm just discussing the possible drawbacks. David
on 2006-09-29 12:58
Hi -- On Fri, 29 Sep 2006, Robert Dober wrote: >> Nic > something.get_other().do_stuff().transform() > > Terrible, it is only now that I realize that the concept of writing code > like this might be flawed, there are OTOH strong indications that it is not > so flawed too. E.g. lots of programing conventions asking for methods to > return objects. I don't think anyone objects to method-chaining in every case. My dislike is for method chaining that, to my eye and brain, is just a way of composing a longer method name (or something that could be done by passing in arguments representing conditions) with dots. Your.mileage.may.vary :-) David
on 2006-09-29 13:09
> My problem with something like this: > > x.should.be.equal.to(y) > > It's very hard to know what "x.should.be" returns. I can figure it > out, of course. But it has a tone of "don't worry about what's > actually happening; just read the dot-connected words like a string", > which makes me uncomfortable. This would be readable assuming that "should" on x meant something. If I saw this I'd guess its like "assert". But, assuming that your list of words DID mean something, then it has to be more readable to newcomers (and yourself in 12 mths time) than the possible equivalent: x.each {|an_x| assert_equal y, x} or to rephrase the example, perhaps: x.should {|an_x| an_x.be :equal, :to, y} The magic dot gives you left-to-right readability, whereas the above makes you figure out whats happening in the closure first, then go back to the x.each part to see where an_x comes from. Still, you'd have to create an appropriate proxy class for each message you want to support. That could be a pain. All good fun though :)
on 2006-09-29 13:34
Hi -- On Fri, 29 Sep 2006, Dr Nic wrote: > saw this I'd guess its like "assert". > > But, assuming that your list of words DID mean something, then it has to > be more readable to newcomers (and yourself in 12 mths time) than the > possible equivalent: The intent is very clear, I think. I just don't like the construct :-) > x.each {|an_x| assert_equal y, x} > > or to rephrase the example, perhaps: > > x.should {|an_x| an_x.be :equal, :to, y} I think my example, in (say) TestUnit, would be more like: assert_equal(y,x) But as I said, the magic dot example was made up, so I can't really push the point of what it would be equal to in other terms. > The magic dot gives you left-to-right readability, whereas the above > makes you figure out whats happening in the closure first, then go back > to the x.each part to see where an_x comes from. I can't really comment without hooking my eyes up to a machine and seeing where they go :-) But there's no necessity to backtrack once you've seen x.each {|an_x|, from the point of view of understanding the code. Mind you, the magic dot doesn't require backtracking either. David
on 2006-09-29 15:14
> > > David wrote > > I don't think anyone objects to method-chaining in every case. My > dislike is for method chaining that, to my eye and brain, is just a > way of composing a longer method name (or something that could be done > by passing in arguments representing conditions) with dots. > > Your.mileage.may.vary :-) I.see.your.point.emphasis_start.now.emphasis_end Now that would be abuse of course, mixing content and presentation. David I am sincerely sorry that my posts -always a bit odd, always a bit funny [ in both senses of the word ] - have given you the impression that I wanted you to explain something. I am, and all other regular readers are, well aware that your Ruby level is way above mine and even I understand the magic dot notation :) As a matter of fact I took your concerns, and Tom's [ I'll come back to that later ] very seriously and did not understand them in the first place. You made a very good point in your last mail, but again I had the feeling that I had inadevertly insulted you, it was *not* my intent. So we have the tool, and as always there is danger with power, Tom's link to the Object Adapter Pattern fits very well and when getting into that paradigm one might as well adapt more towards method chaining than before, even with some small magic. Tom's attitude "I do not like that", "other people like that", "hmmm why do they like it?", "hmm maybe because they see it differently, let us do some research", "see that is interesting" is truely admirable and very productive. I try to mimmick that attitude for sure :) OTOH one has to look out for code that is not too clear, we are starting a very philiosophic thread here, sorry folks, I thaught BDDs paradigm change was too radical too, but I think that is because I am very conservative (c.f. my POV towards ducktyping). Those of us who want always to understand - count me in for 100% - might sometimes fall into the trap of not allowing certain abstractions to take place because, we cannot - even the smartest - immediately understand them. It is a truely difficult process to decide if a new paradigm is worth exploring and I am more than glad to have nice, competent people here talking about their paradigms and ideas. Nuff said for today. Cheers Robert David > > -- > David A. Black | dblack@wobblini.net > Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3] > DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4] > [1] http://www.manning.com/black | [3] http://www.rubypowerandlight.com > [2] http://dablog.rubypal.com | [4] http://www.rubycentral.org > > -- Deux choses sont infinies : l'univers et la bêtise humaine ; en ce qui concerne l'univers, je n'en ai pas acquis la certitude absolue. - Albert Einstein
on 2006-09-29 15:48
Hi -- On Fri, 29 Sep 2006, Robert Dober wrote: > > > As a matter of fact I took your concerns, and Tom's [ I'll come back to that > later ] very seriously and did not understand them in the first place. > > You made a very good point in your last mail, but again I had the feeling > that I had inadevertly insulted you, it was *not* my intent. No, I didn't get that impression in the slightest. I'm just doing my usually thing of burrowing down into Ruby stuff and seeing what's there -- nothing personal riding on it. David
on 2006-09-29 16:02
[David] wrote: > No, I didn't get that impression in the slightest. I'm just doing my > usually thing of burrowing down into Ruby stuff and seeing what's > there -- nothing personal riding on it. Ruby is great for burrowing. Java is bad. I remember burrowing in Java and you always end up at something "final"; something you'd LIKE to override or extend but you're not allowed for reasons unknown. Now we are officially OT. :) Nic
on 2006-09-29 16:34
On 9/28/06, Dr Nic <drnicwilliams@gmail.com> wrote: > I like this idea too - something that allows you to pick which iterator > method to use (that is: select, each, map) > > Implementable syntax might be: > > %w{the meaning of 42}.map_length FWIW I really prefer the magic underscore over anything else. The pluralization thing has too much potential for collision and looks jarring (to_as looks to me like "to" "as", which doesn't make any sense; then again I'm not a fan of programmatic pluralization for any reason, since it's very western-language-centric and far from foolproof (moose? virus? fish?)). The magic dot has a potential to create scads more objects to handle the adapting. With a magic dot, list.map.to_i necessarily has to create some adapter object for the call to map so there's a receiver for to_i where no object was required before. Unless Ruby is adding a pretty powerful GC in the future, perhaps we should avoid adding tons of transient objects just for the magic dot. The magic underscore has far less potential to cause a collision and requires no intermediate objects to be created. And as others have mentioned, it's been proven to look and feel really nice by ActiveRecord.
on 2006-09-29 16:57
I like your summary. > The pluralization thing has too much potential for collision and looks > jarring (to_as looks to me like "to" "as", which doesn't make any > sense; then again I'm not a fan of programmatic pluralization for any > reason, since it's very western-language-centric and far from > foolproof (moose? virus? fish?)). I agree that you shouldn't use it if the resulting code makes no sense or might cause conflicts. If I've got an array of ActiveRecords that have a name field, then calling @list.names to return an array of names still seems to be readable and conflictless imo. > The magic dot has a potential to create scads more objects to handle > the adapting. With a magic dot, list.map.to_i necessarily has to > create some adapter object for the call to map so there's a receiver > for to_i where no object was required before. Unless Ruby is adding a > pretty powerful GC in the future, perhaps we should avoid adding tons > of transient objects just for the magic dot. Perhaps the proxy could be associated with the class and reused? > The magic underscore has far less potential to cause a collision and > requires no intermediate objects to be created. And as others have > mentioned, it's been proven to look and feel really nice by > ActiveRecord. I like this a lot too. #1 and #3 are currently available in the .rb file on my blog page for anyone finding this thread. Nic
on 2006-09-29 17:00
Hi -- On Fri, 29 Sep 2006, Charles O Nutter wrote: > On 9/28/06, Dr Nic <drnicwilliams@gmail.com> wrote: >> I like this idea too - something that allows you to pick which iterator >> method to use (that is: select, each, map) >> >> Implementable syntax might be: >> >> %w{the meaning of 42}.map_length > > FWIW I really prefer the magic underscore over anything else. I do too, generally, though map_length sounds like "the length of the map of this object". One starts to think there's a reason that map takes a block.... > The pluralization thing has too much potential for collision and looks > jarring (to_as looks to me like "to" "as", which doesn't make any > sense; then again I'm not a fan of programmatic pluralization for any > reason, since it's very western-language-centric and far from > foolproof (moose? virus? fish?)). Yeah, that was my point with sheep, even though I do rather like the way the plural things read (when they work). How about: array.meese.each do |moose| ... end The mind boggles :-) David
on 2006-09-29 17:33
On 9/29/06, Dr Nic <drnicwilliams@gmail.com> wrote: > > The magic dot has a potential to create scads more objects to handle > > the adapting. With a magic dot, list.map.to_i necessarily has to > > create some adapter object for the call to map so there's a receiver > > for to_i where no object was required before. Unless Ruby is adding a > > pretty powerful GC in the future, perhaps we should avoid adding tons > > of transient objects just for the magic dot. > > Perhaps the proxy could be associated with the class and reused? Threading, threading, threading...remember JRuby is native-threaded and Ruby 2.0 should be as well. Even if not native-threaded, a green-thread context switch could try to use it twice. And before someone suggests a "proxy pool", almost all benchmarks I've seen show that with a good GC, just creating transient objects is way faster than pooling. Of course, Ruby's GC is not on the same level as the JVM's, so a pool may be faster in Ruby's case.
on 2006-09-29 17:57
Charles O Nutter wrote: > The pluralization thing has too much potential for collision and looks > jarring (to_as looks to me like "to" "as", which doesn't make any > sense; then again I'm not a fan of programmatic pluralization for any > reason, since it's very western-language-centric and far from > foolproof (moose? virus? fish?)). You are right there. Pluralization is really going overboard, besides the exceptions it creates a great deal of computational overhead. It seems great on the surface, but in the end it is simply is not worth the effort. Although it can seem odd for the English speaker at times, Ruby's general favoring of the singular is a very good thing. And this is one area in which I feel Rails has been unhelpful. > The magic dot has a potential to create scads more objects to handle > the adapting. With a magic dot, list.map.to_i necessarily has to > create some adapter object for the call to map so there's a receiver > for to_i where no object was required before. Unless Ruby is adding a > pretty powerful GC in the future, perhaps we should avoid adding tons > of transient objects just for the magic dot. Actually this is largely mitigated. Best practice is to cache the adapter object. So for example Enumerable#every: def every @_functor_every ||= Functor.new do |op,*args| self.collect{ |a| a.send(op,*args) } end end This is something not often shown in examples and admittedly may not make it into first editions of such functions in practice, but utlimately it gets incorporated and makes a huge difference in the over head you mention. > The magic underscore has far less potential to cause a collision and > requires no intermediate objects to be created. And as others have > mentioned, it's been proven to look and feel really nice by > ActiveRecord. I disagree. "Magic underscore"[1] is either a function of method_missing or dynamic method creation. There is acually more potential for name collision in these cases. IN fact that is one of the uses of the magic dot, to create namespaces. Also care must be taken to propogte method_missing through the class heirarchy so as not to step on other people's magic toes. And dynamic method creation adds a lot of additional methods to a class that Ruby must sort through when dispatching a call. It certainly looks nice, and I'm in no way agasint it's use. But there are tradoffs to be considered, and one should apply that techinique that works best to the need at hand. [1]FYI, "magic underscore" isn't an a good name b/c we're really just talking about dynamic method names. These can be anything and do not neccessarly need an underscore. T.
on 2006-09-29 18:07
dblack@wobblini.net wrote: > > I agree - it is very nice syntax. > > The ship has clearly sailed on the magic dot, but for the record let > me explain what I don't like about it. > > My problem with something like this: > > x.should.be.equal.to(y) I agree. Unless #should, #be and #equal are all creating some truly useful and generally applicable adaptation then this is just "fluff code". Again overhead is being added for no appreciable gain. Of course, I making an assumption here since I haven't looked at the code , but I suspect that a magic dot notation like: x.should_be.equal_to(y) is more appropriate. In this case it is easy to see that #should_be is an assertion adapter. This is interesting, b/c when you break it down like this one could actaully go a bit further and create more natural ruby constructs: x.should == y T.
on 2006-09-29 18:25
With everyone's well founded concerns about name collisions, we should all know full well that we live with them in Ruby world on a daily basis: monkey patching, method overriding in subclasses, aliasing and chain aliasing (how many times do ActiveRecords need to alias some methods?!), etc. So hopefully our internal namespace collision detectors are already turned on :) Nic
on 2006-09-29 19:39
On 9/29/06, Dr Nic <drnicwilliams@gmail.com> wrote: > With everyone's well founded concerns about name collisions, we should > all know full well that we live with them in Ruby world on a daily > basis: monkey patching, method overriding in subclasses, aliasing and > chain aliasing (how many times do ActiveRecords need to alias some > methods?!), etc. So hopefully our > internal namespace collision detectors are already turned on :) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ You misspelt "extensive test suite running continuously" =) - rob
on 2006-09-29 20:11
Rob Sanheim wrote: > On 9/29/06, Dr Nic <drnicwilliams@gmail.com> wrote: >> With everyone's well founded concerns about name collisions, we should >> all know full well that we live with them in Ruby world on a daily >> basis: monkey patching, method overriding in subclasses, aliasing and >> chain aliasing (how many times do ActiveRecords need to alias some >> methods?!), etc. So hopefully our >> internal namespace collision detectors are already turned on :) > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > > You misspelt "extensive test suite running continuously" =) No spellchecker on Ruby forums UI. Perhaps I can whip up a greasemonkey script to help me with such simple/common typos. :)
on 2006-09-29 21:31
On 9/29/06, Rob Sanheim <rsanheim@gmail.com> wrote: > > On 9/29/06, Dr Nic <drnicwilliams@gmail.com> wrote: > <SNIP> > > internal namespace collision detectors are already turned on :) > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > > You misspelt "extensive test suite running continuously" =) Miss Van Pelt, reread your Peanuts, but you have a point of course! - rob... ..ert --
on 2006-10-01 07:16
Charles O Nutter wrote: > The pluralization thing has too much potential for collision and looks > jarring (to_as looks to me like "to" "as", which doesn't make any > sense; then again I'm not a fan of programmatic pluralization for any > reason, since it's very western-language-centric and far from > foolproof (moose? virus? fish?)). And just about every programming language syntax isn't already? I like programmatic pluralization because it allows us to write code in the same way we talk about it. It's like optimization for our brains, and expressive power that seems to get closer to more intelligent programs, as well as making syntax more accessible to laypersons. As for the potential inaccuracy of converting certain terms, you could provide an interface to add definitions as Rails' Inflector does. On the topic at hand, I don't like the plurals option. The Symbol#to_proc style currently in ActiveSupport seems just about perfect to me, although I don't see why it couldn't just be: foos.map :bar p.s. I got totally lost with all that crazy.dot.talk. That hypothetic code made me want to cry.
on 2006-10-01 07:31
Seth Thomas Rasmussen wrote:
> foos.map :bar
That'd be workable syntax.
class Array
alias old_map map
def map(*args)
if args.length > 0
return self.map {|item| item.send args.first}.map *args[1..-1]
else
return self.old_map
end
end
end
This would allow:
people.map :fullname, :split
as the equivalent of:
people.map {|p| p.fullname}.map {|n| n.split}
Oh so much fun :)
Nic
on 2006-10-01 07:44
Trans wrote: > Charles O Nutter wrote: >> The pluralization thing has too much potential for collision and looks >> jarring (to_as looks to me like "to" "as", which doesn't make any >> sense; then again I'm not a fan of programmatic pluralization for any >> reason, since it's very western-language-centric and far from >> foolproof (moose? virus? fish?)). > > You are right there. Pluralization is really going overboard, besides > the exceptions it creates a great deal of computational overhead. It > seems great on the surface, but in the end it is simply is not worth > the effort. Although it can seem odd for the English speaker at times, > Ruby's general favoring of the singular is a very good thing. And this > is one area in which I feel Rails has been unhelpful. The way I coded it, it singularizes the string and passes that as the method call for the items of the collection. So if you pass a singular method name, then it will still be singular when its passed to the children. That is: people.names returns the same list of names as people.name So non-English speakers can be happy too. Of course, if the people Array class had a name method, the latter wouldn't work. But unless you have an Array of Arrays, you won't get many name conflicts btw the Array class and the container class. Re: performance overhead - I'm not sure if the standard map+block is more or less efficient than a for-loop, but I use it anyway because my code is much more readable and writable. If I later discovered that I had a performance bottleneck near my map+blocks, then I could refactor them for speed. I don't think that in a thread on "fun syntax ideas" disapproving on the ideas based on small performance hits like "apply a gsub on a string" is much fun. :( Cheers Nic
on 2006-10-01 08:06
Seth Thomas Rasmussen wrote: > On the topic at hand, I don't like the plurals option. The > Symbol#to_proc style currently in ActiveSupport seems just about > perfect to me, although I don't see why it couldn't just be: > > foos.map :bar It can... class Array def map(sym=nil, &block) self.inject([]) { |col, item| if block_given? col << block.call(item) elsif not sym.nil? col << item.send(sym) else col << item end } end def map!(sym=nil, &block) self.replace(map(sym, &block)) end end Regards, Jordan
on 2006-10-01 13:56
Seth Thomas Rasmussen wrote: > expressive power that seems to get closer to more intelligent programs, > p.s. I got totally lost with all that crazy.dot.talk. That hypothetic > code made me want to cry. Rails :\
on 2006-10-02 22:19
On 10/1/06, Dr Nic <drnicwilliams@gmail.com> wrote: > Re: performance overhead - I'm not sure if the standard map+block is > more or less efficient than a for-loop, but I use it anyway because my > code is much more readable and writable. If I later discovered that I > had a performance bottleneck near my map+blocks, then I could refactor > them for speed. The JRuby code started out as a port of the C Ruby code, and for us a 'for' is just an 'each' over a range...so it's probably about the same, performance-wise.
on 2006-10-03 12:04
On 10/1/06, Dr Nic <drnicwilliams@gmail.com> wrote: > > This would allow: > people.map :fullname, :split > > as the equivalent of: > people.map {|p| p.fullname}.map {|n| n.split} > > Oh so much fun :) I did this a while back by adding an f (for functional) to the method name - so if you said people.mapf :fullname method_missing would see that it ended with an f, strip the f out and construct a block to pass to map. map_fullname reads even better, though. martin
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.