Arrow: ["Request"] Add monad-applicative-consistency check to MonadLaws

Created on 16 Dec 2019  路  3Comments  路  Source: arrow-kt/arrow

What would you like to see?

A test which checks whether or not the default implementation for ap (and several other methods from below the monad-hierarchy (select,...)) are consistent in that they produce the same results.

There is this but this is not a good test, since it uses the derived selective methods from monad and those will always be equal to itself ^-^

What needs to be done?
A test case added to MonadLaws which takes an explicit Selective/Applicative/Apply/Functor (which is not the same as the Monad derived one) and that checks if the methods return equal results.

Why bother?
Because changing a constraint should never change the outcome of the code:
IO { println(1) }.ap(IO { { _ -> println(2) } }) should produce the same output for both the applicative and the monad instance ap. If it doesn't it really hurts the ability to properly reason about code.

Btw, I haven't tested this, but I am pretty sure this will fail for the code that I posted above, because the default ap from Monad is implemented as ff.flatMap { f -> this.map { f(it) } } whereas the IO.ap is flatMap { a -> ff.map { it(a) } }

bug enhancement help wanted noob friendly

All 3 comments

I'll probably do this after christmas, but if someone wants to do it before, please go ahead :)

I'll probably include this in #1843. Is it enough to test the core operations from each typeclass?

EDIT: For the moment I'm only requesting Functor and Applicative instances for MonadLaws because not every datatype has an instance for Selective (ie AndThen)

Is it enough to test the core operations from each typeclass?

It'd be good to take a quick look at what monad overwrites from those classes and test those. It is usually only core operations, but in case of followedBy from #1843 that should also be tested in a way.

EDIT: For the moment I'm only requesting Functor and Applicative instances for MonadLaws because not every datatype has an instance for Selective (ie AndThen)

The way I'd add this is as a method overloads on the laws method with more parameters. Then it's opt-in, because in kotlin there are actually valid reasons to not have consistency in some cases. In haskell that is usually solved by using a newtype but in kotlin that approach would not be nice to use, so having a different applicative instance might be desired. I do this in the propCheck rework because the applicative instance can generate better shrinkers than the derived monad one. Also an applicative datatype that runs computation in parallel or somehow inspects the results first come to mind. Those do not exist in arrow, but might be perfectly valid user code and without newtypes breaking this law is the only good option.

This approach means adding one method to every laws test above the monad laws and calling the more specific function which takes an extra Functor, Apply, Applicative and Selective instance. In case one instance is missing from the datatype, just use the derived one from Monad because those should always pass the test anyway :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

AntonioMateoGomez picture AntonioMateoGomez  路  4Comments

pakoito picture pakoito  路  4Comments

jmfayard picture jmfayard  路  4Comments

raulraja picture raulraja  路  5Comments

Northburns picture Northburns  路  4Comments