Lisp on Lines

Devin Mullins wrote:

class Foo; def thing; nil end end
f = Foo.new; f.thing #=> nil
m = f.method :thing; m.call #=> nil
class Foo; def thing; 5 end end
f.thing #=> 5
m.call #=> nil

I think what's going on here is that it's making a copy of the method
instead of just pointing a variable at it. Lisp does the same thing:

(defun foo () 'nil)
(foo) ;nil
(setf bar #'foo)
(funcall bar) ;nil
(defun foo () 5)
(foo) ;5
(funcall bar) ;nil

Hello Brian,

···

On Jul 25, 2005, at 8:55 PM, luke wrote:

Thanks Dave for the interesting post. Would these problems be
solved if
Common Lisp went open source?

Er, there are quite a few open source CL implementations

     http://www.cliki.net/Common%20Lisp%20implementation

Unfortunately there is no good plattform independent CL
implementation - all of them lack windows support. And none of
the free have good multithreading support, for this you must send
some many to Franz Inc.

It's sad to see the Lisp world in such a bad state.

--
Best regards, emailto: scholz at scriptolutions dot com
Lothar Scholz http://www.ruby-ide.com
CTO Scriptolutions Ruby, PHP, Python IDE 's

Hello Mark,

"luke" <lduncalfe@eml.nope> writes:

Thanks Dave for the interesting post. Would these problems be solved if
Common Lisp went open source?

I noticed a post on a site

Lemonodor: The Best Open Source Lisp

that suggests there are some promising dialects of Lisp that are open
source, but currently aren't as rich as, say Common Lisp.

Huh? Is GNU Common Lisp not actually an implementation of Common Lisp?
GCL is not only open source, it's Free Software(TM) in the
Richard Stallman-approved sense.

Here, in an image based system like LISP, free in the GPL sense is a very very
bad idea. Only BSD image based systems are useable for commercial
applications. This is a fundamental difference to the gcc compiler
where the GPL makes sense.

···

--
Best regards, emailto: scholz at scriptolutions dot com
Lothar Scholz http://www.ruby-ide.com
CTO Scriptolutions Ruby, PHP, Python IDE 's

What do you call patrician hooberglobbers that can be invoked with binding to instances of arbitrary classes, such that 'self' and instance variables refer to the supplied instance?

That's what I think of when I think of first-class functions, given my JS background: a single function that may be anonymous, assigned to a variable, and invoked with a specific binding. (And if it's also a closure, so much the better.)

There are few things that make me cry about Ruby, but of those that do, few make me cry more than the current situation with Methods/UnboundMethods/Procs/blocks/lambdas. It's workable, but it really isn't elegant. (IMO)

···

On Jul 25, 2005, at 7:48 PM, Devin Mullins wrote:

I have a stricter definition. Let's call it "patrician hooberglobbers." Patrician hooberglobbers, to me, have the additional quality that the syntax for manipulating a hooberglobber is the same whether it is referred to statically or by evaluation/substitution (as through a variable, or return value of a function call). In this sense, Ruby has patrician classes:

Sorry, please bear with me... the Lisp I had in mind was more like:

(setf a #'length)
(funcall a "foo")

(Hard to make an exact parallel because Ruby has methods not functions, and Lisp AFAIK has functions not methods...?)

Setting a variable to the function itself seems pretty different than wrapping a call to the function in a lambda, but honestly, now I can't think of what the actual implications would be... other than a little bit of additional terseness in calls like

(reduce #'+ '(1 2 3))
vs.
[1, 2, 3].inject {|a,b|a+b}

Are they really, for all intents and purposes, the same?

···

zak.wilson@gmail.com wrote:

It's the same in Lisp. In your first example, "test".length gets
evaluated, and that value is assigned to a:

Ruby:
a = "test".length
a.class #Fixnum
a #4

a = lambda {"test".length}
a.class #Proc
a.call() #4

Lisp:
(setf a (length "test"))
(a) ;error
(type-of a) ;(INTEGER 4 4)
a ;4

(setf a (lambda () (length "test")))
(type-of a) ;EVAL:INTERPRETED-FUNCTION
(funcall a) ;4

Is 1.9 going to be 2.0 when it's released?

···

On 7/25/05, Ara.T.Howard <Ara.T.Howard@noaa.gov> wrote:

On Tue, 26 Jul 2005, Devin Mullins wrote:

> zak.wilson@gmail.com wrote:
>
>> This looks like first class functions to me:
>>
>> def accgen (n)
>> lambda {|i| n += i }
>> end
>>
>> foo = accgen(5)
>> foo.call(5) # returns 10
>> foo.call(5) # returns 15
>>
>> accgen function borrowed from http://www.paulgraham.com/accgen.html
>>
> Perhaps we need a new term. According to Google, "First class hooberglobbers"
> seems to mean hooberglobbers can be assigned to variables, parameters, and
> return values, and then, though the assignee, can be manipulated in all the
> ways that hooberglobbers can be manipulated. Under this definition, yes, Ruby
> has first class functions.
>
> I have a stricter definition. Let's call it "patrician hooberglobbers."
> Patrician hooberglobbers, to me, have the additional quality that the syntax
> for manipulating a hooberglobber is the same whether it is referred to
> statically or by evaluation/substitution (as through a variable, or return
> value of a function call). In this sense, Ruby has patrician classes:
>
> a = String.new
> b = String
> c = b.new
>
> However, while Lisp, Python (from the examples I've seen), and Unlambda have
> patrician functions, Ruby does not. I can think of two simple enough reasons
> for this:
>
> 1. Because Ruby has a "poetry mode," there is no way to refer to a function
> by reference -- simply referring to it invokes it. The closest you can get is
> to refer to the Symbol representing its name, or to create a Method object,
> which really just wraps an invocation of the method inside a class.

   harp:~ > irb
   irb(main):001:0> a = lambda{ p 42 }
   => #<Proc:0xb755d848@(irb):1>

   irb(main):002:0> a ()
   42
   => nil

   irb(main):003:0> a()
   42
   => nil

   irb(main):004:0> b = a
   => #<Proc:0xb755d848@(irb):1>
   irb(main):005:0> b()
   42
   => nil

   harp:~ > ruby -v
   ruby 1.9.0 (2005-05-16) [i686-linux]

   harp:~ > irb -v
   irb 0.9.5(05/04/13)

zak.wilson@gmail.com writes:

Devin Mullins wrote:

class Foo; def thing; nil end end
f = Foo.new; f.thing #=> nil
m = f.method :thing; m.call #=> nil
class Foo; def thing; 5 end end
f.thing #=> 5
m.call #=> nil

I think what's going on here is that it's making a copy of
the method instead of just pointing a variable at it.

Actually, since methods are themselves immutable, making a
copy of one would have the same effect as “just pointing a
variable at it,” except it would be slower.

My point is that ‘def a.b ; 1 end ; def a.b ; 2 end’ is more
like ‘a.b = lambda {1} ; a.b = lambda {2}’ and less like
‘a.b = lambda {1} ; a.b.body = "2"’. (Of course, this last
statement is completely bogus.)

If that doesn't make sense, forget I ever said anything.
I know I mean, and I know you know what you mean. :slight_smile:

···

--
Daniel Brockman <daniel@brockman.se>

    So really, we all have to ask ourselves:
    Am I waiting for RMS to do this? --TTN.

[snip]

Unfortunately there is no good plattform independent CL
implementation - all of them lack windows support. And none of
the free have good multithreading support, for this you must send
some many to Franz Inc.

[snip]

OpenMCL has native threads doesn't it? Now if you want unicode /and/ threads you're out of luck :wink:

Adrian

···

On 26 Jul 2005, at 07:37, Lothar Scholz wrote:

What do you mean by "image based system"? And what does that have to
do with the GPL?

···

On 7/25/05, Lothar Scholz <mailinglists@scriptolutions.com> wrote:

Hello Mark,

> "luke" <lduncalfe@eml.nope> writes:
>>Thanks Dave for the interesting post. Would these problems be solved if
>>Common Lisp went open source?

>>I noticed a post on a site

>>Lemonodor: The Best Open Source Lisp

>>that suggests there are some promising dialects of Lisp that are open
>>source, but currently aren't as rich as, say Common Lisp.

> Huh? Is GNU Common Lisp not actually an implementation of Common Lisp?
> GCL is not only open source, it's Free Software(TM) in the
> Richard Stallman-approved sense.

Here, in an image based system like LISP, free in the GPL sense is a very very
bad idea. Only BSD image based systems are useable for commercial
applications. This is a fundamental difference to the gcc compiler
where the GPL makes sense.

Gavin Kistner wrote:

There are few things that make me cry about Ruby, but of those that do, few make me cry more than the current situation with Methods/ UnboundMethods/Procs/blocks/lambdas. It's workable, but it really isn't elegant. (IMO)

As a relative newcomer to Ruby, I would appreciate hearing you elaborate on this point.

Thanks!
John

Hi Joe,

Sorry, please bear with me... the Lisp I had in mind was
more like:

  (setf a #'length)
  (funcall a "foo")

(Hard to make an exact parallel because Ruby has methods
not functions, and Lisp AFAIK has functions not
methods...?)

Ruby has both methods and functions, but methods are more
primitive than functions. In particular, functions are
objects and thus have methods. Methods, on the other hand,
are not objects (and they are not functions). So functions
are implemented using methods, not the other way around.

As for the example code, the closest Ruby equivalent would
have to be something like the following:

   a = :length
   "foo".send(a)

This is because in Ruby, polymorphism is implemented by
having different ‘length’ methods for different objects,
whereas in Lisp, there is just one function ‘length’ that
handles all types of objects. (Of course, CLOS simplifies
the task of adding new clauses by way of defmethod, but a
generic function is still just one function.)

Setting a variable to the function itself seems pretty
different than wrapping a call to the function in a lambda,

Again, there is not just a single ‘length’ method in Ruby,
so you can't “set a variable to the function itself.” The
closest analogy to the “function itself” is the method name.

but honestly, now I can't think of what the actual
implications would be... other than a little bit of
additional terseness in calls like

(reduce #'+ '(1 2 3))
vs.
[1, 2, 3].inject {|a,b|a+b}

You could actually define the equivalent of Lisp's ‘+’
function in Ruby, by doing this:

   module Kernel
     def +(a, b) a + b end
   end

Then the example with inject could be written like this:

   [1,2,3].inject &method(:+)

But this will fail when someone overrides the ‘+’ operator,
in which case you need to do something like this:

   [1,2,3].inject &Object.new.method(:+)

Much cleaner would be to define the function in a separate
namespace so that name clashes are avoided:
   
   Function = Object.new
   class << Function
     alias method
     def +(*terms) terms.inject(0) { |a, b| a + b } end
   end

Now the example looks like this:

   [1,2,3].inject &Function[:+]

You could go one step further:

   class Symbol
     def to_proc
       Function[self].to_proc
     end
   end

Note that if you take the Kernel#+ approach, the above
implementation needs to be much hairier:

   class Symbol
     def to_proc
       Binding.of_caller do |boc|
         eval(%{method(:#{self})}, boc).to_proc
       end
     end
   end

Now you can reduce the original example (no pun intended)
to the following,

   [1,2,3].inject &:+

which is actually *shorter* than the equivalent Lisp code.

By the way, a more popular definition of Symbol#to_proc is
to have ‘foo.each &:bar’ equal ‘foo.each { |x| x.bar }’.
This won't work in the above example because we cannot use
the Fixnum#+ method to fold an array of integers (since it
only takes one argument!).

(If you want both variants of Symbol#to_proc, I guess you
could use a unary plus or minus operator to disambiguate.
However, ‘[1,2,3].inject &-:+’ is starting to look rather
much like line noise.)

···

--
Daniel Brockman <daniel@brockman.se>

    So really, we all have to ask ourselves:
    Am I waiting for RMS to do this? --TTN.

In an image based system, like lisp or smalltalk, all software is loaded into memory and directly accessed. There is no concept of linking. That explanation is a bit simplistic maybe, but it gives you the idea. I think the difficulty is more to to with the LGPL than the GPL, since the LGPL is defined in terms of linking while the GPL is defined in terms of use. In an image based system you can't have linking, so you can't be LGPL compliant, and so you must be GPL compliant. There is actually something called the LLGPL, where the one of the 'L's stands for 'lisp', that deals with this situation.

I *think* what Lothar is arguing is that using GCL is different than using GCC for commercial applications because of the fact that GCL is image based. This isn't necessarily true, it depends on how the runtime is licensed and designed since GCL compiles to native via GCC and so has a linking step hidden away in there -- but I'm too apathetic/unconcerned to look up the details. As usual, if you don't like the GPL don't use GPLed software. Simple. (Maybe a more interesting question is whether you are allowed to run non-GPL-compatibly licensed software with the GCL runtime at all?)

I don't understand why Lothar says only the BSD license is safe for commercial use -- that is trivially not true since there are several commercially licensed CL implementations that are definitely not BSD licensed, nor is it necessarily true of open source implementations as there are dozens/hundreds of OSI approved licenses -- so I imagine he is just being a bit overly specific. His point is clear enough if you don't take it literally.

I hope this makes some sense... I suppose I really shouldn't be posting this late.

Cheers,
Bob

···

On Jul 26, 2005, at 11:55 PM, Joe Van Dyk wrote:

On 7/25/05, Lothar Scholz <mailinglists@scriptolutions.com> wrote:

Here, in an image based system like LISP, free in the GPL sense is a very very
bad idea. Only BSD image based systems are useable for commercial
applications. This is a fundamental difference to the gcc compiler
where the GPL makes sense.

What do you mean by "image based system"? And what does that have to
do with the GPL?

----
Bob Hutchison -- blogs at <http://www.recursive.ca/hutch/&gt;
Recursive Design Inc. -- <http://www.recursive.ca/&gt;

ruby-talk ML wrote:

Gavin Kistner wrote:

> There are few things that make me cry about Ruby, but of those that
> do, few make me cry more than the current situation with Methods/
> UnboundMethods/Procs/blocks/lambdas. It's workable, but it really
> isn't elegant. (IMO)

As a relative newcomer to Ruby, I would appreciate hearing you elaborate
on this point.

Search the ruby-talk archives and you'll find a good bit about it.
Basically we have four/five differnt entities that are all almost but
not quite the same things. Hopefully Rite/Ruby2 will simplify this
some.

T.

One could generalize that solution:

class Symbol
  def to_proc
    lambda { |a,*b| a.send(self,*b) }
  end
end

[1,2,3].inject(&:+)

(For some reason, irb complained unless I put parens around it.)

For an even shorter version, here's my APL subset for Ruby:

class Symbol
  def *(ary)
    ary.inject {|a,b| a.send(self, b)}
  end
  def (*ary)
    self * ary
  end
end
class Array
  def +@
    :+*self
  end
  def -@
    :-*self
  end
end

Now, :+*[1,2,3], :+[1,2,3], and +[1,2,3] are all valid. (Not that I actually use this libary; just came up with it to make a point about Ruby to somebody. :P)

(I would've overloaded /, but Ruby kept thinking I was trying to construct a regular expression.)

And here's a vaguely neat extension of that idea:

class Symbol
  def **(ary)
    ary.inject { |a,b| a.send(self, *b) }
  end
end

:gsub ** ["Hello!",
          [/Hell/,"Good"]
          [/o!/,"bye.]]

Talk about reducing duplication. :slight_smile:

Devin

Daniel Brockman wrote:

···

Hi Joe,

Sorry, please bear with me... the Lisp I had in mind was
more like:

(setf a #'length)
(funcall a "foo")

(Hard to make an exact parallel because Ruby has methods
not functions, and Lisp AFAIK has functions not
methods...?)
   
Ruby has both methods and functions, but methods are more
primitive than functions. In particular, functions are
objects and thus have methods. Methods, on the other hand,
are not objects (and they are not functions). So functions
are implemented using methods, not the other way around.

As for the example code, the closest Ruby equivalent would
have to be something like the following:

  a = :length
  "foo".send(a)

This is because in Ruby, polymorphism is implemented by
having different ‘length’ methods for different objects,
whereas in Lisp, there is just one function ‘length’ that
handles all types of objects. (Of course, CLOS simplifies
the task of adding new clauses by way of defmethod, but a
generic function is still just one function.)

Setting a variable to the function itself seems pretty
different than wrapping a call to the function in a lambda,
   
Again, there is not just a single ‘length’ method in Ruby,
so you can't “set a variable to the function itself.” The
closest analogy to the “function itself” is the method name.

but honestly, now I can't think of what the actual
implications would be... other than a little bit of
additional terseness in calls like

(reduce #'+ '(1 2 3))
vs.
[1, 2, 3].inject {|a,b|a+b}
   
You could actually define the equivalent of Lisp's ‘+’
function in Ruby, by doing this:

  module Kernel
    def +(a, b) a + b end
  end

Then the example with inject could be written like this:

  [1,2,3].inject &method(:+)

But this will fail when someone overrides the ‘+’ operator,
in which case you need to do something like this:

  [1,2,3].inject &Object.new.method(:+)

Much cleaner would be to define the function in a separate
namespace so that name clashes are avoided:
    Function = Object.new
  class << Function
    alias method
    def +(*terms) terms.inject(0) { |a, b| a + b } end
  end

Now the example looks like this:

  [1,2,3].inject &Function[:+]

You could go one step further:

  class Symbol
    def to_proc
      Function[self].to_proc
    end
  end

Note that if you take the Kernel#+ approach, the above
implementation needs to be much hairier:

  class Symbol
    def to_proc
      Binding.of_caller do |boc|
        eval(%{method(:#{self})}, boc).to_proc
      end
    end
  end

Now you can reduce the original example (no pun intended)
to the following,

  [1,2,3].inject &:+

which is actually *shorter* than the equivalent Lisp code.

By the way, a more popular definition of Symbol#to_proc is
to have ‘foo.each &:bar’ equal ‘foo.each { |x| x.bar }’.
This won't work in the above example because we cannot use
the Fixnum#+ method to fold an array of integers (since it
only takes one argument!).

(If you want both variants of Symbol#to_proc, I guess you
could use a unary plus or minus operator to disambiguate.
However, ‘[1,2,3].inject &-:+’ is starting to look rather
much like line noise.)

Daniel Brockman wrote:

Hi Joe,

Sorry, please bear with me... the Lisp I had in mind was
more like:

(setf a #'length)
(funcall a "foo")

(Hard to make an exact parallel because Ruby has methods
not functions, and Lisp AFAIK has functions not
methods...?)
   

Ruby has both methods and functions, but methods are more
primitive than functions. In particular, functions are
objects and thus have methods. Methods, on the other hand,
are not objects (and they are not functions). So functions
are implemented using methods, not the other way around.

Interesting--that's the first I've heard of it. Do you have a link to some documentation about functions? I don't see anything in the PickAxe or ri.

Oh, is it the Function syntax you demonstrate below?

As for the example code, the closest Ruby equivalent would
have to be something like the following:

  a = :length
  "foo".send(a)

This is because in Ruby, polymorphism is implemented by
having different ‘length’ methods for different objects,
whereas in Lisp, there is just one function ‘length’ that
handles all types of objects. (Of course, CLOS simplifies
the task of adding new clauses by way of defmethod, but a
generic function is still just one function.)

I understand. I was not concerned about polymorphic methods vs. functions, but rather the ability to directly capture a reference to a [method|function].

Let me try illustrating in Boo instead:

class Foo:
  def bar():
    print("hello")

a = Foo() # calling Foo constructor
b = a.bar
b() # same as calling a.bar()

This is different than what you are trying to accomplish in your post, and admittedly, even pretty different from what my Lisp example was in terms of functionality.

Setting a variable to the function itself seems pretty
different than wrapping a call to the function in a lambda,
   

Again, there is not just a single ‘length’ method in Ruby,
so you can't “set a variable to the function itself.” The
closest analogy to the “function itself” is the method name.

I was looking for something like the above example in Boo. Although using lambda/proc, send(:symbol), or method(:symbol), they all seem to accomplish the task (with useful differences in behavior between the three of them).

but honestly, now I can't think of what the actual
implications would be... other than a little bit of
additional terseness in calls like

(reduce #'+ '(1 2 3))
vs.
[1, 2, 3].inject {|a,b|a+b}
   

You could actually define the equivalent of Lisp's ‘+’
function in Ruby, by doing this:

  module Kernel
    def +(a, b) a + b end
  end

Then the example with inject could be written like this:

  [1,2,3].inject &method(:+)

That is actually pretty close. I've never seen the &-syntax (that is, not from the caller)... I can see there are a lot of possibilities with that.

But this will fail when someone overrides the ‘+’ operator,
in which case you need to do something like this:

  [1,2,3].inject &Object.new.method(:+)

Much cleaner would be to define the function in a separate
namespace so that name clashes are avoided:
    Function = Object.new
  class << Function
    alias method
    def +(*terms) terms.inject(0) { |a, b| a + b } end
  end

Now the example looks like this:

  [1,2,3].inject &Function[:+]

You could go one step further:

  class Symbol
    def to_proc
      Function[self].to_proc
    end
  end

Note that if you take the Kernel#+ approach, the above
implementation needs to be much hairier:

  class Symbol
    def to_proc
      Binding.of_caller do |boc|
        eval(%{method(:#{self})}, boc).to_proc
      end
    end
  end

Now you can reduce the original example (no pun intended)
to the following,

  [1,2,3].inject &:+

which is actually *shorter* than the equivalent Lisp code.

Wow, that is really cool. Although one problem... my irb is complaining about Binding.of_caller not being defined. Do I need to require something first?

By the way, a more popular definition of Symbol#to_proc is
to have ‘foo.each &:bar’ equal ‘foo.each { |x| x.bar }’.
This won't work in the above example because we cannot use
the Fixnum#+ method to fold an array of integers (since it
only takes one argument!).

(If you want both variants of Symbol#to_proc, I guess you
could use a unary plus or minus operator to disambiguate.
However, ‘[1,2,3].inject &-:+’ is starting to look rather
much like line noise.)

I didn't mean to emphasize #'+, it is just the canonical example in the Lisp tutorials I've read... :slight_smile:

Thanks for the very interesting response, I learned a lot.

Hello Bob,

I *think* what Lothar is arguing is that using GCL is different than
using GCC for commercial applications because of the fact that GCL is
image based. This isn't necessarily true, it depends on how the runtime
is licensed and designed since GCL compiles to native via GCC and so
has a linking step hidden away in there -- but I'm too

The question is what is the runtime ?

If you for example want any user customizable parts of your
application, you normally allow the loading of lisp files and then the
problem comes up. There are no clear borders and normally you end up
very soon with using the whole lisp system.

I don't understand why Lothar says only the BSD license is safe for
commercial use -- that is trivially not true since there are several
commercially licensed CL implementations that are definitely not BSD
licensed, nor is it necessarily true of open source implementations as

Yes i meant for open source lisp implementations, the commercial ones
are normally okay by definition.

I only know a few of the licenses and so to cut a long story short _I_
would only accept a BSD lisp systems or one that is completely public
domain. And there is none available.

···

--
Best regards, emailto: scholz at scriptolutions dot com
Lothar Scholz http://www.ruby-ide.com
CTO Scriptolutions Ruby, PHP, Python IDE 's

Devin Mullins <twifkak@comcast.net> writes:

One could generalize that solution:

class Symbol
  def to_proc
    lambda { |a,*b| a.send(self,*b) }
  end

Wow, I really like it! Why didn't I think of that?

You've unified my version with the usual definition,
resulting in the be-all and end-all of Symbol#to_proc.

This *so* needs to be put in the standard library. :slight_smile:

class Symbol
  def *(ary)
    ary.inject {|a,b| a.send(self, b)}
  end

Way cool! I actually like this one, and I'm not kidding.
Redefine Array#* to double-dispatch on symbols and this will
actually be usable. Think about it—

     [1,2,3] * "+" means [1,2,3].join("+"),

so it makes sense for

     [1,2,3] * :+ to mean [1,2,3].inject(&:+).

More intuitively, we think of ‘[*foo] * "bar"’ as joining
the elements of ‘foo’ by "bar". Analogously, we can think
of ‘[*foo] * :bar’ as joining, or more correctly *recuding*,
the elements of ‘foo’ using :bar.

This strikes me as highly intuitive:

   [1,2,3] * "+" #=> "1+2+3"

   [1,2,3] * :+ #=> 1 + 2 + 3

As does this (though perhaps slightly less so):

   [1,2,3] * "foo" #=> "1foo2foo3"

   [1,2,3] * :foo #=> 1.foo(2).foo(3)"

[crazy APL stuff snipped]

Now, :+*[1,2,3], :+[1,2,3], and +[1,2,3] are all valid.
(Not that I actually use this libary; just came up with it
to make a point about Ruby to somebody. :P)

That's cool; you really managed to take this to its extreme.
Of course, ‘:+[1,2,3]’ is too cryptic and ‘+[1,2,3]’ is
better written as ‘[1,2,3].sum’, but still. :slight_smile:

(I would've overloaded /, but Ruby kept thinking I was
trying to construct a regular expression.)

Hmm, what would you have had that do?

And here's a vaguely neat extension of that idea:

class Symbol
  def **(ary)
    ary.inject { |a,b| a.send(self, *b) }
  end
end

:gsub ** ["Hello!",
          [/Hell/,"Good"]
          [/o!/,"bye.]]

Talk about reducing duplication. :slight_smile:

Wow... amazing. Of course, this

   ["Hello!", [/Hell/, "Good"], [/o!/, "bye."]] ** :gsub

is both longer and more cryptic than this,

   "Hello!".gsub(/Hell/, "Good").gsub(/o!/, "bye.")

but still... the former has *no* duplication. :slight_smile:

By the way, the above example suggests that it might be
useful to have String#gsub take multiple arguments:

   class String
     unless method_defined? :gsub1
       alias :gsub1 :gsub
       def gsub(*a)
         (a.size == 1 ? a.first : a.pairs).
           inject(self) { |a, b| a.gsub1 *b }
       end
     end
   end

   module Enumerable
     def pairs ; groups(2) end
     def groups(n)
       (a = entries).each_index { |i| a[i, n] = [a[i, n]] }
     end
   end

   "Hello!".gsub(/Hell/ => "Good", /o!/ => "bye.") #=> "Goodbye."

Using a hash to visually pair each expression to its
replacement has the unfortunate side effect of effectively
randomizing the order of the substitutions, so you'll have
to use commas if order is significant:

   "Hello!".gsub(/Hell/, "Good", /o!/, "bye.") #=> "Goodbye."

Anyway, thanks for sharing your cool inventions, Devin. :slight_smile:

···

--
Daniel Brockman <daniel@brockman.se>

    So really, we all have to ask ourselves:
    Am I waiting for RMS to do this? --TTN.

SBCL and CMUCL are mostly public domain, those parts that are not are
BSD licenced. Clisp is GPL, with an LGPL-like exception for saved
images.

Daniel Brockman wrote:

Wow, I really like it! Why didn't I think of that?

You weren't being challenged by someone showing you a code snippet claiming that APL is a superior language. :slight_smile: BTW, change *b to b, in my code.

This *so* needs to be put in the standard library. :slight_smile:

Aww... that's the nicest thing anybody could ever say...

Way cool! I actually like this one, and I'm not kidding.
Redefine Array#* to double-dispatch on symbols and this will
actually be usable. Think about it—

    [1,2,3] * "+" means [1,2,3].join("+"),

so it makes sense for

    [1,2,3] * :+ to mean [1,2,3].inject(&:+).

Actually, I like that a lot more than :+ * [1,2,3].

(I would've overloaded /, but Ruby kept thinking I was
trying to construct a regular expression.)
   

Hmm, what would you have had that do?

Sorry, I just assumed that everybody except me knew APL, so I could just talk about it as if I knew what I was talking about and get buy-in from everybody else. In APL, the syntax for doing that is something akin to "+ / 1 2 3". But you showed an excellent reason why "*" makes more sense for Ruby.

Wow... amazing. Of course, this

  ["Hello!", [/Hell/, "Good"], [/o!/, "bye."]] ** :gsub

is both longer and more cryptic than this,

  "Hello!".gsub(/Hell/, "Good").gsub(/o!/, "bye.")

but still... the former has *no* duplication. :slight_smile:

Well, if you're doing, like, seven gsubs, the former will win out, but if you're chaining seven gsubs, you're probably doing something wrong. I suppose you could use "** :gsub" to aid in some sort of configuration file thing:

class Array
  def **(sym)
    inject {|a,b| a.send(sym,*b)} end end

gsubs = YAML::load [[/Hell/,"Good"],[/o!/,"bye."]].to_yaml #your config file here
some_words_ive_got_lying_around = %w{cat dog tree Hello! french oreo!}
some_words_ive_got_lying_around.map! {|word| [word,*gsubs] ** :gsub }
#=> ["cat", "dog", "tree", "Goodbye.", "french", "orebye."]

By the way, the above example suggests that it might be
useful to have String#gsub take multiple arguments:

  class String
    unless method_defined? :gsub1

Heh, never seen that "unless method_defined?" thing before. I like it, I think. (I'm still not comfortable about the alias-and-call-original trick, and this makes me a little more comfortable about it.)

  "Hello!".gsub(/Hell/ => "Good", /o!/ => "bye.") #=> "Goodbye."
  "Hello!".gsub(/Hell/, "Good", /o!/, "bye.") #=> "Goodbye."

Hehe, neat. I actually haven't used gsub a lot, so I can't comment on its usefulness, but I see a lot of other people gsubbing a lot, so I bet it would be a pretty desired addition.

Anyway, thanks for sharing your cool inventions, Devin. :slight_smile:

You're welcome, and thanks for calling my inventions cool! Makes me feel better for not contributing a library or toolkit or whatever to the Ruby community. :slight_smile:

Random aside: I learned something about #map just now:

irb(main):001:0> [1,2,3].map
=> [1, 2, 3]
irb(main):002:0> [1,2,3].map!
LocalJumpError: no block given
        from (irb):2:in `map!'
        from (irb):2

Odd... :stuck_out_tongue:

Devin

Devin Mullins <twifkak@comcast.net> writes:

Wow, I really like it! Why didn't I think of that?

You weren't being challenged by someone showing you a code
snippet claiming that APL is a superior language. :slight_smile:

Hmm, yeah, I guess that would've done it. :slight_smile:

BTW, change *b to b, in my code.

Why? I can't think of any iterators that pass three or more
arguments to the block off-hand (at least I can't think of
any such cases where it makes sense to simply invoke a
single method upon each yield), but does it hurt to support
that hypothetical case anyway?

Way cool! I actually like this one, and I'm not kidding.
Redefine Array#* to double-dispatch on symbols and this
will actually be usable. Think about it—

    [1,2,3] * "+" means [1,2,3].join("+"),

so it makes sense for

    [1,2,3] * :+ to mean [1,2,3].inject(&:+).

Actually, I like that a lot more than :+ * [1,2,3].

Yeah... I was thinking they should both work, but I guess
that's unnecessary, given that ‘"+" * [1,2,3]’ doesn't work.

Random aside: In mathematics, a non-commutable + operator
is pretty damn near unthinkable, wheras in computer science
it's completely normal. I think that's kind of interesting.

(I would've overloaded /, but Ruby kept thinking I was
trying to construct a regular expression.)

Hmm, what would you have had that do?

Sorry, I just assumed that everybody except me knew APL,
so I could just talk about it as if I knew what I was
talking about and get buy-in from everybody else.

Ah. In light of this thread, I'm actually a little curious
about what other insights one might get from learning APL.

In APL, the syntax for doing that is something akin to
"+ / 1 2 3". But you showed an excellent reason why "*"
makes more sense for Ruby.

I see.

Wow... amazing. Of course, this

   ["Hello!", [/Hell/, "Good"], [/o!/, "bye."]] ** :gsub

is both longer and more cryptic than this,

   "Hello!".gsub(/Hell/, "Good").gsub(/o!/, "bye.")

but still... the former has *no* duplication. :slight_smile:

Well, if you're doing, like, seven gsubs, the former will
win out, but if you're chaining seven gsubs, you're
probably doing something wrong.

Good point. Although I don't think lots of gsubs are any
indication that you are doing something wrong. (I'd hate to
think *I* ever did something wrong.)

I suppose you could use "** :gsub" to aid in some sort of
configuration file thing:

class Array
  def **(sym)
    inject {|a,b| a.send(sym,*b)} end end

Nice to see someone else stuffing away those pesky ‘end’s
like that. I have my Emacs coloring them really dimly.

gsubs = YAML::load [[/Hell/,"Good"],[/o!/,"bye."]].to_yaml
#your config file here
some_words_ive_got_lying_around = %w{cat dog tree Hello! french oreo!}
some_words_ive_got_lying_around.map! {|word| [word,*gsubs] ** :gsub }
#=> ["cat", "dog", "tree", "Goodbye.", "french", "orebye."]

Okay, but with my modified String#gsub, that's better
written like this:

   gsubs.flatten!
   some_words.map! { |word| word.gsub(*gsubs) }

By the way, the above example suggests that it might be
useful to have String#gsub take multiple arguments:

   class String
     unless method_defined? :gsub1

Heh, never seen that "unless method_defined?" thing before.
I like it, I think. (I'm still not comfortable about the
alias-and-call-original trick, and this makes me a little
more comfortable about it.)

Yeah. Having been bitten a few times by the double-aliasing
bug that comes into play when a file that uses the “alias
and redefine” idiom is loaded twice, I always try to make
sure my (declarative) code is resistant to being loaded
multiple times.

···

--
Daniel Brockman <daniel@brockman.se>

    So really, we all have to ask ourselves:
    Am I waiting for RMS to do this? --TTN.