Why does Ruby have TrueClass and FalseClass instead of a single Boolean class?

前端 未结 8 858
野趣味
野趣味 2020-12-07 20:16

I was working on serializing values when found out about this one. Ruby has a TrueClass class, and a FalseClass class, but it has no Boolean class. I\'d like to

相关标签:
8条回答
  • 2020-12-07 20:17

    Since everything but false and nil evaluate to true in Ruby by default, you would only need to add parsing to String.

    Something like this could work:

    class Object
    
      ## Makes sure any other object that evaluates to false will work as intended,
      ##     and returns just an actual boolean (like it would in any context that expect a boolean value).
      def trueish?; !!self; end
    
    end
    
    class String
    
      ## Parses certain strings as true; everything else as false.
      def trueish?
        # check if it's a literal "true" string
        return true if self.strip.downcase == 'true'
    
        # check if the string contains a numerical zero
        [:Integer, :Float, :Rational, :Complex].each do |t|
          begin
            converted_number = Kernel.send(t, self)
            return false if converted_number == 0
          rescue ArgumentError
            # raises if the string could not be converted, in which case we'll continue on
          end
        end
    
        return false
      end
    
    end
    

    When used, this would give you:

    puts false.trueish?   # => false
    puts true.trueish?    # => true
    puts 'false'.trueish? # => false
    puts 'true'.trueish?  # => true
    puts '0'.trueish?     # => false
    puts '1'.trueish?     # => true
    puts '0.0'.trueish?   # => false
    puts '1.0'.trueish?   # => true
    

    I believe part of the “big idea” behind Ruby is to just make the behavior you desire inherent to your program (e.g. boolean parsing), rather creating a fully encapsulated class that lives in it's own namespace world (e.g. BooleanParser).

    0 讨论(0)
  • 2020-12-07 20:24

    The purpose of a class is to group similar objects, or objects with similar behavior together. 1 and 2 are very similar, therefore it makes perfect sense for them to be in the same class. true and false however are not similar. In fact, their whole point is that they are exactly the opposite of each other and have opposite behavior. Therefore, they don't belong in the same class.

    Can you give an example of what sort of common behavior you would implement in a Boolean class? I can't think of anything.

    Let's just look at the behavior that TrueClass and FalseClass have: there's exactly four methods there. No more. And in every single case, the two methods do exactly the opposite. How and why would you put that in a single class?

    Here's how you implement all those methods:

    class TrueClass
      def &(other)
        other
      end
    
      def |(_)
        self
      end
    
      def ^(other)
        !other
      end
    
      def to_s
        'true'
      end
    end
    

    And now the other way around:

    class FalseClass
      def &(_)
        self
      end
    
      def |(other)
        other
      end
    
      def ^(other)
        other
      end
    
      def to_s
        'false'
      end
    end
    

    Granted, in Ruby, there is a lot of "magic" that is going on behind the scenes and that is not actually handled by TrueClass and FalseClass but rather hardwired into the interpreter. Stuff like if, &&, || and !. However, in Smalltalk, from which Ruby borrowed a lot including the concept of FalseClass and TrueClass, all of these are implemented as methods as well, and you can do the same thing in Ruby:

    class TrueClass
      def if
        yield
      end
    
      def ifelse(then_branch=->{}, _=nil)
        then_branch.()
      end
    
      def unless
      end
    
      def unlesselse(_=nil, else_branch=->{})
        ifelse(else_branch, _)
      end
    
      def and
        yield
      end
    
      def or
        self
      end
    
      def not
        false
      end
    end
    

    And again the other way around:

    class FalseClass
      def if
      end
    
      def ifelse(_=nil, else_branch=->{})
        else_branch.()
      end
    
      def unless
        yield
      end
    
      def unlesselse(unless_branch=->{}, _=nil)
        ifelse(_, unless_branch)
      end
    
      def and
        self
      end
    
      def or
        yield
      end
    
      def not
        true
      end
    end
    

    A couple of years ago, I wrote the above just for fun and even published it. Apart from the fact that the syntax looks different because Ruby uses special operators while I use only methods, it behaves exactly like Ruby's builtin operators. In fact, I actually took the RubySpec conformance testsuite and ported it over to my syntax and it passes.

    0 讨论(0)
  • 2020-12-07 20:26

    Quoting Matz on Ruby forum (2013):

    ...There's nothing true and false commonly share, thus no Boolean class. Besides that, in Ruby, everything behave as Boolean value....

    0 讨论(0)
  • 2020-12-07 20:27

    It seems that Matz himself answered this question on a mailing list message in 2004.

    Short version of his answer: "right now it works ok, adding a Boolean doesn't give any advantage".

    Personally I don't agree with that; the aforementioned "string parsing" is one example. Another one is that when you are applying different treatment to a variable depending on its type, (i.e. a yml parser) having a "Boolean" class is handy - it removes one "if". It also looks more correct, but that's a personal opinion.

    0 讨论(0)
  • 2020-12-07 20:27

    As others have said, you could "patch" Ruby. Create your own class. Here is something I came up with. The methods on the Boolean class are a bit silly, but they could be useful programmatically at some point.

    class Boolean
      def self.new(bool)
        bool
      end
    
      def self.true
        true
      end
    
      def self.false
        false
      end
    end
    
    class FalseClass
      def is_a?(other)
        other == Boolean || super
      end
    
      def self.===(other)
        other == Boolean || super
      end
    end
    
    class TrueClass
      def is_a?(other)
        other == Boolean || super
      end
    
      def self.===(other)
        other == Boolean || super
      end
    end
    
    0 讨论(0)
  • 2020-12-07 20:34

    In Ruby nil and false are false and everything else is true. Hence there is no need for a specific boolean class.

    You can try it :

    if 5
      puts "5 is true"
    end
    

    5 evaluates to true

    if nil
        puts "nil is true"
    else
        puts "nil is false"
    end
    

    Will print "nil is false"

    0 讨论(0)
提交回复
热议问题