Does an Array#apply make any sense at all?

Hi!

I wanted to write a simple method for comparing two paths on a Windows
system. My initial algorithm felt very contrived:

def same_path?(a, b)
  a, b = [a, b].map{ |p| File.expand_path(p).downcase }
  a == b
end

It felt like saving the result from #map and then doing the comparison
shouldn't be necessary. So I came up with the following solution:

class Array
  def apply(method)
    shift.send(method, *self)
  end
end

This allowed me to define #same_path? thusly:

def same_path?(a, b)
  [a, b].map{ |p| File.expand_path(p).downcase }.apply(:==)
end

which, to me, looks a lot nicer. Array#apply takes a method (in a
symbolic manner) and applies it to its first element, passing the rest
of the elements as arguments to the given method.

Any thoughts? Is Array#apply a method that could potentially have
other uses than my specific example above?

  nikolai

Is there something wrong with Pathname#==?

···

On Aug 14, 2007, at 02:40, nikolai.weibull@gmail.com wrote:

I wanted to write a simple method for comparing two paths on a Windows
system.

--
Poor workers blame their tools. Good workers build better tools. The
best workers get their tools to do the work for them. -- Syndicate Wars

Your method should be called "apply!" because it's destructive. I'd
rather not do it that way, i.e. without changing the array at hand.You
can use inject for that.

I'd use #inject for your problem:

match = [a,b].map {|x| File.expand_path(x).downcase}.inject {|x,y| x==y}

Yet another solution is to use one of Pathname's multiple methods (for
example #realpath).

Kind regards

robert

···

2007/8/14, nikolai.weibull@gmail.com <nikolai.weibull@gmail.com>:

Hi!

I wanted to write a simple method for comparing two paths on a Windows
system. My initial algorithm felt very contrived:

def same_path?(a, b)
  a, b = [a, b].map{ |p| File.expand_path(p).downcase }
  a == b
end

It felt like saving the result from #map and then doing the comparison
shouldn't be necessary. So I came up with the following solution:

class Array
  def apply(method)
    shift.send(method, *self)
  end
end

This allowed me to define #same_path? thusly:

def same_path?(a, b)
  [a, b].map{ |p| File.expand_path(p).downcase }.apply(:==)
end

which, to me, looks a lot nicer. Array#apply takes a method (in a
symbolic manner) and applies it to its first element, passing the rest
of the elements as arguments to the given method.

Any thoughts? Is Array#apply a method that could potentially have
other uses than my specific example above?

Your first solution is much easier to understand. In fact I would do:

def same_path?(a, b)
   a = File.expand_path(a).downcase
   b = File.expand_path(b).downcase
   a == b
end

It's very clear and actually should be a tad faster. LOC isn't
everything!

But if you just can't abide by more that one line:

def same_path?(a, b)
   File.expand_path(a).downcase == File.expand_path(b).downcase
end

HTH,
T.

···

On Aug 14, 2:40 am, "nikolai.weib...@gmail.com" <nikolai.weib...@gmail.com> wrote:

Hi!

I wanted to write a simple method for comparing two paths on a Windows
system. My initial algorithm felt very contrived:

def same_path?(a, b)
  a, b = [a, b].map{ |p| File.expand_path(p).downcase }
  a == b
end

It felt like saving the result from #map and then doing the comparison
shouldn't be necessary. So I came up with the following solution:

class Array
  def apply(method)
    shift.send(method, *self)
  end
end

This allowed me to define #same_path? thusly:

def same_path?(a, b)
  [a, b].map{ |p| File.expand_path(p).downcase }.apply(:==)
end

Yes, not that it can't be fixed, but the current definition is sort of
broken (on all systems):

···

On Aug 14, 11:50 am, Eric Hodel <drbr...@segment7.net> wrote:

On Aug 14, 2007, at 02:40, nikolai.weib...@gmail.com wrote:

> I wanted to write a simple method for comparing two paths on a Windows
> system.

Is there something wrong with Pathname#==?

  #
  # Compare this pathname with +other+. The comparison is string-
based.
  # Be aware that two different paths (<tt>foo.txt</tt> and <tt>./
foo.txt</tt>)
  # can refer to the same file.
  #
  def ==(other)
    return false unless Pathname === other
    other.to_s == @path
  end
  alias === ==
  alias eql? ==

  nikolai

Ah, I had no idea inject would take the first item of an array if not
given an argument; thanks! That solves my problem.

  nikolai

···

