It’s probably an overused example, but let’s say I’m writing a unit testing framework. I’d like to have
test("arithmetic")
-> check(3+4)
-> equals(8)
There are two polymorphic calls, which take integers in this example.
Behind the scenes, though, there is more polymorphism going on.
First, the equals
check will likely want to delegate to a type-specific equality checker. Second, it equals
fails (as it will here), it will need to convert the actual and expected values to strings in order to generate the failure message.
This is achievable for built-in primitive types. But say I want to write tests for my parser, where the AST is represented by a tree of variants.
I’d like to able to say "when unit testing does an equals
on an AST node, it ignores the line and column number fields. I’d also like to be able to say “here’s how to format the AST node in a failure message.”
rescript-test does this by having you write your own helpers for each test:
let intEqual = (~message=?, a: int, b: int) =>
assertion(~message?, ~operator="intEqual", (a, b) => a === b, a, b)
let stringEqual = (~message=?, a: string, b: string) =>
assertion(~message?, ~operator="stringEqual", (a, b) => a == b, a, b)
This obviously works, but I feel that the level of abstraction is wrong: all I’d really want to do is to define what ==
means.
In Haskell the test framework would use type classes, and in Elixir it would use protocols. Users of the framework would then just provide implementations of functions such as toStrring
and eq
, which the framework would apply when needed.
So… is there an idiom for doing this in RS?
Dave