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
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.
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
This code includes some important points involved with my intention.
-
Data
must be metaclass to create type container (I called something like sum type above). -
Data
's subclass must be a statefull but never be instantiated for gathering member types. -
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
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.
Top comments (0)