[#51213] [ruby-trunk - Bug #7645][Open] BigDecimal#== slow when compared to true/false — "mathie (Graeme Mathieson)" <mathie@...>

11 messages 2013/01/01

[#51328] [ruby-trunk - Bug #7676][Open] Comparison of Float::NAN in array behaves unexpectedly — "simonrussell (Simon Russell)" <spam+ruby@...>

11 messages 2013/01/09

[#51347] [ruby-trunk - Bug #7679][Open] IRB history is broken — "zzak (Zachary Scott)" <zachary@...>

15 messages 2013/01/10

[#51389] [ruby-trunk - Bug #7688][Open] Error hiding with rb_rescue() on Comparable#==, #coerce and others — "Eregon (Benoit Daloze)" <redmine@...>

34 messages 2013/01/11

[#51430] [ruby-trunk - Bug #7696][Open] Lazy enumerators with state can't be rewound — "marcandre (Marc-Andre Lafortune)" <ruby-core@...>

15 messages 2013/01/14

[#51437] [ruby-trunk - Bug #7698][Open] RubyGems 2.0 has an incompatibility about installation of extension libraries — "mrkn (Kenta Murata)" <muraken@...>

21 messages 2013/01/15

[#51454] [CommonRuby - Feature #7701][Open] Non-optional (required) keyword args — "headius (Charles Nutter)" <headius@...>

31 messages 2013/01/15

[#51499] [ruby-trunk - Feature #7712][Open] Add .txt extensions to all plain-text documentation files for Windows users — "postmodern (Hal Brodigan)" <postmodern.mod3@...>

9 messages 2013/01/18

[#51619] [ruby-trunk - Feature #7738][Open] Deprecate Set#+ as an alias of Set#|, use it for symmetric difference. Introduce Hash#| for Hash#reverse_merge in Rails. — "alexeymuranov (Alexey Muranov)" <redmine@...>

11 messages 2013/01/24

[#51623] [ruby-trunk - Feature #7739][Open] Define Hash#| as Hash#reverse_merge in Rails — "alexeymuranov (Alexey Muranov)" <redmine@...>

24 messages 2013/01/24

[#51714] [CommonRuby - Feature #7747][Open] Expanded API for Binding semantics — "jballanc (Joshua Ballanco)" <jballanc@...>

19 messages 2013/01/27

[#51742] [ruby-trunk - Bug #7756][Open] clang 3.2 sees through UNINITIALIZED_VAR macro, gives warning — "drbrain (Eric Hodel)" <drbrain@...7.net>

10 messages 2013/01/29

[#51763] [ruby-trunk - Bug #7758][Open] Ruby on Windows crashes when active codepage is codepage 65001 and outputting unicode character — "joshc (Josh C)" <josh.nw@...>

16 messages 2013/01/30

[ruby-core:51650] [ruby-trunk - Feature #7604] Make === comparison operator ability to delegate comparison to an argument

From: "ko1 (Koichi Sasada)" <redmine@...>
Date: 2013-01-25 03:52:47 UTC
List: ruby-core #51650
Issue #7604 has been updated by ko1 (Koichi Sasada).

Category set to core
Target version set to next minor


----------------------------------------
Feature #7604: Make === comparison operator ability to delegate comparison to an argument
https://bugs.ruby-lang.org/issues/7604#change-35611

Author: prijutme4ty (Ilya Vorontsov)
Status: Open
Priority: Normal
Assignee: 
Category: core
Target version: next minor


=begin
I propose to expand default behaviour of === operator in the following way:
Objects have additional instance method Object#reverse_comparison?(other) which is false by default in all basic classes.
Each class that overrides Object#===(other) should check whether reverse_comparison? is true or false
If it is false, behavior is not changed at all.
If it is true, comparison is delegated to === method of an argument with self as an argument. 

This technique can help in constructing RSpec-style matchers for case statement. Example:

  # usual method call
  arr = %w[cat dog rat bat]
  puts arr.end_with?(%w[dog bat])   # ==> false
  puts arr.end_with?(%w[rat bat])   # ==> true
  puts arr.end_with?(%w[bat])       # ==> true
  
  # predicate-style case
  case %w[cat dog rat bat].end_with?
  when %w[dog bat]
    puts '..., dog, bat'
  when %w[rat bat]
    puts '..., rat, bat'
  when %w[bat]
    puts '..., bat'
  else
    puts 'smth else'
  end
  # ==> ..., rat, bat

Code needed to run this is not very complex:
  class Object
    def reverse_comparison?(other)
      false
    end
    alias_method :'old===', :'==='
    def ===(other)
      (other.reverse_comparison?(self) ? (other.send 'old===',self) : (self.send 'old===',other))
    end
  end

  class Predicate
    def initialize(&block)
      @block = block
    end
    def reverse_comparison?(other)
      true
    end
    def ===(*args)
      @block.call(*args)
    end
  end

  class Array
    alias_method :'old===', :'==='
    def ===(other)
      other.reverse_comparison?(self) ? (other.send('===',self)) : (self.send('old===',other))
    end

    def end_with?(expected_elements = nil)
      return last(expected_elements.size) == expected_elements  if expected_elements
      Predicate.new{|suffix| last(suffix.size) == suffix }
    end
  end

This technique looks powerful and beautiful for me. One detail is that obj#reverse_comparison? can distinguish different types of arguments and returns true only for certain types of given object. Also this can be used to prevent double-mirroring (as shown below)

The problem is that many base classes already defined custom === operator, so each of those classes (Fixnum, Float, String, Regexp, Range etc) should be redefined in such a way to make a solution full-fledged.
Another problem is case that both objects defined reverse_comparison? to return true. In my solution Predicate#=== just ignores result of revese_comparison? which is not consistent.
Another possible way is to raise errors on double mirroring:
  def reverse_comparison?(other)
    raise 'double mirroring'  if @__mirroring_started
    @__mirroring_started = true
    return true  unless other.reverse_comparison?(self)
    false
  ensure
    remove_instance_variable :@__mirroring_started
  end

My proposal is to add reverse_comparison? method and change base classes operator === to use its result as shown above. May be it's worth also to make a class analogous to Predicate in stdlib.
=end


-- 
http://bugs.ruby-lang.org/

In This Thread