-
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
Kleisli and EitherT extra instances and tests + some fixes #127
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -78,3 +78,51 @@ data class EitherT<F, A, B>(val MF: Monad<F>, val value: HK<F, Either<A, B>>) : | |
return GA.map(fa, { EitherT(MF, MF.map(it.lower(), { it.ev() })) }) | ||
} | ||
} | ||
|
||
class EitherTInstances<F, L>(val MF : Monad<F>) : EitherTMonadError<F, L> { | ||
override fun MF(): Monad<F> = MF | ||
} | ||
|
||
interface EitherTMonad<F, L> : Monad<EitherTF<F, L>> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd rather we kept all instances together, yet still on a separate file. Open for discussing pros/cons. |
||
|
||
fun MF() : Monad<F> | ||
|
||
override fun <A> pure(a: A): EitherT<F, L, A> = | ||
EitherT(MF(), MF().pure(Either.Right(a))) | ||
|
||
override fun <A, B> map(fa: EitherTKind<F, L, A>, f: (A) -> B): EitherT<F, L, B> = | ||
fa.ev().map { f(it) } | ||
|
||
override fun <A, B> flatMap(fa: EitherTKind<F, L, A>, f: (A) -> EitherTKind<F, L, B>): EitherT<F, L, B> = | ||
fa.ev().flatMap { f(it).ev() } | ||
|
||
override fun <A, B> tailRecM(a: A, f: (A) -> HK<EitherTF<F, L>, Either<A, B>>): EitherT<F, L, B> = | ||
EitherT(MF(), MF().tailRecM(a, { | ||
MF().map(f(it).ev().value) { recursionControl -> | ||
when (recursionControl) { | ||
is Either.Left<L> -> Either.Right(Either.Left(recursionControl.a)) | ||
is Either.Right<Either<A, B>> -> | ||
when (recursionControl.b) { | ||
is Either.Left<A> -> Either.Left(recursionControl.b.a) | ||
is Either.Right<B> -> Either.Right(Either.Right(recursionControl.b.b)) | ||
} | ||
} | ||
} | ||
})) | ||
|
||
} | ||
|
||
interface EitherTMonadError<F, E> : EitherTMonad<F, E>, MonadError<EitherTF<F, E>, E> { | ||
|
||
override fun <A> handleErrorWith(fa: EitherTKind<F, E, A>, f: (E) -> EitherTKind<F, E, A>): EitherT<F, E, A> = | ||
EitherT(MF(), MF().flatMap(fa.ev().value, { | ||
when (it) { | ||
is Either.Left -> f(it.a).ev().value | ||
is Either.Right -> MF().pure(it) | ||
} | ||
})) | ||
|
||
override fun <A> raiseError(e: E): EitherT<F, E, A> = | ||
EitherT(MF(), MF().pure(Either.Left(e))) | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,9 +2,8 @@ package kategory | |
|
||
typealias KleisliTKind<F, A, B> = HK3<Kleisli.F, F, A, B> | ||
typealias KleisliF<F> = HK<Kleisli.F, F> | ||
|
||
typealias KleisliFD<F, D> = HK2<Kleisli.F, F, D> | ||
typealias KleisliFun<F, D, A> = (D) -> HK<F, A> | ||
|
||
typealias ReaderT<F, D, A> = Kleisli<F, D, A> | ||
|
||
fun <F, D, A> KleisliTKind<F, D, A>.ev(): Kleisli<F, D, A> = | ||
|
@@ -54,3 +53,54 @@ class Kleisli<F, D, A>(val MF: Monad<F>, val run: KleisliFun<F, D, A>) : Kleisli | |
|
||
inline fun <reified F, D, A> Kleisli<F, D, Kleisli<F, D, A>>.flatten(): Kleisli<F, D, A> = | ||
flatMap({ it }) | ||
|
||
class KleisliInstances<F, D, E>(val FME: MonadError<F, E>) : KleisliMonadReader<F, D>, KleisliMonadError<F, D, E> { | ||
override fun FM(): Monad<F> = FME | ||
|
||
override fun FME(): MonadError<F, E> = FME | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above. |
||
} | ||
|
||
interface KleisliMonadReader<F, D> : MonadReader<KleisliFD<F, D>, D>, KleisliMonad<F, D> { | ||
|
||
override fun FM(): Monad<F> | ||
|
||
override fun ask(): Kleisli<F, D, D> = | ||
Kleisli(FM(), { FM().pure(it) }) | ||
|
||
override fun <A> local(f: (D) -> D, fa: HK<KleisliFD<F, D>, A>): Kleisli<F, D, A> = | ||
fa.ev().local(f) | ||
} | ||
|
||
interface KleisliMonad<F, D> : Monad<KleisliFD<F, D>> { | ||
|
||
fun FM(): Monad<F> | ||
|
||
override fun <A, B> flatMap(fa: HK<KleisliFD<F, D>, A>, f: (A) -> HK<KleisliFD<F, D>, B>): Kleisli<F, D, B> = | ||
fa.ev().flatMap(f.andThen { it.ev() }) | ||
|
||
override fun <A, B> map(fa: HK<KleisliFD<F, D>, A>, f: (A) -> B): Kleisli<F, D, B> = | ||
fa.ev().map(f) | ||
|
||
override fun <A, B> product(fa: HK<KleisliFD<F, D>, A>, fb: HK<KleisliFD<F, D>, B>): Kleisli<F, D, Tuple2<A, B>> = | ||
Kleisli(FM(), { FM().product(fa.ev().run(it), fb.ev().run(it)) }) | ||
|
||
override fun <A, B> tailRecM(a: A, f: (A) -> HK<KleisliFD<F, D>, Either<A, B>>): Kleisli<F, D, B> = | ||
Kleisli(FM(), { b -> FM().tailRecM(a, { f(it).ev().run(b) }) }) | ||
|
||
override fun <A> pure(a: A): Kleisli<F, D, A> = | ||
Kleisli(FM(), { FM().pure(a) }) | ||
} | ||
|
||
interface KleisliMonadError<F, D, E> : MonadError<KleisliFD<F, D>, E>, KleisliMonad<F, D> { | ||
|
||
fun FME(): MonadError<F, E> | ||
|
||
override fun <A> handleErrorWith(fa: HK<KleisliFD<F, D>, A>, f: (E) -> HK<KleisliFD<F, D>, A>): Kleisli<F, D, A> = | ||
Kleisli(FME(), { | ||
FME().handleErrorWith(fa.ev().run(it), { e: E -> f(e).ev().run(it) }) | ||
}) | ||
|
||
override fun <A> raiseError(e: E): Kleisli<F, D, A> = | ||
Kleisli(FME(), { FME().raiseError(e) }) | ||
|
||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,16 @@ interface ApplicativeError<F, E> : Applicative<F>, Typeclass { | |
handleErrorWith(map(fa) { Either.Right(it) }) { | ||
pure(Either.Left(it)) | ||
} | ||
|
||
fun <A> fromEither(fab: Either<E, A>): HK<F, A> = | ||
fab.fold({ raiseError<A>(it) }, { pure(it) }) | ||
|
||
fun <A> catch(f: () -> A, recover: (Throwable) -> E): HK<F, A> = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this function be inlined? |
||
try { | ||
pure(f()) | ||
} catch (t: Throwable) { | ||
raiseError<A>(recover(t)) | ||
} | ||
} | ||
|
||
fun <F, A> ApplicativeError<F, Throwable>.catch(f: () -> A): HK<F, A> = | ||
|
@@ -24,4 +34,4 @@ fun <F, A> ApplicativeError<F, Throwable>.catch(f: () -> A): HK<F, A> = | |
} | ||
|
||
inline fun <reified F, reified E> applicativeError(): ApplicativeError<F, E> = | ||
instance(InstanceParametrizedType(Monad::class.java, listOf(F::class.java, E::class.java))) | ||
instance(InstanceParametrizedType(ApplicativeError::class.java, listOf(F::class.java, E::class.java))) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package kategory | ||
|
||
interface MonadReader<F, D> : Monad<F> { | ||
/** Get the environment */ | ||
fun ask(): HK<F, D> | ||
|
||
/** Modify the environment */ | ||
fun <A> local(f: (D) -> D, fa: HK<F, A>): HK<F, A> | ||
|
||
/** Retrieves a function of the environment */ | ||
fun <A> reader(f: (D) -> A): HK<F, A> = map(ask(), f) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NIT: Formatting even for short methods is to have a new line after the = |
||
} | ||
|
||
inline fun <reified F, reified D> monadReader(): MonadReader<F, D> = | ||
instance(InstanceParametrizedType(MonadReader::class.java, listOf(F::class.java, D::class.java))) |
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 class requires a inline fun invoke constructor in the companion object that defaults the MF monad.