On Aug 14, 1:51 pm, "Robert Klemme" <shortcut...@googlemail.com> wrote:

2007/8/14, nikolai.weib...@gmail.com <nikolai.weib...@gmail.com>:

> Hi!

> I wanted to write a simple method for comparing two paths on a Windows
> system. My initial algorithm felt very contrived:

> def same_path?(a, b)
> a, b = [a, b].map{ |p| File.expand_path(p).downcase }
> a == b
> end

> It felt like saving the result from #map and then doing the comparison
> shouldn't be necessary. So I came up with the following solution:

> class Array
> def apply(method)
> shift.send(method, *self)
> end
> end

> This allowed me to define #same_path? thusly:

> def same_path?(a, b)
> [a, b].map{ |p| File.expand_path(p).downcase }.apply(:==)
> end

> which, to me, looks a lot nicer. Array#apply takes a method (in a
> symbolic manner) and applies it to its first element, passing the rest
> of the elements as arguments to the given method.

> Any thoughts? Is Array#apply a method that could potentially have
> other uses than my specific example above?

Your method should be called "apply!" because it's destructive. I'd
rather not do it that way, i.e. without changing the array at hand.You
can use inject for that.

I'd use #inject for your problem:

match = [a,b].map {|x| File.expand_path(x).downcase}.inject {|x,y| x==y}

Hi --

Hi!

I wanted to write a simple method for comparing two paths on a Windows
system. My initial algorithm felt very contrived:

def same_path?(a, b)
  a, b = [a, b].map{ |p| File.expand_path(p).downcase }
  a == b
end

It felt like saving the result from #map and then doing the comparison
shouldn't be necessary. So I came up with the following solution:

class Array
  def apply(method)
    shift.send(method, *self)
  end
end

This allowed me to define #same_path? thusly:

def same_path?(a, b)
  [a, b].map{ |p| File.expand_path(p).downcase }.apply(:==)
end

which, to me, looks a lot nicer. Array#apply takes a method (in a
symbolic manner) and applies it to its first element, passing the rest
of the elements as arguments to the given method.

Any thoughts? Is Array#apply a method that could potentially have
other uses than my specific example above?

Your method should be called "apply!" because it's destructive.

I could be wrong, but I think the convention (at least in core Ruby)
is that ! methods always come in a pair with the non-! version. I
don't think there are any cases where there's just a method called m!
where the ! indicates destructiveness (or other "danger"). All the
unpaired destructive methods have non-! names.

I think this makes sense. If unpaired dangerous methods have !, it
sort of suggests that any time there isn't a !, the method is
non-dangerous, which in turn suggests non-destructive... and that
isn't true.

David

···

On Tue, 14 Aug 2007, Robert Klemme wrote:

2007/8/14, nikolai.weibull@gmail.com <nikolai.weibull@gmail.com>:

