Search code examples
rubyscalafunctional-programmingextractor

Is there a more functional way, than using custom case statements, to write this in Ruby?


Imagine the following code:

class SimpleLetter
  def values
    ("a" .. "z").to_a
  end

  def ===(other)
    values.include?(other)
  end
end

class Vowel < SimpleLetter
  def values
    ["a","e","i","o","u"]
  end
end

class Consonant < SimpleLetter
  def values
    super - Vowel.new.values
  end
end

objects = ("a" .. "f").to_a + (1 .. 5).to_a

objects.each do |letter|
  case letter
    when Vowel.new
      puts "#{letter} it's a vowel"
    when Consonant.new
      puts "#{letter} it's a consonant"
    else
      puts "#{letter} it's something else"
  end
end

I could have chosen any other classes instead, I am just using them as an example. I like Scala's match and extractors a lot, and I figured this could be a nice way of writing the same thing in Ruby. Is there a better way of writing the above without having to instantiate new objects just so I could call their === method?

Just to avoid unnecessary posts, yes, I know I could do this:

case letter
  when ["a","e","i","o","u"].include?(letter)
    # ...
end

Solution

  • You could use class methods instead of instance methods:

    class SimpleLetter
        def self.values
            ("a" .. "z").to_a
        end 
    
        def self.===(other)
            values.include?(other)
        end 
    end
    
    class Vowel < SimpleLetter
        def self.values
            ["a","e","i","o","u"]
        end 
    end
    
    class Consonant < SimpleLetter
        def self.values
            super - Vowel.values
        end 
    end
    
    objects = ("a" .. "f").to_a + (1 .. 5).to_a
    
    objects.each do |letter|
    
        case letter
            when Vowel
                puts "#{letter} it's a vowel"
            when Consonant
                puts "#{letter} it's a consonant"
            else
                puts "#{letter} it's something else"
        end 
    
    end