Syntax sugar: method[](...) => method(...)

Hi,

The [] method is really handy when you have an object that should
behave like a hash or array. One thing I find myself wishing I could
also do is something like

def logins[](date)
  @events.select{|e| e.action == :login and e.date == date}
end

There may be a better way to deal with this example, but this is just
to demonstrate the circumstances where you don't have an array or hash
called logins, but you'd like to be able to use that [] syntax. One
way I though about doing it is

def logins
  EventList.new @events.select{|e| e.action == :login}
end

class EventList
  def initialize data
    @data = data
  end
  def [] date
    @data.select{|e| e.date == date}
  end
end

logins[Date.today] # => all logins today from @events

I guess some people might prefer to write a method like
get_logins(date), but I like this syntax. It seems reasonable to also
define []= in EventList to update the original @events. What I'd like
to know is if there's another way to solve this problem, or if there
are any simple optimizations (maybe only keep one instance of
EventList and update its contents with each call to #events).

Lastly, has there been any mention of allowing function names like so
in ruby 1.9:
  def logins[] date
    # ...
  end

Thanks,
- Erwin

Hi,

···

In message "Re: Syntax sugar: method(...) => method(...)" on Sat, 30 Jun 2007 05:17:27 +0900, "Erwin Abbott" <erwin.abbott@gmail.com> writes:

Lastly, has there been any mention of allowing function names like so
in ruby 1.9:
def logins date
   # ...
end

It is quite difficult since

  obj.login[date]

would be ambiguous. We have no way to tell the method name to be
"login" or "login".

              matz.

def logins
    @_logins ||= Proc.new do |date|
      @events.select{|e| e.action == :login and e.date == date}
    end
  end

However, I feel you are limiting yourself. Try something like this
instead:

  def logins
    @_logins ||= (
      l = @events.select{|e| e.action == :login}
      def l.by_date(date)
        select{|e| e.date == date}
      end
      l
    )
  end

Then you can use:

  logins.by_date(date)

If needed, you could change #logins to lazy evaluate too.

T.

···

On Jun 29, 4:17 pm, "Erwin Abbott" <erwin.abb...@gmail.com> wrote:

Hi,

The method is really handy when you have an object that should
behave like a hash or array. One thing I find myself wishing I could
also do is something like

def logins(date)
  @events.select{|e| e.action == :login and e.date == date}
end

There may be a better way to deal with this example, but this is just
to demonstrate the circumstances where you don't have an array or hash
called logins, but you'd like to be able to use that syntax. One
way I though about doing it is

def logins
  EventList.new @events.select{|e| e.action == :login}
end

class EventList
  def initialize data
    @data = data
  end
  def date
    @data.select{|e| e.date == date}
  end
end

logins[Date.today] # => all logins today from @events

I guess some people might prefer to write a method like
get_logins(date), but I like this syntax. It seems reasonable to also
define = in EventList to update the original @events. What I'd like
to know is if there's another way to solve this problem, or if there
are any simple optimizations (maybe only keep one instance of
EventList and update its contents with each call to #events).

Hi,

The method is really handy when you have an object that should
behave like a hash or array. One thing I find myself wishing I could
also do is something like

def logins(date)
  @events.select{|e| e.action == :login and e.date == date}
end

class Module
  def bracket(name, &code)
    define_method("#{name}_bracketed", &code)
    m = instance_method("#{name}_bracketed")
    remove_method "#{name}_bracketed"

    define_method(name) do ||
       obj = Object.new
       m1 = m.bind(self)
       (class << obj; self; end).module_eval { define_method(:) { |x|
m1 } }
       obj
   end
  end
end

class Foo
   bracket :logins do |date|
     @data.select { |e| e.date == date }
  end
end

Who needs sugar?

(I so didn't test this at all)

There may be a better way to deal with this example, but this is just

···

On 6/29/07, Erwin Abbott <erwin.abbott@gmail.com> wrote:

to demonstrate the circumstances where you don't have an array or hash
called logins, but you'd like to be able to use that syntax. One
way I though about doing it is

def logins
  EventList.new @events.select{|e| e.action == :login}
end

class EventList
  def initialize data
    @data = data
  end
  def date
    @data.select{|e| e.date == date}
  end
end

logins[Date.today] # => all logins today from @events

I guess some people might prefer to write a method like
get_logins(date), but I like this syntax. It seems reasonable to also
define = in EventList to update the original @events. What I'd like
to know is if there's another way to solve this problem, or if there
are any simple optimizations (maybe only keep one instance of
EventList and update its contents with each call to #events).

Lastly, has there been any mention of allowing function names like so
in ruby 1.9:
  def logins date
    # ...
  end

Thanks,
- Erwin

It's pretty easy to achieve what you want:

irb(main):001:0> class Foo
irb(main):002:1> def logins
irb(main):003:2> x = %w{a b c}
irb(main):004:2> def x.(a)
irb(main):005:3> select {|e| e == a}
irb(main):006:3> end
irb(main):007:2> x
irb(main):008:2> end
irb(main):009:1> end
=> nil
irb(main):010:0> f=Foo.new
=> #<Foo:0x7ff7abc8>
irb(main):014:0> f.logins["w"]
=>
irb(main):015:0> f.logins["a"]
=> ["a"]

Here's another variant:

irb(main):016:0> class Bar
irb(main):017:1> def logins
irb(main):018:2> x = %w{a b c}
irb(main):019:2> lambda {|a| x.select {|e| e == a}}
irb(main):020:2> end
irb(main):021:1> end
=> nil
irb(main):022:0> b=Bar.new
=> #<Bar:0x7ff5bfe8>
irb(main):023:0> b.logins["a"]
=> ["a"]
irb(main):024:0> b.logins["w"]
=>

I.e. you just create a proxy object that will handle the call. However I am not sure it's a good idea - it's probably better to return the original array or a copy of it.

Kind regards

  robert

···

On 29.06.2007 22:17, Erwin Abbott wrote:

Hi,

The method is really handy when you have an object that should
behave like a hash or array. One thing I find myself wishing I could
also do is something like

def logins(date)
@events.select{|e| e.action == :login and e.date == date}
end

There may be a better way to deal with this example, but this is just
to demonstrate the circumstances where you don't have an array or hash
called logins, but you'd like to be able to use that syntax. One
way I though about doing it is

def logins
EventList.new @events.select{|e| e.action == :login}
end

class EventList
def initialize data
   @data = data
end
def date
   @data.select{|e| e.date == date}
end
end

logins[Date.today] # => all logins today from @events

I guess some people might prefer to write a method like
get_logins(date), but I like this syntax. It seems reasonable to also
define = in EventList to update the original @events. What I'd like
to know is if there's another way to solve this problem, or if there
are any simple optimizations (maybe only keep one instance of
EventList and update its contents with each call to #events).

I've wished for the functionality the OP suggested but I understand that
there really isn't much wiggle room in the current Ruby syntax.

This would probably be a headache from a parsing perspective but what about
something like:

    obj.login@[date]

which would be interpreted as calling the method named 'login@'.
This is suggestive of the Eiffel array indexing syntax but with the square
brackets to avoid any ambiguity with instance variables.

Gary Wright

···

On Jun 30, 2007, at 3:18 AM, Yukihiro Matsumoto wrote:

In message "Re: Syntax sugar: method(...) => method(...)" > on Sat, 30 Jun 2007 05:17:27 +0900, "Erwin Abbott" > <erwin.abbott@gmail.com> writes:

>Lastly, has there been any mention of allowing function names like so
>in ruby 1.9:
> def logins date
> # ...
> end

It is quite difficult since

  obj.login[date]

would be ambiguous. We have no way to tell the method name to be
"login" or "login".

Hi Logan,

Really cool stuff going on there, that was worth a thousand words. I
never knew about this UnboundMethod#bind thing, curious if Proc has a
similar mechanism. Also happy to learn Method# is an alias to
Method#call

Let me summarize what's going on here to make sure I understand. First
we're creating an UnboundMethod from the given block. Next we create
the named method, which returns a blank object with a method that
calls the block. Okay, that was simpler than I thought!

One thing I'll be trying to work out is calling the method "logins"
will return a plain Object instead of a list of @events that are of
the type :login. I think for my purposes defining a few more singleton
methods like obj.each and obj.size would be good enough.

···

On 7/9/07, Logan Capaldo <logancapaldo@gmail.com> wrote:

class Module
  def bracket(name, &code)
    define_method("#{name}_bracketed", &code)
    m = instance_method("#{name}_bracketed")
    remove_method "#{name}_bracketed"

    define_method(name) do ||
       obj = Object.new
       m1 = m.bind(self)
       (class << obj; self; end).module_eval { define_method(:) { |x|
m1 } }
       obj
   end
  end
end

class Foo
   bracket :logins do |date|
     @data.select { |e| e.date == date }
  end
end

Who needs sugar?

(I so didn't test this at all)

Thanks,
- Erwin

Hi,

···

In message "Re: Syntax sugar: method(...) => method(...)" on Tue, 10 Jul 2007 04:08:26 +0900, Gary Wright <gwtmp01@mac.com> writes:

This would probably be a headache from a parsing perspective but what
about something like:

   obj.login@[date]

Why we need syntax sugar when we can just call

     obj.login(date)

? Maybe you need

     obj.login(date)=whatever

this could be a new RCR.

              matz.

Hi,

At Tue, 10 Jul 2007 12:54:58 +0900,
Yukihiro Matsumoto wrote in [ruby-talk:258520]:

? Maybe you need

     obj.login(date)=whatever

this could be a new RCR.

I'd tried it once but abandoned. I don't think it impossible,
but it must be a really non-trivial change of parse.y.

···

--
Nobu Nakada

Or a really old one: http://oldrcrs.rubypal.com/rcr/show/157

... :slight_smile:

···

On Jul 10, 5:54 am, Yukihiro Matsumoto <m...@ruby-lang.org> wrote:

? Maybe you need

     obj.login(date)=whatever

this could be a new RCR.

I think that there is a significant semantic difference (to the programmer)
between brackets and parenthesis.

  foo(bar) # foo is interpreted as a verb
  foo[bar] # foo is interpreted as a noun (a collection of some sort)

The language rules also force different semantics on those two expressions.
In the first, 'foo' must be a method associated with self and so the method
has semantics defined by the definition of 'foo' but it isn't clear from the
syntax that some sort of indexing is being attempted.

In the second expression, there may be one or two method calls depending on whether
'foo' is a local variable or not. In either case, the interpretation of [bar] is not
done by a method associated with self but instead by a method associated with the
value of 'foo', which might be the result of a method call.

The benefit of some alternate indexing syntax is that the name of the collection and
the implementation of indexing on the collection can be localized to a single
method associated with self rather than delegating the indexing logic to an
intermediate object (the value of 'foo').

This would also probably reek havoc on parse.y but what about:

  foo@(bar)
  foo@(bar) = x

This builds upon Ruby's use of foo! and foo? as having distinct meanings.

Gary Wright

···

On Jul 9, 2007, at 11:54 PM, Yukihiro Matsumoto wrote:

In message "Re: Syntax sugar: method(...) => method(...)" > on Tue, 10 Jul 2007 04:08:26 +0900, Gary Wright > <gwtmp01@mac.com> writes:

>This would probably be a headache from a parsing perspective but what
>about something like:
>
> obj.login@[date]

Why we need syntax sugar when we can just call

     obj.login(date)