Nice work! I reviewed your PR for the docs.
Yes, the measurer reorganization seems tricky.
My thoughts:
1. Measurers and Reporters should become typed - this would probably
mean that their surrounding test class also carries a type parameter
2. Measurers would benefit from a `map` method - i.e. it would be both
useful and straightforward to make them functors
3. I'm still not sure how much we would win if we add the `zip` method
to Measurer. There are some possible gains here, but it seems extremely
tricky to implement a general composition of two measurers. I'm not sure
if even the Runner/Gauge split solve all the problems we've identified.
4. I like that you identified and proposed the Quantity class. I would
still try to simplify it a bit:
- I would try to eliminate the second type parameter
- The quantity is essentially a pair of an arbitrary value and some
string that represents a unit. In your gist, you defined several
categories of quantities, along with constrained conversion within them.
I would instead try to keep it simple - the user should be able to
transform the quantity arbitrarily (there is even a use case for this -
if you measured 10 iterations of some loop, but want to display the
running time for 10000 iterations, then you really want to scale the
value, but keep the unit intact). I would argue for allowing users to do
something (more or less) like this:
val scaledMeasurer = originalMeasurer.map {
case (value, unit) => (value * 1000, unit)
}
Or, if the users want to use the stack profiling measurer, but map it to
the percentage of a particular method, they should be able to do
something like this:
val boxMeasurer = profileMeasurer.map {
case (profile, "") => (profile("BoxesRuntime.boxToInteger"), "%")