Boolean psuedo-protoclass Object extension

There are quite a few gems out there in the world that add boolean-like class functionality to Ruby. (Just FYI, Matz himself is adamantly opposed to these gems, and more specifically adding any Boolean superclass to the Ruby language. He just does not see the need.)

This is my idea at a Boolean psuedo-protoclass Object extension.

It firstly adds 3 default instance methods into class Object:

  • boolean?()
  • false?()
  • true?()

These methods can be overridden in custom boolean classes, if necessary.
(See examples at bottom of the attached file.)

It then defines a mixin module named Boolean, that serves as a proto-boolean-comparison-class. This mixin module is mixed into any custom class that needs to have boolean comparsion operator methods (<=>, <, >, etc.) These operator methods rely upon the 3 test methods added to Object (or overridden locally in the custom class.)

class_Boolean_extension.rb (10.2 KB)

I did this last April, and have not yet entertained if it would work if re-written as a Ruby 2.x refinement. Ordinarily, it is considered a no-no to directly modify Ruby base classes, especially Object. But this is just an exercise in Rubyishness.


Thoughts ?

In my opinion Boolean functionality isnā€™t really a necessity although in some cases perhaps useful.

Like any language you get used to the way it works after a while. Ruby is already very flexible like letting you use ā€˜thenā€™ or a ?. I myself prefer English over the shorter symbols because thatā€™s what Iā€™m used to coming from that type of language.

I think code would be more readable with:

if entity.respond_to?(:transformation).false?

instead of

```if entity.respond_to?(:transformation) == false`

Especially when there are multiple evaluations in the ā€œifā€ statement. I know I could code with ā€œunlessā€, but I tend to use that more in single line evaluations.

I would also like to code optional arguments that are initialized to ā€œnilā€ with:

if argument.not_nil?

instead of

if argument.nil? == false

We need to say that none of the extra method calls (Bruce shows) are actually necessary. And they are in fact poor programming etiquette, as listed in most Ruby coding style guides.

Meaning that the expression itself evaluates to boolean and the if or unless keywords act upon the result without need for calling the == or any other method. (This is why perhaps Matz is so opposed to boolean test methods or Boolean classes. But this is just a guess on my part.)

So it is usually prefered to use a logical negation modifier, rather than a comparison to one of the singleton boolean objects. Ie:

if !entity.respond_to?(:transformation)

ā€¦orā€¦

if not entity.respond_to?(:transformation)

ā€¦orā€¦

unless entity.respond_to?(:transformation)

But as Bruce also noted, many coders get confused with the codeā€™s logic when the unless keyword is used, and many coders stick with if and if not conditionals.

Anyway, although you CAN do as Bruce showed, these new Object test methods were meant to support custom Boolean classes, (as shown in the examples at the end of the file.)


ā€¦ . as I noted in the inline documentation, my ā€œgo at thisā€ considers nil to be boolean neutral, so I did nothing with it, because:

ā€¦ and ā€¦

ā€¦ can just as easily be written as:

if !argument.nil?

ā€¦orā€¦

if not argument.nil?

ā€¦orā€¦

unless argument.nil?

1 Like

I think you guys (and perhaps Matz) are missing the point about how a Boolean class would benefit coders.

You (and he, I think) are concentrating upon those 3 little helpers test methods, and the non-necessity of them.

Please look at the sample custom class definitions (following the definition of the Boolean mixin module.)


P.S. - I actually realized I left something out of the test methods. They are also supposed to test objects if they have the Boolean module mixed in as an ancestor class. (That was the whole point of making it a mixin module, and then includeā€™ing it into TrueClass and FalseClass, and for whatever custom class the Boolean module is mixed into.)

Thatā€™s what happens when you get distracted, and come back to something 8 months later. (But this is what I was hoping for. That a public discussion would jog my memory to what I intended.)

@DanRathbun I am a somewhat concrete thinker (some would say a blockhead) so I donā€™t do well with abstractions without use cases I can understand. I looked at the examples in your file, and I couldnā€™t see a reason why I would want to use them, a situation in which they would make anything I do easier or clearer. Could you provide a non-abstract example of when or why they would be useful? What would this let you do that is impossible, or obscure or unduly cumbersome using the existing stuff? Maybe Iā€™ve just been away from CS theory classes for too long, but I donā€™t get it.

(1) Ruby is multi-paradigm. There are always many ways to do the same things.

This study just may boil down to coding finesse or cleverness.
(Not really the words Iā€™m looking for here.)


(2) I have not said, and do not say, that my class study is meant to solve those 3 points.
Did I imply these ?

Only that it was a study that provided strict boolean testing.

Ie, a paradigm that:

  • does not consider nil to be boolean false
  • that does not consider non-boolean objects to be boolean true
  • one that allows 0 and 1 to test boolean-wise.
  • one that allows this comparability to be inherited into custom classes
  • but most importantly, does not change the way the Ruby interpreter works
    • so, a specially compiled interpreter is not needed
    • and code can still use normal Ruby boolean expressions (where nil is treated as false and any other object evaluates as true, etc.,) ie, most important that it doesnā€™t break normal ā€œlooseā€ boolean expressions

So to satisfy those ā€œlawsā€, we must have special ā€œstrictā€ test methods that the custom boolean classesā€™ methods will use.


(3) I also will retort that I do not believe that those 3 points are some ā€œundeniable rulesā€ that any coding paradigm must pass.

(But Iā€™ll say they look like the minimalistā€™s 3 golden rules.)


(4) Iā€™ll make a note to create SketchUp API specific examples.