--
* Books:
   RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242\)
   RUBY FOR RAILS (http://www.manning.com/black\)
* Ruby/Rails training
     & consulting: Ruby Power and Light, LLC (http://www.rubypal.com)

The problem is that Pathname's comparison is case sensitive.

  nikolai

···

On Aug 14, 1:51 pm, "Robert Klemme" <shortcut...@googlemail.com> wrote:

Yet another solution is to use one of Pathname's multiple methods (for
example #realpath).

It's not about the line count but about executing the same set of
functions/methods on both a and b. With the #inject or #apply
solution it's clear that both a and b undergo the same conversion.

  nikolai

···

On Aug 14, 2:11 pm, Trans <transf...@gmail.com> wrote:

On Aug 14, 2:40 am, "nikolai.weib...@gmail.com"

> def same_path?(a, b)
> [a, b].map{ |p| File.expand_path(p).downcase }.apply(:==)
> end

Your first solution is much easier to understand. In fact I would do:

def same_path?(a, b)
   File.expand_path(a).downcase == File.expand_path(b).downcase
end

match = [a,b].map {|x| File.expand_path(x).downcase}.uniq.size == 1

···

On Aug 14, 6:51 am, "Robert Klemme" <shortcut...@googlemail.com> wrote:

2007/8/14, nikolai.weib...@gmail.com <nikolai.weib...@gmail.com>:

> Hi!

> I wanted to write a simple method for comparing two paths on a Windows
> system. My initial algorithm felt very contrived:

> def same_path?(a, b)
> a, b = [a, b].map{ |p| File.expand_path(p).downcase }
> a == b
> end

> It felt like saving the result from #map and then doing the comparison
> shouldn't be necessary. So I came up with the following solution:

> class Array
> def apply(method)
> shift.send(method, *self)
> end
> end

> This allowed me to define #same_path? thusly:

> def same_path?(a, b)
> [a, b].map{ |p| File.expand_path(p).downcase }.apply(:==)
> end

> which, to me, looks a lot nicer. Array#apply takes a method (in a
> symbolic manner) and applies it to its first element, passing the rest
> of the elements as arguments to the given method.

> Any thoughts? Is Array#apply a method that could potentially have
> other uses than my specific example above?

Your method should be called "apply!" because it's destructive. I'd
rather not do it that way, i.e. without changing the array at hand.You
can use inject for that.

I'd use #inject for your problem:

match = [a,b].map {|x| File.expand_path(x).downcase}.inject {|x,y| x==y}

Ugh. This was so NOT about whether to call the method Array#apply or
Array#apply!. I just wrote the absolute shortest solution I could
think of. I didn't suggest that it was the final version. Yes,
seeing as how my definition mutates its target, Array#apply! is the
appropriate name. I simply didn't want to clutter the definition
thusly:

class Array
  def apply(method)
    first.send(method, *self[1..-1])
  end
end

(which sort of begs the question why we don't have Array#rest :wink:

  nikolai

···

On Aug 14, 2:07 pm, dbl...@rubypal.com wrote:

Hi --

On Tue, 14 Aug 2007, Robert Klemme wrote:
> 2007/8/14, nikolai.weib...@gmail.com <nikolai.weib...@gmail.com>:
>> Hi!

>> I wanted to write a simple method for comparing two paths on a Windows
>> system. My initial algorithm felt very contrived:

>> def same_path?(a, b)
>> a, b = [a, b].map{ |p| File.expand_path(p).downcase }
>> a == b
>> end

>> It felt like saving the result from #map and then doing the comparison
>> shouldn't be necessary. So I came up with the following solution:

>> class Array
>> def apply(method)
>> shift.send(method, *self)
>> end
>> end

>> This allowed me to define #same_path? thusly:

>> def same_path?(a, b)
>> [a, b].map{ |p| File.expand_path(p).downcase }.apply(:==)
>> end

>> which, to me, looks a lot nicer. Array#apply takes a method (in a
>> symbolic manner) and applies it to its first element, passing the rest
>> of the elements as arguments to the given method.

>> Any thoughts? Is Array#apply a method that could potentially have
>> other uses than my specific example above?

> Your method should be called "apply!" because it's destructive.

I could be wrong, but I think the convention (at least in core Ruby)
is that ! methods always come in a pair with the non-! version. I
don't think there are any cases where there's just a method called m!
where the ! indicates destructiveness (or other "danger"). All the
unpaired destructive methods have non-! names.

* <dblack@rubypal.com> (14:07) schrieb:

Your method should be called "apply!" because it's destructive.

I could be wrong, but I think the convention (at least in core Ruby)
is that ! methods always come in a pair with the non-! version. I
don't think there are any cases where there's just a method called m!
where the ! indicates destructiveness (or other "danger"). All the
unpaired destructive methods have non-! names.

That is true for Array#delete. delete is always destructive, there is no
need to flag that.

But there is no need for your method to be destructive, so you need to
tell the world about it.

I think this makes sense. If unpaired dangerous methods have !, it
sort of suggests that any time there isn't a !, the method is
non-dangerous, which in turn suggests non-destructive... and that
isn't true.

It should simply be clear from the name[0] if a method is destructive.
Often you need the ! for that, in some cases it's obvious without the !.

mfg, simon .... [0] is that actually english?

Could you file a bug on the Ruby tracker? (Or has it been fixed?)

···

On Aug 14, 2007, at 04:40, nikolai.weibull@gmail.com wrote:

On Aug 14, 11:50 am, Eric Hodel <drbr...@segment7.net> wrote:

On Aug 14, 2007, at 02:40, nikolai.weib...@gmail.com wrote:

I wanted to write a simple method for comparing two paths on a Windows
system.

Is there something wrong with Pathname#==?

Yes, not that it can't be fixed, but the current definition is sort of
broken (on all systems):

--
Poor workers blame their tools. Good workers build better tools. The
best workers get their tools to do the work for them. -- Syndicate Wars

On Aug 14, 2:40 am, "nikolai.weib...@gmail.com"

def same_path?(a, b)
  [a, b].map{ |p| File.expand_path(p).downcase }.apply(:==)
end

Your first solution is much easier to understand. In fact I would do:

def same_path?(a, b)
   File.expand_path(a).downcase == File.expand_path(b).downcase
end

It's not about the line count but about executing the same set of
functions/methods on both a and b. With the #inject or #apply
solution it's clear that both a and b undergo the same conversion.

perhaps you should just give the functions/methods a name:

def normalize path
  File.expand_path(path).downcase
end

def same_path?(a, b)
  normalize(a) == normalize(b)
end

Of course we are drifting far away from the topic of this thread.

More on topic: I would think an apply method would apply a function to each
member of the array (like map) - just from the sound of word.

  nikolai

cheers

Simon

···

nikolai.weibull@gmail.com wrote:

On Aug 14, 2:11 pm, Trans <transf...@gmail.com> wrote:

Very nice.

  nikolai

···

On Aug 15, 8:44 pm, William James <w_a_x_...@yahoo.com> wrote:

On Aug 14, 6:51 am, "Robert Klemme" <shortcut...@googlemail.com> > wrote:

> 2007/8/14, nikolai.weib...@gmail.com <nikolai.weib...@gmail.com>:

> > Hi!

> > I wanted to write a simple method for comparing two paths on a Windows
> > system. My initial algorithm felt very contrived:

> > def same_path?(a, b)
> > a, b = [a, b].map{ |p| File.expand_path(p).downcase }
> > a == b
> > end

> > It felt like saving the result from #map and then doing the comparison
> > shouldn't be necessary. So I came up with the following solution:

> > class Array
> > def apply(method)
> > shift.send(method, *self)
> > end
> > end

> > This allowed me to define #same_path? thusly:

> > def same_path?(a, b)
> > [a, b].map{ |p| File.expand_path(p).downcase }.apply(:==)
> > end

> > which, to me, looks a lot nicer. Array#apply takes a method (in a
> > symbolic manner) and applies it to its first element, passing the rest
> > of the elements as arguments to the given method.

