DEV Community

mtwtkman
mtwtkman

Posted on

Rethink pattern matching in Ruby

I'm working with ruby but I don't know about ruby well.

And I usually feel that ruby's case ... when never be able to be enough for me because it is soooo loose about data type domein.

I love ADT and its strictness, then I want to introduce strictness like ADT in ruby. Of course ruby doesn't has ADT type system, but has flexibility.

I made a prototype.

I'm sharing how I have made decision a design with ruby which is the programming language I'm not used to.

Decide interface or usage

This phase does'nt matter about what language I use.
Just think about HOW DO I WANT TO USE.

My first priority is strictness and completeness which means that there is a guarantee of a type guard at runtime.

So I made that like below.

# I need a something like some type to gather type constructors.
class Data < PseudoSumType; end 

# I can create any classes freely as product type.
class NoParam
end 

class SingleParam
  def initialize(x)
    @x = x
  end
end

class MultiParams
  def initialize(x, y)
    @x = x
    @y = y
  end
end

# Data is like a container. so now gathering.
Data.define_types(NoParam, SingleParam, MultiParams)

# Activate pattern matching.
# And this system is guard against incompleteness.
# Either lack of branch or lack of fallback shall occur runtime error.
matching = Data.match.of(NoParam).then(->(o) { o.do_something })
          .of(SingleParam).then(->(o) { o.do_something })
          .otherwise(->{ do_fallback }) # Fallback must be requried feature.

# Evaluate pattern matching and it enable to call defined callback. 
raise unless matching.eval(NoParam.new) == do_something_result

Enter fullscreen mode Exit fullscreen mode

Ok, now interface is fixed.

...

Wait a minute. I consider about the key point is the chain of of and then methods. I must avoid to call like .of().of and then().then().

Finally I made a dicision about this structure like below.

Image description

All I need is just implementing that.

Implementing

My ruby ability maybe be a problem about this phase.

Basically there is no difficult but one thing had been difficut to me.

See the code.

class Data
  include Singleton

  def self.inherited(subclass)
    subclass.class_variable_set(:@@types, [])

    def subclass.define_type(type)
      raise Error::AlreadyDefinedError if has?(type)

      class_variable_set(:@@types, owned_types << type)
    end

    def subclass.define_types(*types)
      types.each { define_type(it) }
    end

    def subclass.owned_types
      class_variable_get(:@@types)
    end

    def subclass.match
      raise Error::NoDataDefinedError if owned_types.empty?
      Tree.new(self)
    end

    def subclass.has?(klass)
      owned_types.include?(klass)
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

This code includes some important points involved with my intention.

  1. Data must be metaclass to create type container (I called something like sum type above).
  2. Data's subclass must be a statefull but never be instantiated for gathering member types.
  3. Data's subclass should not be an abstractclass for practial usefulness.

Mainly, I puzzed about inheritention and ruby's singleton class(Actually I don't know how should I say in english. In japanese, I call it "特異クラス")'s self.

I recognized that ruby's singleton class's self is just self lexically.

I mean,

class Data
   include Singleton

   # This `self` is Data itself.
   def self.define_type(type)
      ...
   def 
end
Enter fullscreen mode Exit fullscreen mode

So I use inherited hook and define subclass's method dynamically.
I don't want to metaprogramming but I don't know how avoid it.

Additinally, not so essential but trivial probrme is ruby's class variable.

Though I think that class variable should be used so careful basically because claass variable is shared, I want to use class variable at Data's subclass to store member types.
But class variable is not so match my intention, you know that if I set a class variable in Data directly then this attribute belongs to Data not Data's subclass.

Defin error, Testing etc

I'm curious about strictness, of course I should avoid bottom case.
But I know this topic is so boring for you.

If you interested in, please refer test codes.
And example is here.

Conclusion

I rethink ruby's pattern match style. This is so pernal, hobby, very not for production, yah, I know.

But I dould ruby more and more depeply. It have been so fun and so nice experience.

Tanks to read.

Playwright CLI Flags Tutorial

5 Playwright CLI Flags That Will Transform Your Testing Workflow

  • --last-failed: Zero in on just the tests that failed in your previous run
  • --only-changed: Test only the spec files you've modified in git
  • --repeat-each: Run tests multiple times to catch flaky behavior before it reaches production
  • --forbid-only: Prevent accidental test.only commits from breaking your CI pipeline
  • --ui --headed --workers 1: Debug visually with browser windows and sequential test execution

Learn how these powerful command-line options can save you time, strengthen your test suite, and streamline your Playwright testing experience. Practical examples included!

Watch Video 📹️

Top comments (0)

Playwright CLI Flags Tutorial

5 Playwright CLI Flags That Will Transform Your Testing Workflow

  • 0:56 --last-failed: Zero in on just the tests that failed in your previous run
  • 2:34 --only-changed: Test only the spec files you've modified in git
  • 4:27 --repeat-each: Run tests multiple times to catch flaky behavior before it reaches production
  • 5:15 --forbid-only: Prevent accidental test.only commits from breaking your CI pipeline
  • 5:51 --ui --headed --workers 1: Debug visually with browser windows and sequential test execution

Learn how these powerful command-line options can save you time, strengthen your test suite, and streamline your Playwright testing experience. Click on any timestamp above to jump directly to that section in the tutorial!

Watch Full Video 📹️

👋 Kindness is contagious

Explore a trove of insights in this engaging article, celebrated within our welcoming DEV Community. Developers from every background are invited to join and enhance our shared wisdom.

A genuine "thank you" can truly uplift someone’s day. Feel free to express your gratitude in the comments below!

On DEV, our collective exchange of knowledge lightens the road ahead and strengthens our community bonds. Found something valuable here? A small thank you to the author can make a big difference.

Okay