This whole study actually did come from me reading several forum posts over the years, in which people were at a loss to even begin to know how to code a simple toggle command in SketchUp Ruby. Such as:

  • toggling between two rendering states
  • switching an extension option on & off
  • acting upon a yes / no response from the user

So, this study was actually aimed at basic support of higher level SketchUp specific functionality, in the realm of options and answers.

I never actually got to showing the instantiation of a option like class that could be toggled.
In SketchUp Ruby the class would likely write to the registry, a hash, or an attribute dictionary, for a specific extension.


One of the weird things about Ruby is that it doesnā€™t have a Boolean superclass as an ancestor of TrueClass and FalseClass, in order to group them together.
So for example, without my extension, you cannot do:

option.is_a?(Boolean)

ā€¦ for a reference (option) that may, at various times, be pointing at the singleton instance of two separate classes.

But you CAN if you simply do (at the console):

module Boolean; end
TrueClass.class_eval { include Boolean }
FalseClass.class_eval { include Boolean }

ā€¦ then:

option = true
option.is_a?(Boolean)
# returns >> true
option = false
option.is_a?(Boolean)
# returns >> true

The same will hold for any custom class that includes the Boolean module.

Currently, in order to specifically test for one of these two classes, you have to do this:

option.is_a?(TrueClass) || option.is_a?(FalseClass)

ā€¦ which I always found to be somewhat clunky.


Now, simply using references that directly point variously at true or false, does not protect from an error that can occur if the reference gets pointed at some other object(s).

This is where the custom wrapper Boolean subclass comes in. It will have the toggling mechanism built-in, and the coder just calls #toggle() upon an instance of the class. Internally is has a reference to a boolean that can only be one of the allowable values.

In SketchUp API use, a ExtensionOptions subclass of Boolean would have the value saving built-in. And there might even be a collection wrapper around that. (I have some code Iā€™ve played with in that vein, but itā€™s way rudimentary.)

1 Like

Iā€™ve also been missing a Boolean superclass in Ruby. In certain cases you want to know if a value is either true or false but not anything else, e.g. to expose it to the user using a toggle/checkbox instead of e.g. a text input.

It would help writing nicer code if you could write

if option.is_a?(Boolean)
  // Create checkbox for option.
end

instead of

if option == !!option
  // Create checkbox for option.
end

or

def is_boolean?(value)
  value == !!value
end

id is_boolean?(option9
  // Create checkbox for option.
end

Toggling boolean values could also be made nicer in Ruby.

option.toggle

looks better than

option = !option

However, although the above example shows where code could be a little less clunky by adding a Boolean superclass I doubt it is really worth using refinements or modify base classes for it. If it was added natively Iā€™d be happy but as it is now I think the gain is too small compared to how much extra code and complexity it would require.

I never actually studied whether refinements would work with this.
I suppose it could and then base classes would not be globally modified.

Ie something like? (I donā€™t know, just brainstorming.):

module Author::SomePlugin
  require "boolean" # loads modules Boolean and BooleanObject
  class OptionSet
    # refine Object, but only in this namespace
    using BooleanObject
    # mixin option helpers like toggle methods, etc.
    include Boolean
  end
end

True, ā€¦ I was playing with an options / settings class, and got sidetracked by this study because I thought it would enhance what I was working on. (It may have only served to prevent me from finishing the other study.)

Modifying base classes is so frowned upon in an embedded shared Ruby environment, Iā€™d rather use Ruby 2.x+ refinements. But (I was thinking at the time circe SketchUp 2015) in order to work with older SketchUps that use Ruby 1.8 that base classes would need to be modified.

And I was (still am) thinking of packaging this up as a public Ruby Gem that could be used in standalone ā€œsystemā€ Ruby processes, rather than application embedded processes.

As long as Matz has say, thisā€™ll not happen. Unless he can be convinced. Whenever the request comes up, he squashes it, and closes the issue in Ruby Redmine issue tracker system.

And weirdly, I do not know where to discuss this publicly anymore since the Ruby Forum was shut down (itā€™s read-only now.)

Huh? The goal was / is to reduce coding. It seems **I** have not gotten around to providing examples that show this, because I never finished the Options features that would (or rather could) use this Boolean functionality discussed here.

It that scenario, you would just create an option object, and it would have all of what you need to test booleaness, to toggle it between states, to save it out to the defaults, etc.
Youā€™d just simply do something like:

@save_option = PluginHelper::Option::new(
  key => 'AutoSave',
  name => "Save automatically",
  states => [true,false],
  default => 0 # state index
)

A boolean type of option object is only one of several option types a coder might need for an extension.

But even this example (above) is a bit low level. Iā€™d envisioned something even more robust and automated (easier to use), as many extensions need a set of options. (Similar to LanguageHandler that wraps a hash, but called OptionsHandler or whatever.)

my_opts = OptionsHandler::new(@extension.name,@langhandler)
my_opts.load # loads from defaults
my_opts.new_option(
  'FadeEdit', 'Fade during edit', OptionsHandler::OptionType::Boolean, true
)
# ... later
my_opts['FadeEdit'].toggle
my_opts.save

Anyway, ā€¦ these object classes could use their own internal testing code that would not need the base classes modified. (As I mentioned the the first post, there are 3 methods added to Object that can also be overridden in custom classes, which leads to the implication that these 3 methods can be instead just defined in a custom non-base or non-core class or module, and leave Object unmodified.
An current example is the method that switches a DC attribute among itā€™s set of allowable values.

1 Like