Exploring the type system vol 2. (yesterday it tur...
# getting-started
p
Exploring the type system vol 2. (yesterday it turned out the prepare phase was a bad idea) Can I disallow for a type variable not to be something? Here, condition can either: • return match / no match • return a match with an object / no match without any additional data (Should the 2 factory methods have different names?) https://pl.kotl.in/LWtYo8KZs
Seems almost like Result, but in failure is not an error and does not have a stacktrace or exception
j
To be frank, looking at the code as a whole, it seems that the
Rule
class implementation doesn't bring anything meaningful to the table, given that the 2 factory functions are so vastly different. The whole code could be reduced to just a
Rule
interface with abstract
fire()
and 2 different factory functions providing the boolean approach or the object-based one
p
I'd like to be a mini rule engine. "Facts" can get in (with fire()), and if a rule matches (condition is truthy), it fires.
The next level will be, when you can have a List of Rules (with different types), and you can fire() on the list of rules. That would be my next question.
Copy code
val rule1: Rule<Int, Void>;
val rule2: Rule<String, Whatever>;

val ruleset = RuleSet(rule1, rule2, ...);
ruleset.fireAllRulesFor("string");
j
ruleset.fireAllRulesFor("string");
I think an extremely similar question was posted recently 🤔
🎯 1
p
We had drools among our libraries, but had more worries than value, so we are throwing it away. A simple implementation would fit, and more easier to debug.
👌 1
j
Back to your original question, what I meant is that the whole implementation of
fire
could be passed to the
Rule
class to make it more general, no need for breaking it down into parts
Then you can build on top by having factory functions (or subclasses) that can deal with a condition, or transform the value, etc.
Also
if (!clazz.isInstance(arg)) return false
is redundant. This class cannot be called with an
arg
that is not a
T
, unless you mess with the compiler, but in that case this is not the place to put this check. In our original conversation about
fireAllRulesFor
, this check was in the
RuleSet
(not the
Rule
) for this reason.
p
True, it is redundnat.
And here comes my second question: how to put different rules to one collection, and then how to fireAllRules, which will call the appropriate fire-s?
j
Is there something that doesn't fit the bill in the approach I suggested there? https://kotlinlang.slack.com/archives/C0B8MA7FA/p1718027014018009?thread_ts=1718018864.681229&amp;cid=C0B8MA7FA
p
Just hoped that I can do in a "safe" way
j
What do you mean by "safe" in this case? The signature of the method allows the callers to use it in a perfectly type-safe manner. Inside the body of this method, we perform a manual check before doing an unchecked cast (the unchecked part of the cast is actually checked by the first filter, which makes it safe). The important bit is to encapsulate it so the callers are safe.
p
Yeah, sure, just seems to be unsafe because of the @Suppress annotation
j
Generic casts are not safe by themselves, that's why the compiler warns you. The point of the suppression is to show that we're aware of the things that the compiler cannot check and and we made sure to check them ourselves
I'm not sure we can do better if you really want to use heterogeneous lists of generic things
p
And is there other way to declare, that a lambda is bound to Int? so instead of
Copy code
val rule = Rule.compute<Int, _>("int rule", obj = { if (this < 0) this * 2 else null })
something like
Copy code
val rule = Rule.compute("int rule", obj = Int.{ if (this < 0) this * 2 else null })
(tweaked the syntax with 2 inline factory methods)