> > Any thoughts? Is Array#apply a method that could potentially have
> > other uses than my specific example above?

> Your method should be called "apply!" because it's destructive. I'd
> rather not do it that way, i.e. without changing the array at hand.You
> can use inject for that.

> I'd use #inject for your problem:

> match = [a,b].map {|x| File.expand_path(x).downcase}.inject {|x,y| x==y}

match = [a,b].map {|x| File.expand_path(x).downcase}.uniq.size == 1

Hi --

Your method should be called "apply!" because it's destructive.

I could be wrong, but I think the convention (at least in core Ruby)
is that ! methods always come in a pair with the non-! version. I
don't think there are any cases where there's just a method called m!
where the ! indicates destructiveness (or other "danger"). All the
unpaired destructive methods have non-! names.

Ugh. This was so NOT about whether to call the method Array#apply or
Array#apply!.

I know -- I was just replying to a tangential point that Robert
raised.

I just wrote the absolute shortest solution I could
think of. I didn't suggest that it was the final version. Yes,
seeing as how my definition mutates its target, Array#apply! is the
appropriate name.

I would still disagree, on the grounds that ! doesn't mean that a
method changes its receiver; it means the method is "dangerous"
(Matz's definition), and that only has meaning in reference to a
non-dangerous but otherwise equivalent method.

I simply didn't want to clutter the definition
thusly:

class Array
def apply(method)
   first.send(method, *self[1..-1])
end
end

(which sort of begs the question why we don't have Array#rest :wink:

Good question :slight_smile:

David

···

On Tue, 14 Aug 2007, nikolai.weibull@gmail.com wrote:

On Aug 14, 2:07 pm, dbl...@rubypal.com wrote:

On Tue, 14 Aug 2007, Robert Klemme wrote:

--
* Books:
   RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242\)
   RUBY FOR RAILS (http://www.manning.com/black\)
* Ruby/Rails training
     & consulting: Ruby Power and Light, LLC (http://www.rubypal.com)

Hi --

* <dblack@rubypal.com> (14:07) schrieb:

Your method should be called "apply!" because it's destructive.

I could be wrong, but I think the convention (at least in core Ruby)
is that ! methods always come in a pair with the non-! version. I
don't think there are any cases where there's just a method called m!
where the ! indicates destructiveness (or other "danger"). All the
unpaired destructive methods have non-! names.

That is true for Array#delete. delete is always destructive, there is no
need to flag that.

And push, pop, <<, concat, replace, clear.... There's definitely no
general guideline that destructive methods end in !.

(Of course String has both delete and delete! :slight_smile:

But there is no need for your method to be destructive, so you need to
tell the world about it.

I think this makes sense. If unpaired dangerous methods have !, it
sort of suggests that any time there isn't a !, the method is
non-dangerous, which in turn suggests non-destructive... and that
isn't true.

It should simply be clear from the name[0] if a method is destructive.
Often you need the ! for that, in some cases it's obvious without the !.

I guess I take my cue from the Ruby core/standard language, where
there's no use of !, as far as I know, except to distinguish a
"dangerous" method from its non-dangerous partner. I don't know of any
case where a method is just considered "dangerous" in the abstract,
without comparison to another method.

Basically, I can't find any object for which this:

  obj.methods.grep(/!/).detect {|m| not obj.respond_to?(m.delete('!')) }

or similar tests with private methods, etc., returns a value.

David

···

On Tue, 14 Aug 2007, Simon Krahnke wrote:

--
* Books:
   RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242\)
   RUBY FOR RAILS (http://www.manning.com/black\)
* Ruby/Rails training
     & consulting: Ruby Power and Light, LLC (http://www.rubypal.com)

Well, the documentation is pretty clear about what it does. Even
though I'd considered it bugged. Perhaps the semantics of Pathname#==
should be clarified before we post any change suggestions.

So, should Pathname#== respect the platforms case insensitivity, for
example, on Windows and on Mac OS?

Should Pathname#== try hard to make sure that both arguments are as
complete and clean as possible, for example, by calling
Pathname#expand_path on both arguments? This would have to take into
account that Pathname#== currently has taken any argument that
responds to #to_s.

  nikolai

···

On Aug 14, 8:32 pm, Eric Hodel <drbr...@segment7.net> wrote:

On Aug 14, 2007, at 04:40, nikolai.weib...@gmail.com wrote:

> On Aug 14, 11:50 am, Eric Hodel <drbr...@segment7.net> wrote:

> > Is there something wrong with Pathname#==?

> Yes, not that it can't be fixed, but the current definition is sort of
> broken (on all systems):

Could you file a bug on the Ruby tracker? (Or has it been fixed?)

Ah, OK. Yeah, I suppose that's true. Still, my second suggestion for
how to implement Array#apply is probably better.

  nikolai

···

On Aug 14, 4:38 pm, dbl...@rubypal.com wrote:

On Tue, 14 Aug 2007, nikolai.weib...@gmail.com wrote:

> On Aug 14, 2:07 pm, dbl...@rubypal.com wrote:

> Ugh. This was so NOT about whether to call the method Array#apply or
> Array#apply!.

I know -- I was just replying to a tangential point that Robert
raised.

> I just wrote the absolute shortest solution I could
> think of. I didn't suggest that it was the final version. Yes,
> seeing as how my definition mutates its target, Array#apply! is the
> appropriate name.

I would still disagree, on the grounds that ! doesn't mean that a
method changes its receiver; it means the method is "dangerous"
(Matz's definition), and that only has meaning in reference to a
non-dangerous but otherwise equivalent method.

I dislike this kind of automation behind the scenes because there may
be situations where you do not want this behavior. Also keep in mind
that ultimately a safe comparison needs to access the file system
which has a significant performance impact.

I'd prefer either a method like #normalize which does all the
necessary transformations which enable == to compare for identical
file system paths or add a method that does the comparison explicitly.
Maybe #realpath can be adjusted to deliver this.

Just my 0.02EUR...

Kind regards

robert

···

2007/8/15, nikolai.weibull@gmail.com <nikolai.weibull@gmail.com>:

On Aug 14, 8:32 pm, Eric Hodel <drbr...@segment7.net> wrote:

> On Aug 14, 2007, at 04:40, nikolai.weib...@gmail.com wrote:

> > On Aug 14, 11:50 am, Eric Hodel <drbr...@segment7.net> wrote:

> > > Is there something wrong with Pathname#==?

> > Yes, not that it can't be fixed, but the current definition is sort of
> > broken (on all systems):

> Could you file a bug on the Ruby tracker? (Or has it been fixed?)

Well, the documentation is pretty clear about what it does. Even
though I'd considered it bugged. Perhaps the semantics of Pathname#==
should be clarified before we post any change suggestions.

So, should Pathname#== respect the platforms case insensitivity, for
example, on Windows and on Mac OS?

Should Pathname#== try hard to make sure that both arguments are as
complete and clean as possible, for example, by calling
Pathname#expand_path on both arguments? This would have to take into
account that Pathname#== currently has taken any argument that
responds to #to_s.