-
Notifications
You must be signed in to change notification settings - Fork 454
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add FunctorFilter #224
Add FunctorFilter #224
Conversation
f154b6c
to
ec6cfed
Compare
Codecov Report
@@ Coverage Diff @@
## master #224 +/- ##
=========================================
Coverage ? 49.84%
Complexity ? 207
=========================================
Files ? 103
Lines ? 2329
Branches ? 279
=========================================
Hits ? 1161
Misses ? 1056
Partials ? 112
Continue to review full report at Codecov.
|
@@ -55,3 +55,8 @@ fun genIntPredicate(): Gen<(Int) -> Boolean> = | |||
{ it % absNum == absNum - 1 }) | |||
) | |||
} | |||
|
|||
fun <B> genOption(genB: Gen<B>): Gen<Option<B>> = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method could be implemented using genApplicative(). In any case, to properly test Option we'd need to generate at all possible cases. What I'd suggest is to randomise None to a low percent by capturing a Gen.int() that'll run on every generate and and checking modulus 20 for 5% chance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 fixed. I made it randomized to generate None and Some on a 5% - 95% basis.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to capture genIntSmall() as a field inside the object and call generate() on it inside generate(), else you won't get adequate distributions as it restarts the seed every time.
@@ -4,7 +4,7 @@ fun <I, O> ((I) -> O).k(): Function1<I, O> = Function1(this) | |||
|
|||
operator fun <I, O> Function1Kind<I, O>.invoke(i: I): O = this.ev().f(i) | |||
|
|||
@higherkind class Function1<I, out O>(val f: (I) -> O) : Function1Kind<I, O> { | |||
@higherkind open class Function1<I, out O>(val f: (I) -> O) : Function1Kind<I, O> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Who inherits from Function1?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nobody. It was being inherited by Lifted
in the beginning, but no need for it anymore.
@@ -45,6 +45,13 @@ package kategory | |||
inline fun <reified F> monoidK(MF: Monad<F> = kategory.monad<F>()): MonoidK<OptionTKindPartial<F>> = object : OptionTMonoidK<F> { | |||
override fun F(): Monad<F> = MF | |||
} | |||
|
|||
inline fun <reified F> functorFilter(MF: Monad<F> = kategory.monad<F>(), FF: Functor<F> = kategory.functor()): FunctorFilter<OptionTKindPartial<F>> = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Monad implies functor. You only need 1 parameter.
|
||
fun FF(): Functor<F> | ||
|
||
fun MF(): Monad<F> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Monad implies functor. One should be enough.
|
||
object FunctorFilterLaws { | ||
|
||
inline fun <reified F> laws(AP: Applicative<F> = applicative(), FFF: FunctorFilter<F> = functorFilter(), EQ: Eq<HK<F, Int>>): List<Law> = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some FunctorFilters may not be Applicatives. I'd suggest passing a constructor function, using genConstructor() and inlining AP::pure in the current callsites instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean ? FunctorFilterLaws depend on FunctorLaws which need an Applicative to run, that's why it's being passed here, but the FunctorFilterLaws are being tested using the FunctorFilter passed as a second argument.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FunctorLaws has 2 constructor, one convenient one with Applicative in the case that you're testing anything that inherits from applicative, and another that takes a constructor function for other cases. In this case as we're testing something that doesn't inherit from applicative please use the second one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
testLaws(FunctorFilterLaws.laws( | ||
OptionT.applicative(Option), | ||
OptionT.functorFilter(), | ||
Eq.any())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe it may need an instance of Eq<OptionTPartial<Option, Int>>, as we've seen similar issues with other tests before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed 👍
Back to you @pakoito |
companion object { | ||
inline operator fun <reified F, reified G> invoke(FF: Functor<F> = functor(), FFG: FunctorFilter<G> = functorFilter()): ComposedFunctorFilter<F, G> = | ||
object : ComposedFunctorFilter<F, G> { | ||
override fun F(): Functor<F> = FF |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FunctorFilter implies Functor, you don't need the second parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well that's inherited from ComposedFunctor
, following cats implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean you can assign FFG to this field too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Derp, I see.
OptionT.functorFilter(), | ||
object : Eq<HK<OptionTKindPartial<OptionHK>, Int>>{ | ||
override fun eqv(a: HK<OptionTKindPartial<OptionHK>, Int>, b: HK<OptionTKindPartial<OptionHK>, Int>): Boolean = | ||
a.ev().value == b.ev().value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we sure we can compare Option with ==, or do we need an Eq instance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are doing the same for other laws being tested on this class. I've changed it now to use the already created OptionTFIdEq
instance (on top part of this test class).
Back to you again @pakoito |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Boom! Closer to MTL now!
Fixes: #223
FunctorFilter
FunctorFilterLaws
ComposedFunctorFilter
andOptionTFunctor
FunctorFilter
instances.OptionTFunctor
.