Too Many Ways?

At what point do you cross over from:
  "Various ways to accomplish the same goal is good,
  because it allows the language to work the way
  different people think about working."
into the land of:
  "Duplicate names and techniques to do the same
  atomic thing ends up confusing the nubys"
?

results = my_array.collect{ ... }
results = my_array.map{ ... }

a = Proc.new{ ... }
b = lambda{ ... }
def whee; ...; end
c = self.method( :whee )

def foo &blockAsBlock
  blockAsBlock.call
  yield
end

foo &a
foo &b
foo &c

def bar blockAsParam
  blockAsParam.call
end

bar a
bar b
bar c

Regexp globals vs. MatchData
#insert your own examples here

···

--
(-, /\ \/ / /\/

[snip good examples]

AFAIC, there's no objective answer. I dislike the extremes in both
directions (that goes for any topic, really). Python is way too
strict for my taste. Ruby is pretty much fine, IMO. If I were king,
I'd tighten it up just a little, and probably screw the whole thing
up.

Gavin

···

On Thursday, September 30, 2004, 12:09:33 PM, Gavin wrote:

At what point do you cross over from:
  "Various ways to accomplish the same goal is good,
  because it allows the language to work the way
  different people think about working."
into the land of:
  "Duplicate names and techniques to do the same
  atomic thing ends up confusing the nubys"
?

"Gavin Kistner" <gavin@refinery.com> schrieb im Newsbeitrag news:C3144C65-1285-11D9-BDE2-000A959CF5AC@refinery.com...

At what point do you cross over from:
"Various ways to accomplish the same goal is good,
because it allows the language to work the way
different people think about working."
into the land of:
"Duplicate names and techniques to do the same
atomic thing ends up confusing the nubys"
?

results = my_array.collect{ ... }
results = my_array.map{ ... }

IMHO these were introduced to match other languages notations. I think it makes life easier for newcomers.

a = Proc.new{ ... }
b = lambda{ ... }

Same here.

def whee; ...; end
c = self.method( :whee )

These are two completely different cups of tea.

def foo &blockAsBlock
blockAsBlock.call
yield
end

Same here: the &b notation converts the block into a proc making it possible to store it somewhere or pass it on.

def bar blockAsParam
blockAsParam.call
end

I'd consider this a different case, too, because here the block can't be handed over via "foo { ... }".

Regexp globals vs. MatchData
#insert your own examples here

*That* is an area where I definitely like to see improvements (see the thread some time ago about String#scan and MatchData).

On the whole I feel pretty comfortable with the way Ruby does things although I recognize a certain tendency to increase redundancy ("lambda" and "proc" comes to mind). As far as I remember I wasn't confused when I was a RubyNuby. But then again, everybody feels different about this.

Kind regards

    robert

Gavin Kistner ha scritto:

#insert your own examples here

%{x}

%q{x}
%Q{x}
"x"
'x'
and heredocs.
Definitely too many ways for me :slight_smile:
Amnyway I subscribe robert klemme's Ideas in the other response.

At what point do you cross over from: "Various ways to accomplish
the same goal is good, because it allows the language to work the
way different people think about working." into the land of:
"Duplicate names and techniques to do the same atomic thing ends up
confusing the nubys" ?

Simple: nubys should only be taught one way to do a thing. What's harder than learning to program? Learning to program twice. Or you could ask, I'm getting ready to teach someone two things about Ruby... shouldn't they be *different* things?

(This is something I really tried to follow in my tutorial, which even has a TMTOWTDI section near the end: http://pine.fm/LearnToProgram/\)

So I think the cross over is really in the other direction: from nuby to TMTOWTDI.

···

On Thu, 30 Sep 2004 11:09:33 +0900, Gavin Kistner wrote:

On Thu, 30 Sep 2004 19:04:00 +0900, David A. Black wrote:

If only the things that make life easier for newcomers could
disappear later, like training wheels on a bicycle.... :slight_smile:

So naturally I completely disagree with this! :slight_smile:

Duplicates do *not* make things any easier for newcomers. Learning something twice is just that much harder, and then having one of them disappear later would be disastrous. Again, I think it really moves in the other direction: from nuby to TMTOWTDI.

(Yes, if you are teaching Ruby to a functional programmer, you use map instead of collect (are those really the same? I just always avoided collect: wrong english word, I think), but you still teach them only *one* of them.)

First you learn one way to do it. (Depending on your background, lambda might make more sense than proc (nevermind that these *aren't* the same, though I'll never keep up with that).) Then, once you are doing a pretty good job of coding Ruby on your own, you learn a few other ways to do things so you can read other people's code (which is always a little trickier, anyway).

Just some thoughts,

Chris

        "Duplicate names and techniques to do the same
        atomic thing ends up confusing the nubys"

...

#insert your own examples here

I second your example of:

  def foo
    ...
    yield stuff if block_given?
  end

versus:

  def foo(&blk)
    ...
    blk.call(stuff) unless blk.nil?
  end

I think I would have found iterators much easier to get my head round if I
had seen the second, rather than the first, initially.

And if you use the first syntax, there is the leap you have to make sooner
or later: "hang on - how do I pass this block to another method?"; or
conversely, "I have a proc object, but how do I pass it to a method which
expects a block?"

Regards,

Brian.

Hi --

"Gavin Kistner" <gavin@refinery.com> schrieb im Newsbeitrag
news:C3144C65-1285-11D9-BDE2-000A959CF5AC@refinery.com...
> At what point do you cross over from:
> "Various ways to accomplish the same goal is good,
> because it allows the language to work the way
> different people think about working."
> into the land of:
> "Duplicate names and techniques to do the same
> atomic thing ends up confusing the nubys"
> ?
>
> results = my_array.collect{ ... }
> results = my_array.map{ ... }

IMHO these were introduced to match other languages notations. I think it
makes life easier for newcomers.

If only the things that make life easier for newcomers could disappear
later, like training wheels on a bicycle.... :slight_smile: Personally I've
never minded the handful of duplicate method names, though I know some
people do.

> a = Proc.new{ ... }
> b = lambda{ ... }

Same here.

Proc.new and lambda aren't the same as each other, though:

  irb(main):014:0> l = lambda {|a,b|}
  => #<Proc:0x00000000@(irb):14>
  irb(main):015:0> pr = Proc.new {|a,b|}
  => #<Proc:0x00000000@(irb):15>
  irb(main):016:0> l.call(1)
  ArgumentError: wrong number of arguments (1 for 2)
          from (irb):14
          from (irb):14:in `call'
          from (irb):16
  irb(main):017:0> pr.call(1)
  => nil

> def whee; ...; end
> c = self.method( :whee )

These are two completely different cups of tea.

I think these two lines go together: Gavin was showing that a method,
like a block, lambda, or Proc, could be assigned to a variable.

I tend to feel that the [block, lambda, Proc, method] stuff is too
complicated. But I know Matz is working on it; hopefully there will
be some unification/simplification to come.

David

···

On Thu, 30 Sep 2004, Robert Klemme wrote:

--
David A. Black
dblack@wobblini.net

Yeah. I suppose the question was sort of rhetorical, my way of saying "I raise my hand as someone who feels that there is too much duplication, but then again, I'm not king, and I don't wish to be."

···

On Sep 29, 2004, at 8:30 PM, Gavin Sinclair wrote:

AFAIC, there's no objective answer.
[...]
If I were king, I'd tighten it up just a little, and probably screw the whole thing up.

--
(-, /\ \/ / /\/

Funny, I never understood what #map did. People used it all the time and it baffled me. I kept saying "I should learn this method, because people keep showing how to use it for powerful, terse solutions, but...I'm baffled."

Then someone showed me the #collect method, and I fell in love. It's awesome. I extended arrays in Javascript to use it, I love it so much.

...it was a week later than I pulled up the documentation and saw that they're the same thing. Something about 'map' made absolutely no sense to me (it implies a hash-like permanent connection between the old and the new), and collect made perfect sense (go traipsing through the entire field of daisies, picking up only the best ones).

So if we were deprecating duplicates, I vote to get rid of #map.
(This is the part where 100 rubyists say "No! #collect sucks, #map is king!" and my entire argument for less duplication is shot down by the fact that people like different things.)

···

On Sep 30, 2004, at 7:12 AM, Chris Pine wrote:

(Yes, if you are teaching Ruby to a functional programmer, you use map instead of collect (are those really the same? I just always avoided collect: wrong english word, I think), but you still teach them only *one* of them.)

--
(-, /\ \/ / /\/

Chris Pine ha scritto:

<snipall>
I strongly agree with your view. Actually, I think when you teach
#proc to a functional programmer you just say them 'it is just to declare a lambda', and when you teach #map to a SmalTalker you say them "it's just like #collect ". I think it's a good way but it's just my opinion :slight_smile:

Brian Candler wrote:

       "Duplicate names and techniques to do the same
       atomic thing ends up confusing the nubys"

..

#insert your own examples here

I second your example of:

  def foo
    ...
    yield stuff if block_given?
  end

versus:

  def foo(&blk)
    ...
    blk.call(stuff) unless blk.nil?
  end

I think I would have found iterators much easier to get my head round if I
had seen the second, rather than the first, initially.

And if you use the first syntax, there is the leap you have to make sooner
or later: "hang on - how do I pass this block to another method?"; or
conversely, "I have a proc object, but how do I pass it to a method which
expects a block?"

Well, yes, I got hung up on this right off the bat--I got it into my head that yield was somehow dynamic, so that it would find some magic block up the stack to yield to . So I was very puzzled when I couldn't yield from within a method called by the method with the block.

I had assumed this level of dyanmicity since the block wasn't specified--it seemed obvious to me that objects would be simply invoked in a context where a yield would magically invoke some block defined somewhere.

If I'd had to declare the block from the start, then this would never have been an issue.

As a nuby, even when you *know* there's two ways to do something, you're stuck wondering if there is some subtle reason that one is better (or simply different) than the other. You see two mechanisms, can't find anything written that describes when to use one or the other, and are left puzzled and confused.

Bob

I tend to feel that the [block, lambda, Proc, method] stuff is too
complicated.

I'm agree with you, just make it more complex to solve the
complication :slight_smile:

svg% cat b.rb
#!/usr/bin/ruby
require 'aa'

class A < Proc
   def self.new(&block)
      proc(&block)
   end
end

a = A.new {|x, *y| p x,y }
p a.class
a.call([1, 2])
svg%

svg% b.rb
A
[1, 2]

svg%

Guy Decoux

results = my_array.collect{ ... }
results = my_array.map{ ... }

IMHO these were introduced to match other languages notations. I think it
makes life easier for newcomers.

If only the things that make life easier for newcomers could disappear
later, like training wheels on a bicycle.... :slight_smile:

I suppose that's my wish, too. As unrealistic as it may be.
("Sorry, play time is over. All your existing code may crash and burn, but in the end it'll make you a better programmer.")

What I'd think was nice is if a bunch of things were officially deprecated; no runtime warning (although perhaps with a -w or something) but where exact duplicates exist, the King from on High would say "thou shalt use #foo and not #bar". (I was even thinking of removing the deprecated names from the documentation, but then that's no good for someone needing to decipher existing code.)

Proc.new and lambda aren't the same as each other, though:

Whoa, that I didn't know.
So a lambda is more like a method (currently)?

Actually, I'd say this is an argument for merging, or something. My personal principle of least surprise was violated when I found that I couldn't create a block with default parameters. Procs, blocks, methods, lambdas ... four ways to do almost exactly the same thing.

On the whole I feel pretty comfortable with the way Ruby does things although I recognize a certain tendency to increase redundancy ("lambda" and "proc" comes to mind). As far as I remember I wasn't confused when I was a RubyNuby. But then again, everybody feels different about this.

I'm not sure if I'm a nuby or not; I certainly am when it comes to grokking lambdas. The concepts are all pretty clear (not confusing when learning), but when I was designing the EventTarget module, I kept waffling between:
def add_event_listener( evt_type, callback_proc )
and
def add_event_listener( evt_type, &callback_proc )

Proc-as-param allows multiple params to be procs, and makes it easy to pass methods/procs/lambdas (which are what would most likely be used for a callback: an existing, centrally-defined procedure)...but prevents blocks from being used without Proc.new. That's bad, because Ruby people LIKE blocks. I know I do.

Proc-as-block allows no more than one 'proc', and requires &foo to be used when passing method references or procs or lambdas..but *does* allow blocks.

I suppose this is a case where subtle flexibility is important (different functionality), but it's the case of the blank canvas scaring and stymying the novice artist: "Where do I begin?" There's so much/too much flexibility; so much overlap muddled in with the tradeoffs it's frustrating. But now I'm getting onto the specific topic of merging the features and fun of both sides, instead of the general "I'd like to see a little less duplication in the future, please" topic.

···

On Sep 30, 2004, at 4:04 AM, David A. Black wrote:

On Thu, 30 Sep 2004, Robert Klemme wrote:

On Thu, 30 Sep 2004, Robert Klemme wrote:
--
(-, /\ \/ / /\/

[snip]

the new), and collect made perfect sense (go traipsing through the
entire field of daisies, picking up only the best ones).

Perfect sense? Your description sounds a lot more like #select than
#collect :slight_smile:

andrew

···

On Thu, 30 Sep 2004 22:28:07 +0900, Gavin Kistner <gavin@refinery.com> wrote:

Programming languages are a lot like philosophy. Nothing ever gets thrown away!

I find the duplicate names for a method a, minor, irritant but getting rid of them is not going to be painless.

One of the first things I learned about Ruby was that some methods are
aliased. It never occured to me to dislike this, it's never caused me
any problems, and I _really_ like "map" (the name and the method) and
don't understand where the name "collect" comes from. But 'ri' tells
me they're the same -- it's not like there's a conspiracy to hide the
fact.

Maybe people just need to learn about the alias thing early on, know
how to look out for it, and be happy ever after :slight_smile:

Gavin

BTW, the method "map" maps a function onto a set and returns the
result. I think that's the right terminology. It comes from
functional languages, anyway. In Miranda (and I presume Haskell):

  f(x) = x * 5 + 1
  map f [10..15] # -> [51, 56, 61, 66, 71, 76]

···

On Thursday, September 30, 2004, 11:28:07 PM, Gavin wrote:

On Sep 30, 2004, at 7:12 AM, Chris Pine wrote:

(Yes, if you are teaching Ruby to a functional programmer, you use map
instead of collect (are those really the same? I just always avoided
collect: wrong english word, I think), but you still teach them only
*one* of them.)

Funny, I never understood what #map did. People used it all the time
and it baffled me. I kept saying "I should learn this method, because
people keep showing how to use it for powerful, terse solutions,
but...I'm baffled."

Then someone showed me the #collect method, and I fell in love. It's
awesome. I extended arrays in Javascript to use it, I love it so much.

...it was a week later than I pulled up the documentation and saw that
they're the same thing.

Gavin Kistner wrote:

(Yes, if you are teaching Ruby to a functional programmer, you use map instead of collect (are those really the same? I just always avoided collect: wrong english word, I think), but you still teach them only *one* of them.)

Funny, I never understood what #map did. People used it all the time and it baffled me. I kept saying "I should learn this method, because people keep showing how to use it for powerful, terse solutions, but...I'm baffled."

How would you feel if you were forced to use #map all the time? That's why I think it's good that Ruby follows TMTOWTDI. Python for example AFAIK only provides map() and #collect comes from Smalltalk, I think. Now, everyone from both worlds feel happy in Ruby :wink:

Of course when reading others' sourcecodes you'll get into trouble. But I don't as often as I write new code.

Then someone showed me the #collect method, and I fell in love. It's awesome. I extended arrays in Javascript to use it, I love it so much.

...it was a week later than I pulled up the documentation and saw that they're the same thing. Something about 'map' made absolutely no sense to me (it implies a hash-like permanent connection between the old and the new), and collect made perfect sense (go traipsing through the entire field of daisies, picking up only the best ones).

So if we were deprecating duplicates, I vote to get rid of #map.
(This is the part where 100 rubyists say "No! #collect sucks, #map is king!" and my entire argument for less duplication is shot down by the fact that people like different things.)

Well, of course both #map and #collect behave in the same way, but I'm not sure I use both of them in the same way! That's a bit similar to "{...}" and "do ... end" block, which are similar but used by some people in different situations. I think, I use #map when the result is short and very similar to the source (e.g. {|a| a+1}), and #collect if it spans multiple lines and the calcluation is big.

Regards,

   Michael

···

On Sep 30, 2004, at 7:12 AM, Chris Pine wrote:

But this isn't what collect does. It picks up all the daisies. I think
it's called collect because it returns a collection of the same type
as the receiver in Smalltalk. The elements in this collection are the
result of evaluating the given block with each element of the original
collection as first argument. The funny thing is that collect in Ruby
always returns an array and not the same type. If Ruby would know
a default way of adding to a collection (like <<) one could define
collect like this:

module Enumerable
  def collect
    inject(self.class.new) { |s, x| s << yield(x); s }
  end
end

Map (or mapcar) was invented in the LISP world. It maps a given
function f on each value of the (list x1 x2 ...) and returns the
resulting new (list (f x1) (f x2) ...). Ruby doesn't have a builtin
list type but uses Array instead. So it makes a lot of sense to have
map return an array. I think the idea behind Ruby's map is: If
i have the enumerable enum let's pretend it was already an array. Now
lets yield the given block to each of its values and build a new array
of the same size that consists of the return values of the block.

The ruby block does act pretty much like a function here:

You can translate from Scheme

(map (lambda (x) (+ x 1)) (list 1 2 3))

into Ruby pretty easy

[1, 2, 3].map &lambda { |x| x + 1 }

Now, you can call Non-Prefix-LISP if you like. :wink:

···

On 2004-09-30 22:28:07 +0900, Gavin Kistner wrote:

...it was a week later than I pulled up the documentation and saw that
they're the same thing. Something about 'map' made absolutely no sense
to me (it implies a hash-like permanent connection between the old and
the new), and collect made perfect sense (go traipsing through the
entire field of daisies, picking up only the best ones).

--
lambda { |c| lambda { |f| f[f] } [ lambda { |f| c[lambda { |x| f[f] } ] }] }

Being a Haskell programmer, I prefer map to collect. Actually, the
name "map" makes more sense to me than "collect". To many names for
the same thing can make newcomers' life a bit confusing, but after a
while you get rid of duplicates in your own mind and settle down to
the ones you like the most.

Regards,
Ed

···

On Thu, 30 Sep 2004 22:28:07 +0900, Gavin Kistner <gavin@refinery.com> wrote:

On Sep 30, 2004, at 7:12 AM, Chris Pine wrote:

So if we were deprecating duplicates, I vote to get rid of #map.
(This is the part where 100 rubyists say "No! #collect sucks, #map is
king!" and my entire argument for less duplication is shot down by the
fact that people like different things.)

--
" Don't relax! It's only your tension that's holding you together."

My point was that if I am teaching Ruby to a functional programmer, I don't teach them proc at all, and they already know what lambda means, so no problem. Similiarly, I wouldn't teach map to a Smalltalker.

Of course, later on, when they are fairly confident with Ruby (and no longer nubys), then I would say exactly what you said: proc is just like lambda (well, except that I guess it isn't), and map is just like collect.

To clarify, I have no problem with aliasing at all. In fact, I even kind of like it, precisely because it makes teaching *easier* (based on where a person is coming from). Of course, they have to learn it all to really learn Ruby; I'm just arguing about the order things ought to be taught in. Why confuse a fp'er with proc when they already get lambda? Move on to other things, get them hooked on Ruby, *then* mention TMTOWTDI later.

Chris

···

On Thu, 30 Sep 2004 23:30:02 +0900, gabriele renzi wrote:

Chris Pine ha scritto:

<snipall>
I strongly agree with your view. Actually, I think when you teach
#proc to a functional programmer you just say them 'it is just to
declare a lambda', and when you teach #map to a SmalTalker you say
them "it's just like #collect ". I think it's a good way but it's
just my opinion :slight_smile: