Forum: Ruby-core [ruby-trunk - Feature #7986][Open] Custom case statement comparison method

Posted by Thomas Sawyer (7rans)
on 2013-02-28 14:09
(Received via mailing list)
Issue #7986 has been reported by trans (Thomas Sawyer).

----------------------------------------
Feature #7986: Custom case statement comparison method
https://bugs.ruby-lang.org/issues/7986

Author: trans (Thomas Sawyer)
Status: Open
Priority: Normal
Assignee:
Category: core
Target version: Next Major


=begin
Case statements use #=== to handle matching, but sometimes this can be 
limiting. It such cases it would be helpful to be able to specify the 
comparison method. So I wondered if that could be done by hanging the 
method off the `case` keyword, e.g.

    class Bar < Foo; end

    case == obj.class
    when Foo
      p "Foo, not Bar!"
    when Bar
      p "Bar!"
    end

And

    case.include? name
    when a
      p "a includes name"
    when b
      p "b includes name, not a"
    else
      p "neither a or b includes name"
    end
=end
Posted by drbrain (Eric Hodel) (Guest)
on 2013-03-01 04:56
(Received via mailing list)
Issue #7986 has been updated by drbrain (Eric Hodel).


=begin
Why not use the features of case statements properly?  For classes, 
place the subclass above the superclass.  For array inclusion, use 
splat:

  class Foo; end
  class Bar < Foo; end

  def case_klass obj
    case obj
    when Bar
      p "Bar!"
    when Foo
      p "Foo, not Bar!"
    end
  end

  case_klass Foo.new
  case_klass Bar.new

  def case_include obj
    case obj
    when *%w[b]
      p "b includes name, not a"
    when *%w[a]
      p "a includes name"
    else
      p "neither a or b includes name"
    end
  end

  case_include 'a'
  case_include 'b'
  case_include 'c'

=end

----------------------------------------
Feature #7986: Custom case statement comparison method
https://bugs.ruby-lang.org/issues/7986#change-37215

Author: trans (Thomas Sawyer)
Status: Open
Priority: Normal
Assignee:
Category: core
Target version: Next Major


=begin
Case statements use #=== to handle matching, but sometimes this can be 
limiting. It such cases it would be helpful to be able to specify the 
comparison method. So I wondered if that could be done by hanging the 
method off the `case` keyword, e.g.

    class Bar < Foo; end

    case == obj.class
    when Foo
      p "Foo, not Bar!"
    when Bar
      p "Bar!"
    end

And

    case.include? name
    when a
      p "a includes name"
    when b
      p "b includes name, not a"
    else
      p "neither a or b includes name"
    end
=end
Posted by Thomas Sawyer (7rans)
on 2013-03-01 09:47
(Received via mailing list)
Issue #7986 has been updated by trans (Thomas Sawyer).


One does not necessarily have the luxury of putting the classes in 
order. Consider a case that uses inheritance:

  class Handler
    def handle(obj)
      case obj
      when Foo
        "Foo"
      end
    end
  end

  class SubHandler < Handler
    def handle(obj)
      result = super(obj)
      return result if result

      case obj
      when Bar
        p "Bar"
      end
    end
  end

Now add a dozen or so additional classes and that is the use case I 
presently have.

As for the splat, one can do that for this particular example. (Though I 
wonder how efficient splatting in this way will be). I was just trying 
to give another example off the top of my head. But the point is that 
any method can be used, which is nice for it's flexibility.

    case.foo? name
    when a
      p "a foos name"
    when b
      p "b foos name, not a"
    else
      p "neither a nor b foos name"
    end

----------------------------------------
Feature #7986: Custom case statement comparison method
https://bugs.ruby-lang.org/issues/7986#change-37218

Author: trans (Thomas Sawyer)
Status: Open
Priority: Normal
Assignee:
Category: core
Target version: Next Major


=begin
Case statements use #=== to handle matching, but sometimes this can be 
limiting. It such cases it would be helpful to be able to specify the 
comparison method. So I wondered if that could be done by hanging the 
method off the `case` keyword, e.g.

    class Bar < Foo; end

    case == obj.class
    when Foo
      p "Foo, not Bar!"
    when Bar
      p "Bar!"
    end

And

    case.include? name
    when a
      p "a includes name"
    when b
      p "b includes name, not a"
    else
      p "neither a or b includes name"
    end
=end
Posted by "Martin J. Dürst" <duerst@it.aoyama.ac.jp> (Guest)
on 2013-03-01 10:50
(Received via mailing list)
On 2013/03/01 17:42, trans (Thomas Sawyer) wrote:

> One does not necessarily have the luxury of putting the classes in order. 
Consider a case that uses inheritance:

[snip]

> Now add a dozen or so additional classes and that is the use case I presently 
have.

I'm of course not familiar with your problem or program, but a
proliferation of case statements, or case alternatives, is usually taken
as a sign that there is something more fundamental that needs to be
improved.

Regards,   Martin.
Posted by Thomas Sawyer (7rans)
on 2013-03-01 14:46
(Received via mailing list)
Issue #7986 has been updated by trans (Thomas Sawyer).


@martin Being able to rely on inheritance and case statements I am able 
to remove hundreds of lines of complex DSL code. For example, old code:

    emit Foo, via: foo

    def emit_foo(obj)
       ...
    end

The new code:

    def emit(obj)
      case obj
      when Foo
        ...
      end
    end

While the first might look a bit prettier, the latter is far more 
flexible. This is a good example of the YADSL principle actually --don't 
create a DSL when regular code can do the job just as well or better. So 
that's the particular case I am presently looking at.

Regardless of my particular case though, doesn't being able to select 
the case operator seem like a nice bit of flexibility in itself?

Another example that comes to mind:

    case.start_with? uri
    when "http:" then ...
    when "https:" then ...
    when "ftp:" then ...
    else ...
    end

Think of the efficiency improvements over the typical use of regular 
expressions in such a case.

----------------------------------------
Feature #7986: Custom case statement comparison method
https://bugs.ruby-lang.org/issues/7986#change-37223

Author: trans (Thomas Sawyer)
Status: Open
Priority: Normal
Assignee:
Category: core
Target version: Next Major


=begin
Case statements use #=== to handle matching, but sometimes this can be 
limiting. It such cases it would be helpful to be able to specify the 
comparison method. So I wondered if that could be done by hanging the 
method off the `case` keyword, e.g.

    class Bar < Foo; end

    case == obj.class
    when Foo
      p "Foo, not Bar!"
    when Bar
      p "Bar!"
    end

And

    case.include? name
    when a
      p "a includes name"
    when b
      p "b includes name, not a"
    else
      p "neither a or b includes name"
    end
=end
Posted by drbrain (Eric Hodel) (Guest)
on 2013-03-01 21:44
(Received via mailing list)
Issue #7986 has been updated by drbrain (Eric Hodel).


=begin
It is puzzling that you must always super first, then if unhandled 
execute the case expression, but do nothing if the object is unknown. 
It seems very error prone.

Why not execute the subclass case expression first, then super in its 
else?

As such, I agree with Martin that there is something wrong with your 
design. If you study design patterns you'll find that dispatching to 
methods is preferred (such as in the visitor pattern) to using 
comparison constructs due to the inflexibility that your design 
displays.

A case expression can already handle URI matching just fine:

  case uri
  when /\Ahttps:/i then …
  when /\Ahttp:/i then …
  when /\Aftp:/i then …
  else …
  end

or if you use URI objects:

  case uri
  when URI::HTTPS then …
  when URI::HTTP then …
  when URI::FTP then …
  else …
  end

or:

  case uri.scheme.downcase
  when 'https' then …
  when 'http' then …
  when 'ftp' then …
  else …
  end

=end
----------------------------------------
Feature #7986: Custom case statement comparison method
https://bugs.ruby-lang.org/issues/7986#change-37243

Author: trans (Thomas Sawyer)
Status: Open
Priority: Normal
Assignee:
Category: core
Target version: Next Major


=begin
Case statements use #=== to handle matching, but sometimes this can be 
limiting. It such cases it would be helpful to be able to specify the 
comparison method. So I wondered if that could be done by hanging the 
method off the `case` keyword, e.g.

    class Bar < Foo; end

    case == obj.class
    when Foo
      p "Foo, not Bar!"
    when Bar
      p "Bar!"
    end

And

    case.include? name
    when a
      p "a includes name"
    when b
      p "b includes name, not a"
    else
      p "neither a or b includes name"
    end
=end
Posted by Thomas Sawyer (7rans)
on 2013-03-01 22:00
(Received via mailing list)
Issue #7986 has been updated by trans (Thomas Sawyer).


=begin
> "It is puzzling that you must always super first, then if unhandled execute the 
case expression, but do nothing if the object is unknown. It seems very error 
prone."

Why? That's not uncommon. In this particular case you don't "always". It 
depends on which you want to take precedence, the superclass or the 
subclass. Recall my case is in place of a DSL --it's up to the end 
developer to decide which. Super may even be called in the middle 
somewhere. It is not at all error prone in the least.

> A case expression can already handle URI matching just fine

Of course, and I mentioned that. And what did I say? Quote: "think of 
the efficiency improvements over the typical use of regular 
expressions". Moreover the point has nothing whatsoever to do with URIs.

I could sit here and give 100 different examples and for everyone, 
someone could say, well you could also do it this way. In the end you 
can replace every case statement with if/else too. That point is the 
convenience it provides.
=end

----------------------------------------
Feature #7986: Custom case statement comparison method
https://bugs.ruby-lang.org/issues/7986#change-37244

Author: trans (Thomas Sawyer)
Status: Open
Priority: Normal
Assignee:
Category: core
Target version: Next Major


=begin
Case statements use #=== to handle matching, but sometimes this can be 
limiting. It such cases it would be helpful to be able to specify the 
comparison method. So I wondered if that could be done by hanging the 
method off the `case` keyword, e.g.

    class Bar < Foo; end

    case == obj.class
    when Foo
      p "Foo, not Bar!"
    when Bar
      p "Bar!"
    end

And

    case.include? name
    when a
      p "a includes name"
    when b
      p "b includes name, not a"
    else
      p "neither a or b includes name"
    end
=end
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.