diff --git a/modules/core/arrow-annotations-processor/src/main/java/arrow/common/utils/AbstractProcessor.kt b/modules/core/arrow-annotations-processor/src/main/java/arrow/common/utils/AbstractProcessor.kt index 12ee9f55c16..46f7fa014e1 100644 --- a/modules/core/arrow-annotations-processor/src/main/java/arrow/common/utils/AbstractProcessor.kt +++ b/modules/core/arrow-annotations-processor/src/main/java/arrow/common/utils/AbstractProcessor.kt @@ -3,6 +3,8 @@ package arrow.common.utils import arrow.common.messager.logE import arrow.documented import arrow.meta.encoder.jvm.KotlinMetatadataEncoder +import me.eugeniomarletti.kotlin.metadata.kotlinMetadata +import me.eugeniomarletti.kotlin.metadata.shadow.metadata.deserialization.receiverType import me.eugeniomarletti.kotlin.processing.KotlinAbstractProcessor import java.io.File import java.io.IOException @@ -58,8 +60,14 @@ abstract class AbstractProcessor : KotlinAbstractProcessor(), ProcessorUtils, Ko ElementKind.INTERFACE -> (this as TypeElement).qualifiedName.toString() ElementKind.METHOD -> (this as ExecutableElement).let { val name = (it.enclosingElement as TypeElement).qualifiedName.toString() + + val extensionName = (it.enclosingElement.kotlinMetadata?.asClassOrPackageDataWrapper(it.enclosingElement as TypeElement) as? ClassOrPackageDataWrapper.Class)?.let { classData -> + val n = classData.getFunction(it).toMeta(classData, it).receiverType?.simpleName + if (n == classData.simpleName) "" else "$n-" + } ?: "" + val functionName = it.simpleName.toString() - "$name.$functionName" + "$name.$extensionName$functionName" } else -> knownError("Unsupported @documented $kind") } diff --git a/modules/core/arrow-annotations-processor/src/main/java/arrow/instances/PolyTemplateGenerator.kt b/modules/core/arrow-annotations-processor/src/main/java/arrow/instances/PolyTemplateGenerator.kt index 320583c10f2..f6583ddec81 100644 --- a/modules/core/arrow-annotations-processor/src/main/java/arrow/instances/PolyTemplateGenerator.kt +++ b/modules/core/arrow-annotations-processor/src/main/java/arrow/instances/PolyTemplateGenerator.kt @@ -110,7 +110,7 @@ interface PolyTemplateGenerator : MetaApi { private fun String.replaceMonadDeferImports(info: TypeClassInstance): String { val monadDeferPackageName = PackageName(info.instance.packageName.value + "." + info.projectedCompanion.simpleName.substringAfterLast(".").toLowerCase() + - ".monaddefer") + ".monadDefer") return replace( "_imports_monaddefer_", """ @@ -133,8 +133,7 @@ interface PolyTemplateGenerator : MetaApi { |import ${packageName.value.quote()}.* |import arrow.core.* |$factoryImports - |$additionalImports - |""".trimMargin() + |$additionalImports""".trimMargin() ) } @@ -152,8 +151,7 @@ interface PolyTemplateGenerator : MetaApi { val factory = it.returnType?.simpleName?.decapitalize()?.substringBefore("<") ?: "" val fact = if (factory == "functor") "monad" else factory """|import arrow.instances.id.$fact.$fact - |import arrow.core.* - """.trimMargin() + |import arrow.core.*""".trimMargin() } } diff --git a/modules/core/arrow-core/src/test/kotlin/arrow/core/IdTest.kt b/modules/core/arrow-core/src/test/kotlin/arrow/core/IdTest.kt index 79190ebe501..141972332b1 100644 --- a/modules/core/arrow-core/src/test/kotlin/arrow/core/IdTest.kt +++ b/modules/core/arrow-core/src/test/kotlin/arrow/core/IdTest.kt @@ -1,6 +1,5 @@ package arrow.core -import arrow.core.Id import arrow.instances.eq import arrow.instances.hash import arrow.instances.id.applicative.applicative @@ -8,6 +7,7 @@ import arrow.instances.id.comonad.comonad import arrow.instances.id.eq.eq import arrow.instances.id.hash.hash import arrow.instances.id.monad.monad +import arrow.instances.id.monoid.monoid import arrow.instances.id.show.show import arrow.instances.id.traverse.traverse import arrow.instances.id.semigroup.semigroup diff --git a/modules/core/arrow-data/src/main/kotlin/arrow/data/StateT.kt b/modules/core/arrow-data/src/main/kotlin/arrow/data/StateT.kt index 8492a017645..06fe0bf10ff 100644 --- a/modules/core/arrow-data/src/main/kotlin/arrow/data/StateT.kt +++ b/modules/core/arrow-data/src/main/kotlin/arrow/data/StateT.kt @@ -201,7 +201,7 @@ class StateT( * @param ff function with the [StateT] context. */ fun ap(MF: Monad, ff: StateTOf B>): StateT = - ff.fix().map2(MF, this) { f, a -> f(a) } + ff.fix().map2(MF, this) { f: (A) -> B, a: A -> f(a) } /** * Create a product of the value types of [StateT]. diff --git a/modules/core/arrow-data/src/test/kotlin/arrow/data/KleisliTest.kt b/modules/core/arrow-data/src/test/kotlin/arrow/data/KleisliTest.kt index 2208bfbbcb9..e85dcd40a5a 100644 --- a/modules/core/arrow-data/src/test/kotlin/arrow/data/KleisliTest.kt +++ b/modules/core/arrow-data/src/test/kotlin/arrow/data/KleisliTest.kt @@ -46,7 +46,7 @@ class KleisliTest : UnitSpec() { testLaws( BracketLaws.laws( - BF = Kleisli.bracket(IO.bracket()), + Kleisli.bracket(IO.bracket()), EQ = IOEQ(), EQ_EITHER = IOEitherEQ(), EQERR = IOEQ() diff --git a/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/const.kt b/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/const.kt index d08f19341be..d30342c3a4d 100644 --- a/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/const.kt +++ b/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/const.kt @@ -54,7 +54,7 @@ interface ConstFoldableInstance : Foldable> { @extension interface ConstTraverseInstance : Traverse>, ConstFoldableInstance { - override fun Kind, T>.map(f: (T) -> U): Const = fix().retag() + override fun ConstOf.map(f: (T) -> U): Const = fix().retag() override fun ConstOf.traverse(AP: Applicative, f: (A) -> Kind): Kind> = fix().traverse(AP, f) @@ -108,7 +108,7 @@ class ConstContext(val MA: Monoid) : ConstApplicativeInstance, ConstTra override fun MA(): Monoid = MA override fun ConstOf.map(f: (T) -> U): Const = - fix().map(f) + fix().retag() } class ConstContextPartiallyApplied(val MA: Monoid) { diff --git a/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/either.kt b/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/either.kt index 7fcde53c149..9c37d781b33 100644 --- a/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/either.kt +++ b/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/either.kt @@ -1,7 +1,6 @@ package arrow.instances import arrow.Kind -import arrow.Kind2 import arrow.core.* import arrow.deprecation.ExtensionsDSLDeprecated import arrow.extension @@ -48,12 +47,12 @@ interface EitherMonoidInstance : Monoid>, EitherSemigroupInst @extension interface EitherFunctorInstance : Functor> { - override fun Kind, A>.map(f: (A) -> B): Either = fix().map(f) + override fun EitherOf.map(f: (A) -> B): Either = fix().map(f) } @extension interface EitherBifunctorInstance : Bifunctor { - override fun Kind2.bimap(fl: (A) -> C, fr: (B) -> D): Kind2 = + override fun EitherOf.bimap(fl: (A) -> C, fr: (B) -> D): Either = fix().bimap(fl, fr) } @@ -62,24 +61,24 @@ interface EitherApplicativeInstance : Applicative>, Either override fun just(a: A): Either = Right(a) - override fun Kind, A>.map(f: (A) -> B): Either = fix().map(f) + override fun EitherOf.map(f: (A) -> B): Either = fix().map(f) - override fun Kind, A>.ap(ff: Kind, (A) -> B>): Either = + override fun EitherOf.ap(ff: EitherOf B>): Either = fix().eitherAp(ff) } @extension interface EitherMonadInstance : Monad>, EitherApplicativeInstance { - override fun Kind, A>.map(f: (A) -> B): Either = fix().map(f) + override fun EitherOf.map(f: (A) -> B): Either = fix().map(f) - override fun Kind, A>.ap(ff: Kind, (A) -> B>): Either = + override fun EitherOf.ap(ff: EitherOf B>): Either = fix().eitherAp(ff) - override fun Kind, A>.flatMap(f: (A) -> Kind, B>): Either = + override fun EitherOf.flatMap(f: (A) -> EitherOf): Either = fix().eitherFlatMap { f(it).fix() } - override fun tailRecM(a: A, f: (A) -> Kind, Either>): Either = + override fun tailRecM(a: A, f: (A) -> EitherOf>): Either = Either.tailRecM(a, f) } @@ -88,7 +87,7 @@ interface EitherApplicativeErrorInstance : ApplicativeError raiseError(e: L): Either = Left(e) - override fun Kind, A>.handleErrorWith(f: (L) -> Kind, A>): Either { + override fun EitherOf.handleErrorWith(f: (L) -> EitherOf): Either { val fea = fix() return when (fea) { is Either.Left -> f(fea.a).fix() @@ -103,10 +102,10 @@ interface EitherMonadErrorInstance : MonadError, L>, Eithe @extension interface EitherFoldableInstance : Foldable> { - override fun Kind, A>.foldLeft(b: B, f: (B, A) -> B): B = + override fun EitherOf.foldLeft(b: B, f: (B, A) -> B): B = fix().foldLeft(b, f) - override fun Kind, A>.foldRight(lb: Eval, f: (A, Eval) -> Eval): Eval = + override fun EitherOf.foldRight(lb: Eval, f: (A, Eval) -> Eval): Eval = fix().foldRight(lb, f) } @@ -116,14 +115,14 @@ fun EitherOf.traverse(GA: Applicative, f: (B) -> Kind : Traverse>, EitherFoldableInstance { - override fun Kind, A>.traverse(AP: Applicative, f: (A) -> Kind): Kind, B>> = + override fun EitherOf.traverse(AP: Applicative, f: (A) -> Kind): Kind> = fix().eitherTraverse(AP, f) } @extension interface EitherSemigroupKInstance : SemigroupK> { - override fun Kind, A>.combineK(y: Kind, A>): Either = + override fun EitherOf.combineK(y: EitherOf): Either = fix().eitherCombineK(y) } @@ -171,7 +170,7 @@ interface EitherHashInstance : Hash>, EitherEqInstance } class EitherContext : EitherMonadErrorInstance, EitherTraverseInstance, EitherSemigroupKInstance { - override fun Kind, A>.map(f: (A) -> B): Either = + override fun EitherOf.map(f: (A) -> B): Either = fix().map(f) } diff --git a/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/eval.kt b/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/eval.kt index 7b52bef6c0f..bfed4f8665e 100644 --- a/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/eval.kt +++ b/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/eval.kt @@ -1,6 +1,5 @@ package arrow.instances -import arrow.Kind import arrow.core.* import arrow.deprecation.ExtensionsDSLDeprecated import arrow.extension @@ -8,16 +7,16 @@ import arrow.typeclasses.* @extension interface EvalFunctorInstance : Functor { - override fun Kind.map(f: (A) -> B): Eval = + override fun EvalOf.map(f: (A) -> B): Eval = fix().map(f) } @extension interface EvalApplicativeInstance : Applicative { - override fun Kind.ap(ff: Kind B>): Eval = + override fun EvalOf.ap(ff: EvalOf<(A) -> B>): Eval = fix().ap(ff) - override fun Kind.map(f: (A) -> B): Eval = + override fun EvalOf.map(f: (A) -> B): Eval = fix().map(f) override fun just(a: A): Eval = @@ -26,16 +25,16 @@ interface EvalApplicativeInstance : Applicative { @extension interface EvalMonadInstance : Monad { - override fun Kind.ap(ff: Kind B>): Eval = + override fun EvalOf.ap(ff: EvalOf<(A) -> B>): Eval = fix().ap(ff) - override fun Kind.flatMap(f: (A) -> Kind): Eval = + override fun EvalOf.flatMap(f: (A) -> EvalOf): Eval = fix().flatMap(f) override fun tailRecM(a: A, f: kotlin.Function1>>): Eval = Eval.tailRecM(a, f) - override fun Kind.map(f: (A) -> B): Eval = + override fun EvalOf.map(f: (A) -> B): Eval = fix().map(f) override fun just(a: A): Eval = @@ -44,37 +43,37 @@ interface EvalMonadInstance : Monad { @extension interface EvalComonadInstance : Comonad { - override fun Kind.coflatMap(f: (Kind) -> B): Eval = + override fun EvalOf.coflatMap(f: (EvalOf) -> B): Eval = fix().coflatMap(f) - override fun Kind.extract(): A = + override fun EvalOf.extract(): A = fix().extract() - override fun Kind.map(f: (A) -> B): Eval = + override fun EvalOf.map(f: (A) -> B): Eval = fix().map(f) } @extension interface EvalBimonadInstance : Bimonad { - override fun Kind.ap(ff: Kind B>): Eval = + override fun EvalOf.ap(ff: EvalOf<(A) -> B>): Eval = fix().ap(ff) - override fun Kind.flatMap(f: (A) -> Kind): Eval = + override fun EvalOf.flatMap(f: (A) -> EvalOf): Eval = fix().flatMap(f) override fun tailRecM(a: A, f: kotlin.Function1>>): Eval = Eval.tailRecM(a, f) - override fun Kind.map(f: (A) -> B): Eval = + override fun EvalOf.map(f: (A) -> B): Eval = fix().map(f) override fun just(a: A): Eval = Eval.just(a) - override fun Kind.coflatMap(f: (Kind) -> B): Eval = + override fun EvalOf.coflatMap(f: (EvalOf) -> B): Eval = fix().coflatMap(f) - override fun Kind.extract(): A = + override fun EvalOf.extract(): A = fix().extract() } diff --git a/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/function0.kt b/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/function0.kt index 2f59eefaa87..785dbc87e46 100644 --- a/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/function0.kt +++ b/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/function0.kt @@ -1,6 +1,5 @@ package arrow.instances -import arrow.Kind import arrow.core.* import arrow.deprecation.ExtensionsDSLDeprecated import arrow.extension @@ -10,7 +9,8 @@ import arrow.typeclasses.* interface Function0SemigroupInstance : Semigroup> { fun SA(): Semigroup - override fun Function0.combine(b: Function0): Function0 = { SA().run { invoke().combine(b.invoke()) } }.k() + override fun Function0.combine(b: Function0): Function0 = + { SA().run { invoke().combine(b.invoke()) } }.k() } @extension @@ -19,21 +19,22 @@ interface Function0MonoidInstance : Monoid>, Function0SemigroupI override fun SA() = MA() - override fun empty(): Function0 = { MA().run { empty() } }.k() + override fun empty(): Function0 = + { MA().run { empty() } }.k() } @extension interface Function0FunctorInstance : Functor { - override fun Kind.map(f: (A) -> B): Function0 = + override fun Function0Of.map(f: (A) -> B): Function0 = fix().map(f) } @extension interface Function0ApplicativeInstance : Applicative { - override fun Kind.ap(ff: Kind B>): Function0 = + override fun Function0Of.ap(ff: Function0Of<(A) -> B>): Function0 = fix().ap(ff) - override fun Kind.map(f: (A) -> B): Function0 = + override fun Function0Of.map(f: (A) -> B): Function0 = fix().map(f) override fun just(a: A): Function0 = @@ -42,16 +43,16 @@ interface Function0ApplicativeInstance : Applicative { @extension interface Function0MonadInstance : Monad { - override fun Kind.ap(ff: Kind B>): Function0 = + override fun Function0Of.ap(ff: Function0Of<(A) -> B>): Function0 = fix().ap(ff) - override fun Kind.flatMap(f: (A) -> Kind): Function0 = + override fun Function0Of.flatMap(f: (A) -> Function0Of): Function0 = fix().flatMap(f) override fun tailRecM(a: A, f: kotlin.Function1>>): Function0 = Function0.tailRecM(a, f) - override fun Kind.map(f: (A) -> B): Function0 = + override fun Function0Of.map(f: (A) -> B): Function0 = fix().map(f) override fun just(a: A): Function0 = @@ -60,37 +61,37 @@ interface Function0MonadInstance : Monad { @extension interface Function0ComonadInstance : Comonad { - override fun Kind.coflatMap(f: (Kind) -> B): Function0 = + override fun Function0Of.coflatMap(f: (Function0Of) -> B): Function0 = fix().coflatMap(f) - override fun Kind.extract(): A = + override fun Function0Of.extract(): A = fix().extract() - override fun Kind.map(f: (A) -> B): Function0 = + override fun Function0Of.map(f: (A) -> B): Function0 = fix().map(f) } @extension interface Function0BimonadInstance : Bimonad { - override fun Kind.ap(ff: Kind B>): Function0 = + override fun Function0Of.ap(ff: Function0Of<(A) -> B>): Function0 = fix().ap(ff) - override fun Kind.flatMap(f: (A) -> Kind): Function0 = + override fun Function0Of.flatMap(f: (A) -> Function0Of): Function0 = fix().flatMap(f) override fun tailRecM(a: A, f: kotlin.Function1>>): Function0 = Function0.tailRecM(a, f) - override fun Kind.map(f: (A) -> B): Function0 = + override fun Function0Of.map(f: (A) -> B): Function0 = fix().map(f) override fun just(a: A): Function0 = Function0.just(a) - override fun Kind.coflatMap(f: (Kind) -> B): Function0 = + override fun Function0Of.coflatMap(f: (Function0Of) -> B): Function0 = fix().coflatMap(f) - override fun Kind.extract(): A = + override fun Function0Of.extract(): A = fix().extract() } diff --git a/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/function1.kt b/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/function1.kt index 0e9045d32ec..e89347fb8d5 100644 --- a/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/function1.kt +++ b/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/function1.kt @@ -1,7 +1,6 @@ package arrow.instances import arrow.Kind -import arrow.Kind2 import arrow.core.* import arrow.deprecation.ExtensionsDSLDeprecated import arrow.extension @@ -25,7 +24,7 @@ interface Function1MonoidInstance : Monoid>, Function1Semi @extension interface Function1FunctorInstance : Functor> { - override fun Kind, A>.map(f: (A) -> B): Function1 = + override fun Function1Of.map(f: (A) -> B): Function1 = fix().map(f) } @@ -40,7 +39,7 @@ interface Function1ContravariantInstance : Contravariant { - override fun Kind, B>.dimap(fl: (C) -> A, fr: (B) -> D): Function1 = + override fun Function1Of.dimap(fl: (C) -> A, fr: (B) -> D): Function1 = (fr compose fix().f compose fl).k() } @@ -50,23 +49,23 @@ interface Function1ApplicativeInstance : Applicative>, override fun just(a: A): Function1 = Function1.just(a) - override fun Kind, A>.map(f: (A) -> B): Function1 = + override fun Function1Of.map(f: (A) -> B): Function1 = fix().map(f) - override fun Kind, A>.ap(ff: Kind, (A) -> B>): Function1 = + override fun Function1Of.ap(ff: Function1Of B>): Function1 = fix().ap(ff) } @extension interface Function1MonadInstance : Monad>, Function1ApplicativeInstance { - override fun Kind, A>.map(f: (A) -> B): Function1 = + override fun Function1Of.map(f: (A) -> B): Function1 = fix().map(f) - override fun Kind, A>.ap(ff: Kind, (A) -> B>): Function1 = + override fun Function1Of.ap(ff: Function1Of B>): Function1 = fix().ap(ff) - override fun Kind, A>.flatMap(f: (A) -> Kind, B>): Function1 = + override fun Function1Of.flatMap(f: (A) -> Function1Of): Function1 = fix().flatMap(f) override fun tailRecM(a: A, f: (A) -> Function1Of>): Function1 = @@ -75,9 +74,9 @@ interface Function1MonadInstance : Monad>, Function1App @extension interface Function1CategoryInstance : Category { - override fun id(): Kind2 = Function1.id() + override fun id(): Function1 = Function1.id() - override fun Kind2.compose(arr: Kind2): Kind2 = fix().compose(arr.fix()) + override fun Function1Of.compose(arr: Function1Of): Function1Of = fix().compose(arr.fix()) } class Function1Context : Function1MonadInstance diff --git a/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/id.kt b/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/id.kt index 9858576faa1..00aca19509e 100644 --- a/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/id.kt +++ b/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/id.kt @@ -19,7 +19,7 @@ interface IdMonoidInstance : Monoid>, IdSemigroupInstance { fun MA(): Monoid override fun SA(): Semigroup = MA() - override fun empty(): Id = Id(MA().run { empty() }) + override fun empty(): Id = Id(MA().empty()) } @extension @@ -39,16 +39,16 @@ interface IdShowInstance : Show> { @extension interface IdFunctorInstance : Functor { - override fun Kind.map(f: (A) -> B): Id = + override fun IdOf.map(f: (A) -> B): Id = fix().map(f) } @extension interface IdApplicativeInstance : Applicative { - override fun Kind.ap(ff: Kind B>): Id = + override fun IdOf.ap(ff: IdOf<(A) -> B>): Id = fix().ap(ff) - override fun Kind.map(f: (A) -> B): Id = + override fun IdOf.map(f: (A) -> B): Id = fix().map(f) override fun just(a: A): Id = @@ -57,16 +57,16 @@ interface IdApplicativeInstance : Applicative { @extension interface IdMonadInstance : Monad { - override fun Kind.ap(ff: Kind B>): Id = + override fun IdOf.ap(ff: IdOf<(A) -> B>): Id = fix().ap(ff) - override fun Kind.flatMap(f: (A) -> Kind): Id = + override fun IdOf.flatMap(f: (A) -> IdOf): Id = fix().flatMap(f) override fun tailRecM(a: A, f: kotlin.Function1>>): Id = Id.tailRecM(a, f) - override fun Kind.map(f: (A) -> B): Id = + override fun IdOf.map(f: (A) -> B): Id = fix().map(f) override fun just(a: A): Id = @@ -75,46 +75,46 @@ interface IdMonadInstance : Monad { @extension interface IdComonadInstance : Comonad { - override fun Kind.coflatMap(f: (Kind) -> B): Id = + override fun IdOf.coflatMap(f: (IdOf) -> B): Id = fix().coflatMap(f) - override fun Kind.extract(): A = + override fun IdOf.extract(): A = fix().extract() - override fun Kind.map(f: (A) -> B): Id = + override fun IdOf.map(f: (A) -> B): Id = fix().map(f) } @extension interface IdBimonadInstance : Bimonad { - override fun Kind.ap(ff: Kind B>): Id = + override fun IdOf.ap(ff: IdOf<(A) -> B>): Id = fix().ap(ff) - override fun Kind.flatMap(f: (A) -> Kind): Id = + override fun IdOf.flatMap(f: (A) -> IdOf): Id = fix().flatMap(f) override fun tailRecM(a: A, f: kotlin.Function1>>): Id = Id.tailRecM(a, f) - override fun Kind.map(f: (A) -> B): Id = + override fun IdOf.map(f: (A) -> B): Id = fix().map(f) override fun just(a: A): Id = Id.just(a) - override fun Kind.coflatMap(f: (Kind) -> B): Id = + override fun IdOf.coflatMap(f: (IdOf) -> B): Id = fix().coflatMap(f) - override fun Kind.extract(): A = + override fun IdOf.extract(): A = fix().extract() } @extension interface IdFoldableInstance : Foldable { - override fun Kind.foldLeft(b: B, f: (B, A) -> B): B = + override fun IdOf.foldLeft(b: B, f: (B, A) -> B): B = fix().foldLeft(b, f) - override fun Kind.foldRight(lb: Eval, f: (A, Eval) -> Eval): Eval = + override fun IdOf.foldRight(lb: Eval, f: (A, Eval) -> Eval): Eval = fix().foldRight(lb, f) } @@ -127,16 +127,16 @@ fun IdOf>.sequence(GA: Applicative): Kind> = @extension interface IdTraverseInstance : Traverse { - override fun Kind.map(f: (A) -> B): Id = + override fun IdOf.map(f: (A) -> B): Id = fix().map(f) - override fun Kind.traverse(AP: Applicative, f: (A) -> Kind): Kind> = + override fun IdOf.traverse(AP: Applicative, f: (A) -> Kind): Kind> = idTraverse(AP, f) - override fun Kind.foldLeft(b: B, f: (B, A) -> B): B = + override fun IdOf.foldLeft(b: B, f: (B, A) -> B): B = fix().foldLeft(b, f) - override fun arrow.Kind.foldRight(lb: arrow.core.Eval, f: (A, arrow.core.Eval) -> arrow.core.Eval): Eval = + override fun IdOf.foldRight(lb: Eval, f: (A, Eval) -> Eval): Eval = fix().foldRight(lb, f) } @@ -151,7 +151,7 @@ interface IdHashInstance : Hash>, IdEqInstance { } object IdContext : IdBimonadInstance, IdTraverseInstance { - override fun Kind.map(f: (A) -> B): Id = + override fun IdOf.map(f: (A) -> B): Id = fix().map(f) } diff --git a/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/monoid.kt b/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/monoid.kt index 078af948476..4f965e53f67 100644 --- a/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/monoid.kt +++ b/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/monoid.kt @@ -1,12 +1,11 @@ package arrow.instances -import arrow.Kind import arrow.extension import arrow.typeclasses.* @extension interface MonoidInvariantInstance : Invariant { - override fun Kind.imap(f: (A) -> B, g: (B) -> A): Monoid = + override fun MonoidOf.imap(f: (A) -> B, g: (B) -> A): Monoid = object : Monoid { override fun empty(): B = f(this@imap.fix().empty()) diff --git a/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/option.kt b/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/option.kt index 684e22baeee..12b96f58d16 100644 --- a/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/option.kt +++ b/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/option.kt @@ -33,16 +33,16 @@ interface OptionApplicativeErrorInstance : ApplicativeError, Op override fun raiseError(e: Unit): Option = None - override fun Kind.handleErrorWith(f: (Unit) -> Kind): Option = + override fun OptionOf.handleErrorWith(f: (Unit) -> OptionOf): Option = fix().orElse { f(Unit).fix() } } @extension interface OptionMonadErrorInstance : MonadError, OptionMonadInstance { - override fun raiseError(e: Unit): Kind = + override fun raiseError(e: Unit): OptionOf = None - override fun Kind.handleErrorWith(f: (Unit) -> Kind): Option = + override fun OptionOf.handleErrorWith(f: (Unit) -> OptionOf): Option = fix().orElse { f(Unit).fix() } } @@ -72,16 +72,16 @@ interface OptionShowInstance : Show> { @extension interface OptionFunctorInstance : Functor { - override fun Kind.map(f: (A) -> B): Option = + override fun OptionOf.map(f: (A) -> B): Option = fix().map(f) } @extension interface OptionApplicativeInstance : Applicative { - override fun Kind.ap(ff: Kind B>): Option = + override fun OptionOf.ap(ff: OptionOf<(A) -> B>): Option = fix().ap(ff) - override fun Kind.map(f: (A) -> B): Option = + override fun OptionOf.map(f: (A) -> B): Option = fix().map(f) override fun just(a: A): Option = @@ -90,16 +90,16 @@ interface OptionApplicativeInstance : Applicative { @extension interface OptionMonadInstance : Monad { - override fun Kind.ap(ff: Kind B>): Option = + override fun OptionOf.ap(ff: OptionOf<(A) -> B>): Option = fix().ap(ff) - override fun Kind.flatMap(f: (A) -> Kind): Option = + override fun OptionOf.flatMap(f: (A) -> OptionOf): Option = fix().flatMap(f) override fun tailRecM(a: A, f: (A) -> OptionOf>): Option = Option.tailRecM(a, f) - override fun Kind.map(f: (A) -> B): Option = + override fun OptionOf.map(f: (A) -> B): Option = fix().map(f) override fun just(a: A): Option = @@ -108,28 +108,28 @@ interface OptionMonadInstance : Monad { @extension interface OptionFoldableInstance : Foldable { - override fun Kind.exists(p: (A) -> Boolean): Boolean = + override fun OptionOf.exists(p: (A) -> Boolean): Boolean = fix().exists(p) - override fun Kind.foldLeft(b: B, f: (B, A) -> B): B = + override fun OptionOf.foldLeft(b: B, f: (B, A) -> B): B = fix().foldLeft(b, f) - override fun Kind.foldRight(lb: Eval, f: (A, Eval) -> Eval): Eval = + override fun OptionOf.foldRight(lb: Eval, f: (A, Eval) -> Eval): Eval = fix().foldRight(lb, f) override fun OptionOf.forAll(p: (A) -> Boolean): Boolean = fix().forall(p) - override fun Kind.isEmpty(): Boolean = + override fun OptionOf.isEmpty(): Boolean = fix().isEmpty() - override fun Kind.nonEmpty(): Boolean = + override fun OptionOf.nonEmpty(): Boolean = fix().nonEmpty() } @extension interface OptionSemigroupKInstance : SemigroupK { - override fun Kind.combineK(y: Kind): Option = + override fun OptionOf.combineK(y: OptionOf): Option = orElse { y.fix() } } @@ -138,7 +138,7 @@ interface OptionMonoidKInstance : MonoidK { override fun empty(): Option = Option.empty() - override fun Kind.combineK(y: Kind): Option = + override fun OptionOf.combineK(y: OptionOf): Option = orElse { y.fix() } } @@ -155,28 +155,28 @@ fun OptionOf.traverseFilter(GA: Applicative, f: (A) -> Kind { - override fun Kind.map(f: (A) -> B): Option = + override fun OptionOf.map(f: (A) -> B): Option = fix().map(f) - override fun Kind.traverse(AP: Applicative, f: (A) -> Kind): Kind> = + override fun OptionOf.traverse(AP: Applicative, f: (A) -> Kind): Kind> = optionTraverse(AP, f) - override fun Kind.exists(p: (A) -> Boolean): Boolean = + override fun OptionOf.exists(p: (A) -> Boolean): Boolean = fix().exists(p) - override fun Kind.foldLeft(b: B, f: (B, A) -> B): B = + override fun OptionOf.foldLeft(b: B, f: (B, A) -> B): B = fix().foldLeft(b, f) - override fun Kind.foldRight(lb: Eval, f: (A, Eval) -> Eval): Eval = + override fun OptionOf.foldRight(lb: Eval, f: (A, Eval) -> Eval): Eval = fix().foldRight(lb, f) - override fun Kind.forAll(p: (A) -> Boolean): Boolean = + override fun OptionOf.forAll(p: (A) -> Boolean): Boolean = fix().forall(p) - override fun Kind.isEmpty(): Boolean = + override fun OptionOf.isEmpty(): Boolean = fix().isEmpty() - override fun Kind.nonEmpty(): Boolean = + override fun OptionOf.nonEmpty(): Boolean = fix().nonEmpty() } @@ -195,7 +195,7 @@ interface OptionHashInstance : Hash>, OptionEqInstance { } object OptionContext : OptionMonadErrorInstance, OptionTraverseInstance { - override fun Kind.map(f: (A) -> B): Option = + override fun OptionOf.map(f: (A) -> B): Option = fix().map(f) } diff --git a/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/try.kt b/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/try.kt index 6e1106aa160..c59917c309e 100644 --- a/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/try.kt +++ b/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/try.kt @@ -40,7 +40,7 @@ interface TryApplicativeErrorInstance : ApplicativeError, Try override fun raiseError(e: Throwable): Try = Failure(e) - override fun Kind.handleErrorWith(f: (Throwable) -> Kind): Try = + override fun TryOf.handleErrorWith(f: (Throwable) -> TryOf): Try = fix().recoverWith { f(it).fix() } } @@ -50,7 +50,7 @@ interface TryMonadErrorInstance : MonadError, TryMonadInstanc override fun raiseError(e: Throwable): Try = Failure(e) - override fun Kind.handleErrorWith(f: (Throwable) -> Kind): Try = + override fun TryOf.handleErrorWith(f: (Throwable) -> TryOf): Try = fix().recoverWith { f(it).fix() } } @@ -85,16 +85,16 @@ interface TryShowInstance : Show> { @extension interface TryFunctorInstance : Functor { - override fun Kind.map(f: (A) -> B): Try = + override fun TryOf.map(f: (A) -> B): Try = fix().map(f) } @extension interface TryApplicativeInstance : Applicative { - override fun Kind.ap(ff: Kind B>): Try = + override fun TryOf.ap(ff: TryOf<(A) -> B>): Try = fix().ap(ff) - override fun Kind.map(f: (A) -> B): Try = + override fun TryOf.map(f: (A) -> B): Try = fix().map(f) override fun just(a: A): Try = @@ -103,16 +103,16 @@ interface TryApplicativeInstance : Applicative { @extension interface TryMonadInstance : Monad { - override fun Kind.ap(ff: Kind B>): Try = + override fun TryOf.ap(ff: TryOf<(A) -> B>): Try = fix().ap(ff) - override fun Kind.flatMap(f: (A) -> Kind): Try = + override fun TryOf.flatMap(f: (A) -> TryOf): Try = fix().flatMap(f) override fun tailRecM(a: A, f: kotlin.Function1>>): Try = Try.tailRecM(a, f) - override fun Kind.map(f: (A) -> B): Try = + override fun TryOf.map(f: (A) -> B): Try = fix().map(f) override fun just(a: A): Try = @@ -124,10 +124,10 @@ interface TryFoldableInstance : Foldable { override fun TryOf.exists(p: (A) -> Boolean): Boolean = fix().exists(p) - override fun Kind.foldLeft(b: B, f: (B, A) -> B): B = + override fun TryOf.foldLeft(b: B, f: (B, A) -> B): B = fix().foldLeft(b, f) - override fun Kind.foldRight(lb: Eval, f: (A, Eval) -> Eval): Eval = + override fun TryOf.foldRight(lb: Eval, f: (A, Eval) -> Eval): Eval = fix().foldRight(lb, f) } @@ -149,10 +149,10 @@ interface TryTraverseInstance : Traverse { override fun TryOf.exists(p: (A) -> Boolean): kotlin.Boolean = fix().exists(p) - override fun Kind.foldLeft(b: B, f: (B, A) -> B): B = + override fun TryOf.foldLeft(b: B, f: (B, A) -> B): B = fix().foldLeft(b, f) - override fun Kind.foldRight(lb: Eval, f: (A, Eval) -> Eval): Eval = + override fun TryOf.foldRight(lb: Eval, f: (A, Eval) -> Eval): Eval = fix().foldRight(lb, f) } @@ -174,7 +174,7 @@ interface TryHashInstance : Hash>, TryEqInstance { } object TryContext : TryMonadErrorInstance, TryTraverseInstance { - override fun Kind.map(f: (A) -> B): Try = + override fun TryOf.map(f: (A) -> B): Try = fix().map(f) } diff --git a/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/tuple.kt b/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/tuple.kt index 1aa60ca106b..68182a15fb2 100644 --- a/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/tuple.kt +++ b/modules/core/arrow-instances-core/src/main/kotlin/arrow/instances/tuple.kt @@ -1,7 +1,6 @@ package arrow.instances import arrow.Kind -import arrow.Kind2 import arrow.core.* import arrow.core.Either.Left import arrow.core.Either.Right @@ -14,7 +13,7 @@ import arrow.instances.traverse as tuple2Traverse @extension interface Tuple2FunctorInstance : Functor> { - override fun Kind, A>.map(f: (A) -> B) = + override fun Tuple2Of.map(f: (A) -> B) = fix().map(f) } @@ -22,10 +21,10 @@ interface Tuple2FunctorInstance : Functor> { interface Tuple2ApplicativeInstance : Applicative>, Tuple2FunctorInstance { fun MF(): Monoid - override fun Kind, A>.map(f: (A) -> B) = + override fun Tuple2Of.map(f: (A) -> B) = fix().map(f) - override fun Kind, A>.ap(ff: Kind, (A) -> B>) = + override fun Tuple2Of.ap(ff: Tuple2Of B>) = fix().ap(ff.fix()) override fun just(a: A) = @@ -37,13 +36,13 @@ interface Tuple2MonadInstance : Monad>, Tuple2ApplicativeI override fun MF(): Monoid - override fun Kind, A>.map(f: (A) -> B) = + override fun Tuple2Of.map(f: (A) -> B) = fix().map(f) - override fun Kind, A>.ap(ff: Kind, (A) -> B>) = + override fun Tuple2Of.ap(ff: Tuple2Of B>) = fix().ap(ff) - override fun Kind, A>.flatMap(f: (A) -> Kind, B>) = + override fun Tuple2Of.flatMap(f: (A) -> Tuple2Of) = fix().flatMap { f(it).fix() } override tailrec fun tailRecM(a: A, f: (A) -> Tuple2Of>): Tuple2 { @@ -57,7 +56,7 @@ interface Tuple2MonadInstance : Monad>, Tuple2ApplicativeI @extension interface Tuple2BifunctorInstance : Bifunctor { - override fun Kind2.bimap( + override fun Tuple2Of.bimap( fl: (A) -> C, fr: (B) -> D ) = fix().bimap(fl, fr) @@ -65,19 +64,19 @@ interface Tuple2BifunctorInstance : Bifunctor { @extension interface Tuple2ComonadInstance : Comonad>, Tuple2FunctorInstance { - override fun Kind, A>.coflatMap(f: (Kind, A>) -> B) = + override fun Tuple2Of.coflatMap(f: (Tuple2Of) -> B) = fix().coflatMap(f) - override fun Kind, A>.extract() = + override fun Tuple2Of.extract() = fix().extract() } @extension interface Tuple2FoldableInstance : Foldable> { - override fun Kind, A>.foldLeft(b: B, f: (B, A) -> B) = + override fun Tuple2Of.foldLeft(b: B, f: (B, A) -> B) = fix().foldL(b, f) - override fun Kind, A>.foldRight(lb: Eval, f: (A, Eval) -> Eval) = + override fun Tuple2Of.foldRight(lb: Eval, f: (A, Eval) -> Eval) = fix().foldR(lb, f) } diff --git a/modules/core/arrow-instances-data/src/main/kotlin/arrow/instances/kleisli.kt b/modules/core/arrow-instances-data/src/main/kotlin/arrow/instances/kleisli.kt index 4761e0f9c5f..88e3c5c4cfa 100644 --- a/modules/core/arrow-instances-data/src/main/kotlin/arrow/instances/kleisli.kt +++ b/modules/core/arrow-instances-data/src/main/kotlin/arrow/instances/kleisli.kt @@ -95,6 +95,12 @@ interface KleisliMonadErrorInstance : MonadError override fun AE(): ApplicativeError = ME() override fun AF(): Applicative = ME() + +} + +@extension +interface KleisliMonadThrow : MonadThrow>, KleisliMonadErrorInstance { + override fun ME(): MonadError } /** diff --git a/modules/core/arrow-mtl/src/main/kotlin/arrow/mtl/typeclasses/MonadCombine.kt b/modules/core/arrow-mtl/src/main/kotlin/arrow/mtl/typeclasses/MonadCombine.kt index 97ef8599fef..32e5829dae9 100644 --- a/modules/core/arrow-mtl/src/main/kotlin/arrow/mtl/typeclasses/MonadCombine.kt +++ b/modules/core/arrow-mtl/src/main/kotlin/arrow/mtl/typeclasses/MonadCombine.kt @@ -3,7 +3,6 @@ package arrow.mtl.typeclasses import arrow.Kind import arrow.Kind2 import arrow.core.Tuple2 -import arrow.instances.statet.applicative.just import arrow.typeclasses.Alternative import arrow.typeclasses.Bifoldable import arrow.typeclasses.Foldable diff --git a/modules/core/arrow-test/src/main/kotlin/arrow/test/laws/AsyncLaws.kt b/modules/core/arrow-test/src/main/kotlin/arrow/test/laws/AsyncLaws.kt index fe5d4190154..2c6cff0061f 100644 --- a/modules/core/arrow-test/src/main/kotlin/arrow/test/laws/AsyncLaws.kt +++ b/modules/core/arrow-test/src/main/kotlin/arrow/test/laws/AsyncLaws.kt @@ -4,12 +4,19 @@ import arrow.Kind import arrow.core.Either import arrow.core.Left import arrow.core.Right +import arrow.effects.IO +import arrow.effects.Promise +import arrow.effects.instances.io.async.defer +import arrow.effects.instances.io.monadDefer.defer import arrow.effects.typeclasses.Async +import arrow.effects.typeclasses.ExitCase +import arrow.test.generators.genEither import arrow.test.generators.genIntSmall import arrow.test.generators.genThrowable import arrow.typeclasses.Eq import io.kotlintest.properties.Gen import io.kotlintest.properties.forAll +import io.kotlintest.properties.map import kotlinx.coroutines.newSingleThreadContext object AsyncLaws { @@ -25,7 +32,11 @@ object AsyncLaws { Law("Async Laws: error equivalence") { AC.asyncError(EQ) }, Law("Async Laws: continueOn jumps threads") { AC.continueOn(EQ) }, Law("Async Laws: async constructor") { AC.asyncConstructor(EQ) }, - Law("Async Laws: continueOn on comprehensions") { AC.continueOnComprehension(EQ) } + Law("Async Laws: async can be derived from asyncF") { AC.asyncCanBeDerivedFromAsyncF(EQ) }, + Law("Async Laws: bracket release is called on completed or error") { AC.bracketReleaseIscalledOnCompletedOrError(EQ) }, + Law("Async Laws: continueOn on comprehensions") { AC.continueOnComprehension(EQ) }, + Law("Async Laws: async cancelable coherence") { AC.asyncCancelableCoherence(EQ) }, + Law("Async Laws: cancelable cancelableF coherence") { AC.cancelableCancelableFCoherence(EQ) } ) fun Async.asyncSuccess(EQ: Eq>): Unit = @@ -67,7 +78,46 @@ object AsyncLaws { }.equalUnderTheLaw(just(threadId1 + threadId2), EQ) } - // Turns out that kotlinx.coroutines decides to rewrite thread names - private fun getCurrentThread() = - Thread.currentThread().name.substringBefore(' ').toInt() -} + fun Async.asyncCanBeDerivedFromAsyncF(EQ: Eq>): Unit = + forAll(genEither(genThrowable(), Gen.int())) { eith -> + val k: ((Either) -> Unit) -> Unit = { f -> + f(eith) + } + + async(k).equalUnderTheLaw(asyncF { cb -> delay { k(cb) } }, EQ) + } + + fun Async.bracketReleaseIscalledOnCompletedOrError(EQ: Eq>): Unit = + forAll(Gen.string().map(::just), Gen.int()) { fa, b -> + Promise.uncancelable(this).flatMap { promise -> + val br = delay { promise }.bracketCase(use = { fa }, release = { r, exitCase -> + when (exitCase) { + is ExitCase.Completed -> r.complete(b) + is ExitCase.Error -> r.complete(b) + else -> just(Unit) + } + }) + + asyncF { cb -> + br.flatMap { delay { cb(Right(Unit)) } } + }.flatMap { promise.get } + }.equalUnderTheLaw(just(b), EQ) + } + + fun Async.asyncCancelableCoherence(EQ: Eq>): Unit = + forAll(genEither(genThrowable(), Gen.int())) { eith -> + async { cb -> cb(eith) } + .equalUnderTheLaw(cancelable { cb -> cb(eith); just(Unit) }, EQ) + } + + fun Async.cancelableCancelableFCoherence(EQ: Eq>): Unit = + forAll(genEither(genThrowable(), Gen.int())) { eith -> + cancelable { cb -> cb(eith); just(Unit) } + .equalUnderTheLaw(cancelableF { cb -> delay { cb(eith); just(Unit) } }, EQ) + } + + // Turns out that kotlinx.coroutines decides to rewrite thread names + private fun getCurrentThread() = + Thread.currentThread().name.substringBefore(' ').toInt() + + } diff --git a/modules/core/arrow-typeclasses/src/main/kotlin/arrow/typeclasses/Applicative.kt b/modules/core/arrow-typeclasses/src/main/kotlin/arrow/typeclasses/Applicative.kt index 227e536a8e4..2d7620926ad 100644 --- a/modules/core/arrow-typeclasses/src/main/kotlin/arrow/typeclasses/Applicative.kt +++ b/modules/core/arrow-typeclasses/src/main/kotlin/arrow/typeclasses/Applicative.kt @@ -13,7 +13,8 @@ interface Applicative : Functor { fun just(a: A): Kind - fun A.just(dummy: Unit = Unit): Kind = just(this) + fun A.just(dummy: Unit = Unit): Kind = + just(this) fun unit(): Kind = just(Unit) @@ -22,11 +23,14 @@ interface Applicative : Functor { fun Kind.product(fb: Kind): Kind> = fb.ap(this.map { a: A -> { b: B -> Tuple2(a, b) } }) - override fun Kind.map(f: (A) -> B): Kind = ap(just(f)) + override fun Kind.map(f: (A) -> B): Kind = + ap(just(f)) - fun Kind.map2(fb: Kind, f: (Tuple2) -> Z): Kind = product(fb).map(f) + fun Kind.map2(fb: Kind, f: (Tuple2) -> Z): Kind = + product(fb).map(f) - fun Kind.map2Eval(fb: Eval>, f: (Tuple2) -> Z): Eval> = fb.map { fc -> map2(fc, f) } + fun Kind.map2Eval(fb: Eval>, f: (Tuple2) -> Z): Eval> = + fb.map { fc -> map2(fc, f) } fun Kind>.product( other: Kind, diff --git a/modules/effects/arrow-data-instances-effects/src/main/kotlin/arrow/instances/eithert.kt b/modules/effects/arrow-data-instances-effects/src/main/kotlin/arrow/instances/eithert.kt index 0b89bc97c7e..b8c655c6f13 100644 --- a/modules/effects/arrow-data-instances-effects/src/main/kotlin/arrow/instances/eithert.kt +++ b/modules/effects/arrow-data-instances-effects/src/main/kotlin/arrow/instances/eithert.kt @@ -9,9 +9,11 @@ import arrow.instances.EitherTMonadThrowInstance import arrow.instances.either.monad.flatten import arrow.typeclasses.ApplicativeError import arrow.typeclasses.Monad +import arrow.undocumented import kotlin.coroutines.CoroutineContext @extension +@undocumented interface EitherTBracketInstance : Bracket, Throwable>, EitherTMonadThrowInstance { fun MDF(): MonadDefer @@ -64,6 +66,7 @@ interface EitherTBracketInstance : Bracket, Th } @extension +@undocumented interface EitherTMonadDeferInstance : MonadDefer>, EitherTBracketInstance { override fun MDF(): MonadDefer @@ -74,6 +77,7 @@ interface EitherTMonadDeferInstance : MonadDefer : Async>, EitherTMonadDeferInstance { fun ASF(): Async @@ -84,6 +88,10 @@ interface EitherTAsyncInstance : Async>, Eithe EitherT.liftF(this, async(fa)) } + override fun asyncF(k: ProcF, A>): EitherT = ASF().run { + EitherT.liftF(this, asyncF { cb -> k(cb).value().unit() }) + } + override fun EitherTOf.continueOn(ctx: CoroutineContext): EitherT = ASF().run { EitherT(value().continueOn(ctx)) } @@ -91,6 +99,7 @@ interface EitherTAsyncInstance : Async>, Eithe } @extension +@undocumented interface EitherTEffectInstance : Effect>, EitherTAsyncInstance { fun EFF(): Effect @@ -108,6 +117,7 @@ interface EitherTEffectInstance : Effect>, Eit } @extension +@undocumented interface EitherTConcurrentEffectInstance : ConcurrentEffect>, EitherTEffectInstance { fun CEFF(): ConcurrentEffect diff --git a/modules/effects/arrow-data-instances-effects/src/main/kotlin/arrow/instances/kleisli.kt b/modules/effects/arrow-data-instances-effects/src/main/kotlin/arrow/instances/kleisli.kt index 7e9cdb0d629..c4e4b1d24b6 100644 --- a/modules/effects/arrow-data-instances-effects/src/main/kotlin/arrow/instances/kleisli.kt +++ b/modules/effects/arrow-data-instances-effects/src/main/kotlin/arrow/instances/kleisli.kt @@ -6,9 +6,11 @@ import arrow.effects.typeclasses.* import arrow.extension import arrow.instances.KleisliMonadErrorInstance import arrow.typeclasses.MonadError +import arrow.undocumented import kotlin.coroutines.CoroutineContext @extension +@undocumented interface KleisliBracketInstance : Bracket, E>, KleisliMonadErrorInstance { fun BF(): Bracket diff --git a/modules/effects/arrow-data-instances-effects/src/main/kotlin/arrow/instances/optiont.kt b/modules/effects/arrow-data-instances-effects/src/main/kotlin/arrow/instances/optiont.kt index df650bccbbd..bd465ebff32 100644 --- a/modules/effects/arrow-data-instances-effects/src/main/kotlin/arrow/instances/optiont.kt +++ b/modules/effects/arrow-data-instances-effects/src/main/kotlin/arrow/instances/optiont.kt @@ -7,9 +7,11 @@ import arrow.effects.typeclasses.* import arrow.extension import arrow.instances.OptionTMonadError import arrow.typeclasses.MonadError +import arrow.undocumented import kotlin.coroutines.CoroutineContext @extension +@undocumented interface OptionTBracketInstance : Bracket, Throwable>, OptionTMonadError { fun MD(): MonadDefer @@ -47,6 +49,7 @@ interface OptionTBracketInstance : Bracket, Throwable>, O } @extension +@undocumented interface OptionTMonadDeferInstance : MonadDefer>, OptionTBracketInstance { override fun MD(): MonadDefer @@ -57,6 +60,7 @@ interface OptionTMonadDeferInstance : MonadDefer>, Option } @extension +@undocumented interface OptionTAsyncInstance : Async>, OptionTMonadDeferInstance { fun AS(): Async @@ -67,6 +71,10 @@ interface OptionTAsyncInstance : Async>, OptionTMonadDefe OptionT.liftF(this, async(fa)) } + override fun asyncF(k: ProcF, A>): OptionT = AS().run { + OptionT.liftF(this, asyncF { cb -> k(cb).value().unit() }) + } + override fun OptionTOf.continueOn(ctx: CoroutineContext): OptionT = AS().run { OptionT(value().continueOn(ctx)) } diff --git a/modules/effects/arrow-data-instances-effects/src/main/kotlin/arrow/instances/statet.kt b/modules/effects/arrow-data-instances-effects/src/main/kotlin/arrow/instances/statet.kt index c447510adc8..af913b0b33a 100644 --- a/modules/effects/arrow-data-instances-effects/src/main/kotlin/arrow/instances/statet.kt +++ b/modules/effects/arrow-data-instances-effects/src/main/kotlin/arrow/instances/statet.kt @@ -1,18 +1,17 @@ package arrow.effects.instances -import arrow.core.None -import arrow.core.Option -import arrow.core.Some -import arrow.core.getOrElse +import arrow.core.* import arrow.data.* import arrow.effects.Ref import arrow.effects.typeclasses.* import arrow.extension import arrow.instances.StateTMonadThrowInstance import arrow.typeclasses.MonadError +import arrow.undocumented import kotlin.coroutines.CoroutineContext @extension +@undocumented interface StateTBracketInstance : Bracket, Throwable>, StateTMonadThrowInstance { fun MD(): MonadDefer @@ -47,6 +46,7 @@ interface StateTBracketInstance : Bracket, Throwable } @extension +@undocumented interface StateTMonadDeferInstance : MonadDefer>, StateTBracketInstance { override fun MD(): MonadDefer @@ -58,6 +58,7 @@ interface StateTMonadDeferInstance : MonadDefer>, St } @extension +@undocumented interface StateTAsyncInstane : Async>, StateTMonadDeferInstance { fun AS(): Async @@ -68,6 +69,13 @@ interface StateTAsyncInstane : Async>, StateTMonadDe StateT.liftF(this, async(fa)) } + override fun asyncF(k: ProcF, A>): StateT = AS().run { + StateT.invoke(this) { s -> + asyncF { cb -> k(cb).fix().runA(this, s) } + .map { Tuple2(s, it) } + } + } + override fun StateTOf.continueOn(ctx: CoroutineContext): StateT = AS().run { StateT(this) { s -> runM(this, s).continueOn(ctx) } } diff --git a/modules/effects/arrow-data-instances-effects/src/main/kotlin/arrow/instances/writert.kt b/modules/effects/arrow-data-instances-effects/src/main/kotlin/arrow/instances/writert.kt index d2d91b9c19d..dff4aa25f04 100644 --- a/modules/effects/arrow-data-instances-effects/src/main/kotlin/arrow/instances/writert.kt +++ b/modules/effects/arrow-data-instances-effects/src/main/kotlin/arrow/instances/writert.kt @@ -9,9 +9,11 @@ import arrow.extension import arrow.instances.WriterTMonadThrow import arrow.typeclasses.MonadError import arrow.typeclasses.Monoid +import arrow.undocumented import kotlin.coroutines.CoroutineContext @extension +@undocumented interface WriterTBrackInstance : Bracket, Throwable>, WriterTMonadThrow { fun MD(): MonadDefer @@ -43,6 +45,7 @@ interface WriterTBrackInstance : Bracket, Throwable } @extension +@undocumented interface WriterTMonadDeferInstance : MonadDefer>, WriterTBrackInstance { override fun MD(): MonadDefer @@ -55,6 +58,7 @@ interface WriterTMonadDeferInstance : MonadDefer>, } @extension +@undocumented interface WriterTAsyncInstance : Async>, WriterTMonadDeferInstance { fun AS(): Async @@ -67,6 +71,10 @@ interface WriterTAsyncInstance : Async>, WriterTMon WriterT.liftF(async(fa), MM(), this) } + override fun asyncF(k: ProcF, A>): Kind, A> = AS().run { + WriterT.liftF(asyncF { cb -> k(cb).value().unit() }, MM(), this) + } + override fun WriterTOf.continueOn(ctx: CoroutineContext): WriterT = AS().run { WriterT(value().continueOn(ctx)) } @@ -74,6 +82,7 @@ interface WriterTAsyncInstance : Async>, WriterTMon } @extension +@undocumented interface WriterTEffectInstance : Effect>, WriterTAsyncInstance { fun EFF(): Effect @@ -92,6 +101,7 @@ interface WriterTEffectInstance : Effect>, WriterTA } @extension +@undocumented interface WriterTConcurrentEffectInstance : ConcurrentEffect>, WriterTEffectInstance { fun CEFF(): ConcurrentEffect diff --git a/modules/effects/arrow-effects-instances/src/main/kotlin/arrow/effects/instances/io.kt b/modules/effects/arrow-effects-instances/src/main/kotlin/arrow/effects/instances/io.kt index d8ce7130017..4918d687f23 100644 --- a/modules/effects/arrow-effects-instances/src/main/kotlin/arrow/effects/instances/io.kt +++ b/modules/effects/arrow-effects-instances/src/main/kotlin/arrow/effects/instances/io.kt @@ -1,6 +1,5 @@ package arrow.effects.instances -import arrow.Kind import arrow.core.Either import arrow.deprecation.ExtensionsDSLDeprecated import arrow.effects.* @@ -13,28 +12,28 @@ import arrow.effects.handleErrorWith as ioHandleErrorWith @extension interface IOFunctorInstance : Functor { - override fun Kind.map(f: (A) -> B): IO = + override fun IOOf.map(f: (A) -> B): IO = fix().map(f) } @extension interface IOApplicativeInstance : Applicative { - override fun Kind.map(f: (A) -> B): IO = + override fun IOOf.map(f: (A) -> B): IO = fix().map(f) override fun just(a: A): IO = IO.just(a) - override fun Kind.ap(ff: IOOf<(A) -> B>): IO = - fix().ioAp(ff) + override fun IOOf.ap(ff: IOOf<(A) -> B>): IO = + ioAp(ff) } @extension interface IOMonadInstance : Monad { - override fun Kind.flatMap(f: (A) -> Kind): IO = + override fun IOOf.flatMap(f: (A) -> IOOf): IO = fix().flatMap(f) - override fun Kind.map(f: (A) -> B): IO = + override fun IOOf.map(f: (A) -> B): IO = fix().map(f) override fun tailRecM(a: A, f: kotlin.Function1>>): IO = @@ -46,23 +45,32 @@ interface IOMonadInstance : Monad { @extension interface IOApplicativeErrorInstance : ApplicativeError, IOApplicativeInstance { - override fun Kind.attempt(): IO> = + override fun IOOf.attempt(): IO> = fix().attempt() - override fun Kind.handleErrorWith(f: (Throwable) -> Kind): IO = - fix().ioHandleErrorWith(f) + override fun IOOf.handleErrorWith(f: (Throwable) -> IOOf): IO = + ioHandleErrorWith(f) override fun raiseError(e: Throwable): IO = IO.raiseError(e) } @extension -interface IOMonadErrorInstance : MonadError, IOMonadInstance { - override fun Kind.attempt(): IO> = +interface IOMonadErrorInstance : MonadError, IOApplicativeErrorInstance, IOMonadInstance { + + override fun just(a: A): IO = IO.just(a) + + override fun IOOf.ap(ff: IOOf<(A) -> B>): IO = + ioAp(ff) + + override fun IOOf.map(f: (A) -> B): IO = + fix().map(f) + + override fun IOOf.attempt(): IO> = fix().attempt() - override fun Kind.handleErrorWith(f: (Throwable) -> Kind): IO = - fix().ioHandleErrorWith(f) + override fun IOOf.handleErrorWith(f: (Throwable) -> IOOf): IO = + ioHandleErrorWith(f) override fun raiseError(e: Throwable): IO = IO.raiseError(e) @@ -73,17 +81,17 @@ interface IOMonadThrowInstance : MonadThrow, IOMonadErrorInstance @extension interface IOBracketInstance : Bracket, IOMonadThrowInstance { - override fun Kind.bracketCase(release: (A, ExitCase) -> Kind, use: (A) -> Kind): IO = - fix().bracketCase({ a, e -> release(a, e).fix() }, { a -> use(a).fix() }) + override fun IOOf.bracketCase(release: (A, ExitCase) -> IOOf, use: (A) -> IOOf): IO = + fix().bracketCase({ a, e -> release(a, e) }, { a -> use(a) }) - override fun Kind.bracket(release: (A) -> Kind, use: (A) -> Kind): IO = - fix().bracket({ a -> release(a).fix() }, { a -> use(a).fix() }) + override fun IOOf.bracket(release: (A) -> IOOf, use: (A) -> IOOf): IO = + fix().bracket({ a -> release(a) }, { a -> use(a) }) - override fun Kind.guarantee(finalizer: Kind): IO = - fix().guarantee(finalizer.fix()) + override fun IOOf.guarantee(finalizer: IOOf): IO = + fix().guarantee(finalizer) - override fun Kind.guaranteeCase(finalizer: (ExitCase) -> Kind): IO = - fix().guaranteeCase { e -> finalizer(e).fix() } + override fun IOOf.guaranteeCase(finalizer: (ExitCase) -> IOOf): IO = + fix().guaranteeCase { e -> finalizer(e) } } @extension @@ -99,19 +107,22 @@ interface IOAsyncInstance : Async, IOMonadDeferInstance { override fun async(fa: Proc): IO = IO.async(fa.toIOProc()) + override fun asyncF(k: ProcF): IO = + IO.asyncF(k.toIOProcF()) + override fun IOOf.continueOn(ctx: CoroutineContext): IO = fix().continueOn(ctx) } @extension interface IOEffectInstance : Effect, IOAsyncInstance { - override fun Kind.runAsync(cb: (Either) -> Kind): IO = + override fun IOOf.runAsync(cb: (Either) -> IOOf): IO = fix().runAsync(cb) } @extension interface IOConcurrentEffectInstance : ConcurrentEffect, IOEffectInstance { - override fun Kind.runAsyncCancellable(cb: (Either) -> Kind): IO = + override fun IOOf.runAsyncCancellable(cb: (Either) -> IOOf): IO = fix().runAsyncCancellable(OnCancel.ThrowCancellationException, cb) } @@ -130,10 +141,8 @@ interface IOMonoidInstance : Monoid>, IOSemigroupInstance { fun SM(): Monoid - override fun IO.combine(b: IO): IO = - flatMap { a1: A -> b.map { a2: A -> SM().run { a1.combine(a2) } } } - override fun empty(): IO = IO.just(SM().empty()) + } object IOContext : IOConcurrentEffectInstance diff --git a/modules/effects/arrow-effects-instances/src/test/kotlin/arrow/effects/IOTest.kt b/modules/effects/arrow-effects-instances/src/test/kotlin/arrow/effects/IOTest.kt index 857437e61fb..d33e1d23428 100644 --- a/modules/effects/arrow-effects-instances/src/test/kotlin/arrow/effects/IOTest.kt +++ b/modules/effects/arrow-effects-instances/src/test/kotlin/arrow/effects/IOTest.kt @@ -1,7 +1,7 @@ package arrow.effects -import arrow.Kind import arrow.core.* +import arrow.effects.instances.io.applicativeError.attempt import arrow.effects.instances.io.async.async import arrow.effects.instances.io.monad.binding import arrow.effects.instances.io.monad.flatMap @@ -25,12 +25,11 @@ import java.util.concurrent.TimeUnit @RunWith(KTestJUnitRunner::class) class IOTest : UnitSpec() { - val EQ_OPTION = Option.eq(Eq.any()) - fun EQ(): Eq> { + fun EQ(): Eq> { return Eq { a, b -> - EQ_OPTION.run { - a.fix().attempt().unsafeRunTimed(60.seconds).eqv(b.fix().attempt().unsafeRunTimed(60.seconds)) + Option.eq(Eq.any()).run { + a.attempt().unsafeRunTimed(1.seconds).eqv(b.attempt().unsafeRunTimed(1.seconds)) } } } @@ -379,19 +378,41 @@ class IOTest : UnitSpec() { } "IO bracket cancellation should release resource with cancel exit status" { - lateinit var ec: ExitCase - val countDownLatch = CountDownLatch(1) - - IO.just(0L) - .bracketCase( - use = { IO.never }, - release = { _, exitCase -> IO { ec = exitCase; countDownLatch.countDown() } } - ) - .unsafeRunAsyncCancellable { } - .invoke() //cancel immediately - - countDownLatch.await(2, TimeUnit.SECONDS) - ec shouldBe ExitCase.Cancelled + Promise.uncancelable>(IO.async()).flatMap { p -> + IO.just(0L) + .bracketCase( + use = { IO.never }, + release = { _, exitCase -> p.complete(exitCase) } + ) + .unsafeRunAsyncCancellable { } + .invoke() //cancel immediately + + p.get + }.unsafeRunSync() shouldBe ExitCase.Cancelled + } + + "Cancelable should run CancelToken" { + Promise.uncancelable(IO.async()).flatMap { p -> + IO.async().cancelable { _ -> + p.complete(Unit) + }.fix() + .unsafeRunAsyncCancellable { } + .invoke() + + p.get + }.unsafeRunSync() shouldBe Unit + } + + "CancelableF should run CancelToken" { + Promise.uncancelable(IO.async()).flatMap { p -> + IO.async().cancelableF { _ -> + IO { p.complete(Unit) } + }.fix() + .unsafeRunAsyncCancellable { } + .invoke() + + p.get + }.unsafeRunSync() shouldBe Unit } "IO should cancel KindConnection on dispose" { diff --git a/modules/effects/arrow-effects-kotlinx-coroutines-instances/src/main/kotlin/arrow/effects/DeferredKInstances.kt b/modules/effects/arrow-effects-kotlinx-coroutines-instances/src/main/kotlin/arrow/effects/DeferredKInstances.kt index ae5939180ee..e32658b59ec 100644 --- a/modules/effects/arrow-effects-kotlinx-coroutines-instances/src/main/kotlin/arrow/effects/DeferredKInstances.kt +++ b/modules/effects/arrow-effects-kotlinx-coroutines-instances/src/main/kotlin/arrow/effects/DeferredKInstances.kt @@ -89,10 +89,13 @@ interface DeferredKAsyncInstance : Async, DeferredKMonadDeferInsta override fun async(fa: Proc): DeferredK = DeferredK.async(fa = { _, cb -> fa(cb) }) + override fun asyncF(k: ProcF): DeferredK = + DeferredK.asyncF(fa = { _, cb -> k(cb) }) + override fun DeferredKOf.continueOn(ctx: CoroutineContext): DeferredK = fix().continueOn(ctx = ctx) - override fun invoke(ctx: CoroutineContext, f: () -> A): Kind = + override fun invoke(ctx: CoroutineContext, f: () -> A): DeferredK = DeferredK.invoke(ctx = ctx, f = { f() }) } @@ -104,7 +107,7 @@ interface DeferredKEffectInstance : Effect, DeferredKAsyncInstance @extension interface DeferredKConcurrentEffectInstance : ConcurrentEffect, DeferredKEffectInstance { - override fun DeferredKOf.runAsyncCancellable(cb: (Either) -> Kind): Kind = + override fun DeferredKOf.runAsyncCancellable(cb: (Either) -> Kind): DeferredK = fix().runAsyncCancellable(onCancel = OnCancel.ThrowCancellationException, cb = cb) } diff --git a/modules/effects/arrow-effects-kotlinx-coroutines/src/main/kotlin/arrow/effects/DeferredK.kt b/modules/effects/arrow-effects-kotlinx-coroutines/src/main/kotlin/arrow/effects/DeferredK.kt index f4ba63075ab..f07a4179fa1 100644 --- a/modules/effects/arrow-effects-kotlinx-coroutines/src/main/kotlin/arrow/effects/DeferredK.kt +++ b/modules/effects/arrow-effects-kotlinx-coroutines/src/main/kotlin/arrow/effects/DeferredK.kt @@ -437,7 +437,7 @@ sealed class DeferredK( private suspend inline fun Deferred.bracketCase(use: (A) -> DeferredKOf, release: (A, ExitCase) -> DeferredKOf): B { val a = await() - return try { + val b = try { use(a).await() } catch (e: Throwable) { try { @@ -448,6 +448,9 @@ sealed class DeferredK( } throw e } + + release(a, ExitCase.Completed).await() + return b } /** @@ -653,6 +656,16 @@ sealed class DeferredK( } } + fun asyncF(scope: CoroutineScope = GlobalScope, ctx: CoroutineContext = Dispatchers.Default, start: CoroutineStart = CoroutineStart.LAZY, fa: DeferredKProcF): DeferredK { + val newScope = if (scope.coroutineContext[Job] == null) scope + Job() else scope + val parent = newScope.coroutineContext[Job]!! + return ConnectedGenerated(ctx, start, scope) { conn -> + CompletableDeferred(parent).apply { + fa(conn) { it.fold(this::completeExceptionally, this::complete) }.await() + }.await() + } + } + fun tailRecM(a: A, f: (A) -> DeferredKOf>): DeferredK = f(a).value().let { initial: Deferred> -> var current: Deferred> = initial diff --git a/modules/effects/arrow-effects-kotlinx-coroutines/src/main/kotlin/arrow/effects/DeferredKConnection.kt b/modules/effects/arrow-effects-kotlinx-coroutines/src/main/kotlin/arrow/effects/DeferredKConnection.kt index 1902e59c1bd..494c2d8d051 100644 --- a/modules/effects/arrow-effects-kotlinx-coroutines/src/main/kotlin/arrow/effects/DeferredKConnection.kt +++ b/modules/effects/arrow-effects-kotlinx-coroutines/src/main/kotlin/arrow/effects/DeferredKConnection.kt @@ -7,6 +7,7 @@ import arrow.effects.handleErrorWith as handleErrorW typealias DeferredKConnection = KindConnection typealias DeferredKProc = (DeferredKConnection, (Either) -> Unit) -> Unit +typealias DeferredKProcF = (DeferredKConnection, (Either) -> Unit) -> DeferredKOf /** * Connection for [DeferredK]. diff --git a/modules/effects/arrow-effects-reactor-instances/src/main/kotlin/arrow/effects/FluxKInstances.kt b/modules/effects/arrow-effects-reactor-instances/src/main/kotlin/arrow/effects/FluxKInstances.kt index 5635c4ff1a7..4ccd83d39fd 100644 --- a/modules/effects/arrow-effects-reactor-instances/src/main/kotlin/arrow/effects/FluxKInstances.kt +++ b/modules/effects/arrow-effects-reactor-instances/src/main/kotlin/arrow/effects/FluxKInstances.kt @@ -13,7 +13,7 @@ import kotlin.coroutines.CoroutineContext @extension interface FluxKFunctorInstance : Functor { - override fun Kind.map(f: (A) -> B): FluxK = + override fun FluxKOf.map(f: (A) -> B): FluxK = fix().map(f) } @@ -25,7 +25,7 @@ interface FluxKApplicativeInstance : Applicative { override fun FluxKOf.ap(ff: FluxKOf<(A) -> B>): FluxK = fix().ap(ff) - override fun Kind.map(f: (A) -> B): FluxK = + override fun FluxKOf.map(f: (A) -> B): FluxK = fix().map(f) } @@ -34,10 +34,10 @@ interface FluxKMonadInstance : Monad { override fun FluxKOf.ap(ff: FluxKOf<(A) -> B>): FluxK = fix().ap(ff) - override fun Kind.flatMap(f: (A) -> Kind): FluxK = + override fun FluxKOf.flatMap(f: (A) -> FluxKOf): FluxK = fix().flatMap(f) - override fun Kind.map(f: (A) -> B): FluxK = + override fun FluxKOf.map(f: (A) -> B): FluxK = fix().map(f) override fun tailRecM(a: A, f: kotlin.Function1>>): FluxK = @@ -49,25 +49,25 @@ interface FluxKMonadInstance : Monad { @extension interface FluxKFoldableInstance : Foldable { - override fun Kind.foldLeft(b: B, f: (B, A) -> B): B = + override fun FluxKOf.foldLeft(b: B, f: (B, A) -> B): B = fix().foldLeft(b, f) - override fun Kind.foldRight(lb: Eval, f: (A, Eval) -> Eval): arrow.core.Eval = + override fun FluxKOf.foldRight(lb: Eval, f: (A, Eval) -> Eval): arrow.core.Eval = fix().foldRight(lb, f) } @extension interface FluxKTraverseInstance : Traverse { - override fun Kind.map(f: (A) -> B): FluxK = + override fun FluxKOf.map(f: (A) -> B): FluxK = fix().map(f) override fun FluxKOf.traverse(AP: Applicative, f: (A) -> Kind): Kind> = fix().traverse(AP, f) - override fun Kind.foldLeft(b: B, f: (B, A) -> B): B = + override fun FluxKOf.foldLeft(b: B, f: (B, A) -> B): B = fix().foldLeft(b, f) - override fun Kind.foldRight(lb: Eval, f: (A, Eval) -> Eval): arrow.core.Eval = + override fun FluxKOf.foldRight(lb: Eval, f: (A, Eval) -> Eval): arrow.core.Eval = fix().foldRight(lb, f) } @@ -98,7 +98,7 @@ interface FluxKMonadThrowInstance : MonadThrow, FluxKMonadErrorInstanc @extension interface FluxKBracketInstance : Bracket, FluxKMonadThrowInstance { - override fun Kind.bracketCase(release: (A, ExitCase) -> Kind, use: (A) -> Kind): FluxK = + override fun FluxKOf.bracketCase(release: (A, ExitCase) -> Kind, use: (A) -> FluxKOf): FluxK = fix().bracketCase({ use(it) }, { a, e -> release(a, e) }) } @@ -117,6 +117,9 @@ interface FluxKAsyncInstance : override fun async(fa: Proc): FluxK = FluxK.async { _, cb -> fa(cb) } + override fun asyncF(k: ProcF): FluxK = + FluxK.asyncF { _, cb -> k(cb) } + override fun FluxKOf.continueOn(ctx: CoroutineContext): FluxK = fix().continueOn(ctx) } @@ -133,31 +136,31 @@ interface FluxKEffectInstance : interface FluxKConcurrentEffectInstance : ConcurrentEffect, FluxKEffectInstance { - override fun Kind.runAsyncCancellable(cb: (Either) -> FluxKOf): FluxK = + override fun FluxKOf.runAsyncCancellable(cb: (Either) -> FluxKOf): FluxK = fix().runAsyncCancellable(cb) } fun FluxK.Companion.monadFlat(): FluxKMonadInstance = monad() fun FluxK.Companion.monadConcat(): FluxKMonadInstance = object : FluxKMonadInstance { - override fun Kind.flatMap(f: (A) -> Kind): FluxK = + override fun FluxKOf.flatMap(f: (A) -> FluxKOf): FluxK = fix().concatMap { f(it).fix() } } fun FluxK.Companion.monadSwitch(): FluxKMonadInstance = object : FluxKMonadErrorInstance { - override fun Kind.flatMap(f: (A) -> Kind): FluxK = + override fun FluxKOf.flatMap(f: (A) -> FluxKOf): FluxK = fix().switchMap { f(it).fix() } } fun FluxK.Companion.monadErrorFlat(): FluxKMonadErrorInstance = monadError() fun FluxK.Companion.monadErrorConcat(): FluxKMonadErrorInstance = object : FluxKMonadErrorInstance { - override fun Kind.flatMap(f: (A) -> Kind): FluxK = + override fun FluxKOf.flatMap(f: (A) -> FluxKOf): FluxK = fix().concatMap { f(it).fix() } } fun FluxK.Companion.monadErrorSwitch(): FluxKMonadErrorInstance = object : FluxKMonadErrorInstance { - override fun Kind.flatMap(f: (A) -> Kind): FluxK = + override fun FluxKOf.flatMap(f: (A) -> FluxKOf): FluxK = fix().switchMap { f(it).fix() } } diff --git a/modules/effects/arrow-effects-reactor-instances/src/main/kotlin/arrow/effects/MonoKInstances.kt b/modules/effects/arrow-effects-reactor-instances/src/main/kotlin/arrow/effects/MonoKInstances.kt index 569635070cf..fc8b0ea742b 100644 --- a/modules/effects/arrow-effects-reactor-instances/src/main/kotlin/arrow/effects/MonoKInstances.kt +++ b/modules/effects/arrow-effects-reactor-instances/src/main/kotlin/arrow/effects/MonoKInstances.kt @@ -1,6 +1,5 @@ package arrow.effects -import arrow.Kind import arrow.core.Either import arrow.deprecation.ExtensionsDSLDeprecated import arrow.effects.typeclasses.* @@ -10,44 +9,39 @@ import kotlin.coroutines.CoroutineContext @extension interface MonoKFunctorInstance : Functor { - override fun Kind.map(f: (A) -> B): MonoK = + override fun MonoKOf.map(f: (A) -> B): MonoK = fix().map(f) } @extension -interface MonoKApplicativeInstance : Applicative { +interface MonoKApplicativeInstance : Applicative, MonoKFunctorInstance { + override fun MonoKOf.map(f: (A) -> B): MonoK = + fix().map(f) + override fun MonoKOf.ap(ff: MonoKOf<(A) -> B>): MonoK = fix().ap(ff) - override fun Kind.map(f: (A) -> B): MonoK = - fix().map(f) - override fun just(a: A): MonoK = MonoK.just(a) } @extension -interface MonoKMonadInstance : Monad { +interface MonoKMonadInstance : Monad, MonoKApplicativeInstance { + override fun MonoKOf.map(f: (A) -> B): MonoK = + fix().map(f) + override fun MonoKOf.ap(ff: MonoKOf<(A) -> B>): MonoK = fix().ap(ff) - override fun MonoKOf.flatMap(f: (A) -> Kind): MonoK = + override fun MonoKOf.flatMap(f: (A) -> MonoKOf): MonoK = fix().flatMap(f) - override fun MonoKOf.map(f: (A) -> B): MonoK = - fix().map(f) - override fun tailRecM(a: A, f: kotlin.Function1>>): MonoK = MonoK.tailRecM(a, f) - - override fun just(a: A): MonoK = - MonoK.just(a) } @extension -interface MonoKApplicativeErrorInstance : - ApplicativeError, - MonoKApplicativeInstance { +interface MonoKApplicativeErrorInstance : ApplicativeError, MonoKApplicativeInstance { override fun raiseError(e: Throwable): MonoK = MonoK.raiseError(e) @@ -56,9 +50,10 @@ interface MonoKApplicativeErrorInstance : } @extension -interface MonoKMonadErrorInstance : - MonadError, - MonoKMonadInstance { +interface MonoKMonadErrorInstance : MonadError, MonoKMonadInstance, MonoKApplicativeErrorInstance { + override fun MonoKOf.map(f: (A) -> B): MonoK = + fix().map(f) + override fun raiseError(e: Throwable): MonoK = MonoK.raiseError(e) @@ -71,42 +66,37 @@ interface MonoKMonadThrowInstance : MonadThrow, MonoKMonadErrorInstanc @extension interface MonoKBracketInstance : Bracket, MonoKMonadThrowInstance { - override fun Kind.bracketCase(release: (A, ExitCase) -> Kind, use: (A) -> Kind): MonoK = + override fun MonoKOf.bracketCase(release: (A, ExitCase) -> MonoKOf, use: (A) -> MonoKOf): MonoK = fix().bracketCase({ use(it) }, { a, e -> release(a, e) }) } @extension -interface MonoKMonadDeferInstance : - MonoKBracketInstance, - MonadDefer { +interface MonoKMonadDeferInstance : MonadDefer, MonoKBracketInstance { override fun defer(fa: () -> MonoKOf): MonoK = MonoK.defer(fa) } @extension -interface MonoKAsyncInstance : - Async, - MonoKMonadDeferInstance { +interface MonoKAsyncInstance : Async, MonoKMonadDeferInstance { override fun async(fa: Proc): MonoK = MonoK.async { _, cb -> fa(cb) } + override fun asyncF(k: ProcF): MonoK = + MonoK.asyncF { _, cb -> k(cb) } + override fun MonoKOf.continueOn(ctx: CoroutineContext): MonoK = fix().continueOn(ctx) } @extension -interface MonoKEffectInstance : - Effect, - MonoKAsyncInstance { +interface MonoKEffectInstance : Effect, MonoKAsyncInstance { override fun MonoKOf.runAsync(cb: (Either) -> MonoKOf): MonoK = fix().runAsync(cb) } @extension -interface MonoKConcurrentEffectInstance : - ConcurrentEffect, - MonoKEffectInstance { - override fun Kind.runAsyncCancellable(cb: (Either) -> MonoKOf): MonoK = +interface MonoKConcurrentEffectInstance : ConcurrentEffect, MonoKEffectInstance { + override fun MonoKOf.runAsyncCancellable(cb: (Either) -> MonoKOf): MonoK = fix().runAsyncCancellable(cb) } diff --git a/modules/effects/arrow-effects-reactor-instances/src/test/kotlin/arrow/effects/MonoKTests.kt b/modules/effects/arrow-effects-reactor-instances/src/test/kotlin/arrow/effects/MonoKTests.kt index ad342a47abc..34d6e5beffd 100644 --- a/modules/effects/arrow-effects-reactor-instances/src/test/kotlin/arrow/effects/MonoKTests.kt +++ b/modules/effects/arrow-effects-reactor-instances/src/test/kotlin/arrow/effects/MonoKTests.kt @@ -125,7 +125,7 @@ class MonoKTest : UnitSpec() { MonoK.just(Unit) .bracketCase( - use = { MonoK.async { _, _ -> } }, + use = { MonoK.async { _,_ -> } }, release = { _, exitCase -> MonoK { ec = exitCase diff --git a/modules/effects/arrow-effects-reactor/src/main/kotlin/arrow/effects/FluxK.kt b/modules/effects/arrow-effects-reactor/src/main/kotlin/arrow/effects/FluxK.kt index 2c3999d04c8..7f5d8b39631 100644 --- a/modules/effects/arrow-effects-reactor/src/main/kotlin/arrow/effects/FluxK.kt +++ b/modules/effects/arrow-effects-reactor/src/main/kotlin/arrow/effects/FluxK.kt @@ -5,6 +5,7 @@ import arrow.core.* import arrow.effects.CoroutineContextReactorScheduler.asScheduler import arrow.effects.typeclasses.Disposable import arrow.effects.typeclasses.ExitCase +import reactor.core.publisher.FluxSink import arrow.higherkind import arrow.typeclasses.Applicative import reactor.core.publisher.Flux @@ -196,6 +197,24 @@ data class FluxK(val flux: Flux) : FluxKOf, FluxKKindedJ { } }.k() + fun asyncF(fa: FluxKProcF): FluxK = + Flux.create { sink: FluxSink -> + val conn = FluxKConnection() + //On disposing of the upstream stream this will be called by `setCancellable` so check if upstream is already disposed or not because + //on disposing the stream will already be in a terminated state at this point so calling onError, in a terminated state, will blow everything up. + conn.push(FluxK { if (!sink.isCancelled) sink.error(ConnectionCancellationException) }) + sink.onCancel { conn.cancel().value().subscribe() } + + fa(conn) { callback: Either -> + callback.fold({ + sink.error(it) + }, { + sink.next(it) + sink.complete() + }) + }.fix().flux.subscribe({}, sink::error) + }.k() + tailrec fun tailRecM(a: A, f: (A) -> FluxKOf>): FluxK { val either = f(a).value().blockFirst() return when (either) { diff --git a/modules/effects/arrow-effects-reactor/src/main/kotlin/arrow/effects/FluxKConnection.kt b/modules/effects/arrow-effects-reactor/src/main/kotlin/arrow/effects/FluxKConnection.kt index c246d0b4184..f6caf0856f7 100644 --- a/modules/effects/arrow-effects-reactor/src/main/kotlin/arrow/effects/FluxKConnection.kt +++ b/modules/effects/arrow-effects-reactor/src/main/kotlin/arrow/effects/FluxKConnection.kt @@ -6,6 +6,7 @@ import arrow.effects.typeclasses.MonadDefer typealias FluxKConnection = KindConnection typealias FluxKProc = (FluxKConnection, (Either) -> Unit) -> Unit +typealias FluxKProcF = (FluxKConnection, (Either) -> Unit) -> FluxKOf /** * Connection for [FluxK]. diff --git a/modules/effects/arrow-effects-reactor/src/main/kotlin/arrow/effects/MonoK.kt b/modules/effects/arrow-effects-reactor/src/main/kotlin/arrow/effects/MonoK.kt index f96dc016842..9fb8bb6c791 100644 --- a/modules/effects/arrow-effects-reactor/src/main/kotlin/arrow/effects/MonoK.kt +++ b/modules/effects/arrow-effects-reactor/src/main/kotlin/arrow/effects/MonoK.kt @@ -8,6 +8,7 @@ import arrow.effects.typeclasses.Disposable import arrow.effects.typeclasses.ExitCase import arrow.higherkind import reactor.core.publisher.Mono +import reactor.core.publisher.MonoSink import java.util.concurrent.atomic.AtomicBoolean import kotlin.coroutines.CoroutineContext @@ -175,6 +176,25 @@ data class MonoK(val mono: Mono) : MonoKOf, MonoKKindedJ { } }.k() + fun asyncF(fa: MonoKProcF): MonoK = + Mono.create { sink: MonoSink -> + val conn = MonoKConnection() + val isCancelled = AtomicBoolean(false) //Sink is missing isCancelled so we have to do book keeping. + conn.push(MonoK { if (!isCancelled.get()) sink.error(ConnectionCancellationException) }) + sink.onCancel { + isCancelled.compareAndSet(false, true) + conn.cancel().value().subscribe() + } + + fa(conn) { either: Either -> + either.fold({ + sink.error(it) + }, { + sink.success(it) + }) + }.fix().mono.subscribe({}, sink::error) + }.k() + tailrec fun tailRecM(a: A, f: (A) -> MonoKOf>): MonoK { val either = f(a).value().block() return when (either) { @@ -184,3 +204,4 @@ data class MonoK(val mono: Mono) : MonoKOf, MonoKKindedJ { } } } + diff --git a/modules/effects/arrow-effects-reactor/src/main/kotlin/arrow/effects/MonoKConnection.kt b/modules/effects/arrow-effects-reactor/src/main/kotlin/arrow/effects/MonoKConnection.kt index f0815ee7108..5c1cb397c00 100644 --- a/modules/effects/arrow-effects-reactor/src/main/kotlin/arrow/effects/MonoKConnection.kt +++ b/modules/effects/arrow-effects-reactor/src/main/kotlin/arrow/effects/MonoKConnection.kt @@ -6,6 +6,7 @@ import arrow.effects.typeclasses.MonadDefer typealias MonoKConnection = KindConnection typealias MonoKProc = (MonoKConnection, (Either) -> Unit) -> Unit +typealias MonoKProcF = (MonoKConnection, (Either) -> Unit) -> MonoKOf /** * Connection for [MonoK]. diff --git a/modules/effects/arrow-effects-rx2-instances/src/main/kotlin/arrow/effects/FlowableKInstances.kt b/modules/effects/arrow-effects-rx2-instances/src/main/kotlin/arrow/effects/FlowableKInstances.kt index db42975e409..b3fcef5a73f 100644 --- a/modules/effects/arrow-effects-rx2-instances/src/main/kotlin/arrow/effects/FlowableKInstances.kt +++ b/modules/effects/arrow-effects-rx2-instances/src/main/kotlin/arrow/effects/FlowableKInstances.kt @@ -44,7 +44,7 @@ interface FlowableKMonadInstance : Monad { override fun FlowableKOf.map(f: (A) -> B): FlowableK = fix().map(f) - override fun tailRecM(a: A, f: kotlin.Function1>>): FlowableK = + override fun tailRecM(a: A, f: kotlin.Function1>>): FlowableK = FlowableK.tailRecM(a, f) override fun just(a: A): FlowableK = @@ -121,6 +121,9 @@ interface FlowableKAsyncInstance : override fun async(fa: Proc): FlowableK = FlowableK.async({ _, cb -> fa(cb) }, BS()) + override fun asyncF(k: ProcF): FlowableKOf = + FlowableK.asyncF({ _, cb -> k(cb) }, BS()) + override fun FlowableKOf.continueOn(ctx: CoroutineContext): FlowableK = fix().continueOn(ctx) } diff --git a/modules/effects/arrow-effects-rx2-instances/src/main/kotlin/arrow/effects/MaybeKInstances.kt b/modules/effects/arrow-effects-rx2-instances/src/main/kotlin/arrow/effects/MaybeKInstances.kt index e18b1a11588..6f042ac8517 100644 --- a/modules/effects/arrow-effects-rx2-instances/src/main/kotlin/arrow/effects/MaybeKInstances.kt +++ b/modules/effects/arrow-effects-rx2-instances/src/main/kotlin/arrow/effects/MaybeKInstances.kt @@ -108,6 +108,9 @@ interface MaybeKAsyncInstance : Async, MaybeKMonadDeferInstance { override fun async(fa: Proc): MaybeK = MaybeK.async { _, cb -> fa(cb) } + override fun asyncF(k: ProcF): MaybeK = + MaybeK.asyncF { _, cb -> k(cb) } + override fun MaybeKOf.continueOn(ctx: CoroutineContext): MaybeK = fix().continueOn(ctx) } diff --git a/modules/effects/arrow-effects-rx2-instances/src/main/kotlin/arrow/effects/ObservableKInstances.kt b/modules/effects/arrow-effects-rx2-instances/src/main/kotlin/arrow/effects/ObservableKInstances.kt index de64afd2e63..4605e8c71a4 100644 --- a/modules/effects/arrow-effects-rx2-instances/src/main/kotlin/arrow/effects/ObservableKInstances.kt +++ b/modules/effects/arrow-effects-rx2-instances/src/main/kotlin/arrow/effects/ObservableKInstances.kt @@ -6,14 +6,7 @@ import arrow.core.Eval import arrow.deprecation.ExtensionsDSLDeprecated import arrow.effects.observablek.monad.monad import arrow.effects.observablek.monadError.monadError -import arrow.effects.typeclasses.Async -import arrow.effects.typeclasses.Bracket -import arrow.effects.typeclasses.ConcurrentEffect -import arrow.effects.typeclasses.Disposable -import arrow.effects.typeclasses.Effect -import arrow.effects.typeclasses.ExitCase -import arrow.effects.typeclasses.MonadDefer -import arrow.effects.typeclasses.Proc +import arrow.effects.typeclasses.* import arrow.extension import arrow.typeclasses.* import kotlin.coroutines.CoroutineContext @@ -47,7 +40,7 @@ interface ObservableKMonadInstance : Monad { override fun ObservableKOf.map(f: (A) -> B): ObservableK = fix().map(f) - override fun tailRecM(a: A, f: kotlin.Function1>>): ObservableK = + override fun tailRecM(a: A, f: kotlin.Function1>>): ObservableK = ObservableK.tailRecM(a, f) override fun just(a: A): ObservableK = @@ -120,6 +113,9 @@ interface ObservableKAsyncInstance : Async, ObservableKMonadDefe override fun async(fa: Proc): ObservableK = ObservableK.async { _, cb -> fa(cb) } + override fun asyncF(k: ProcF): ObservableK = + ObservableK.asyncF { _, cb -> k(cb) } + override fun ObservableKOf.continueOn(ctx: CoroutineContext): ObservableK = fix().continueOn(ctx) } diff --git a/modules/effects/arrow-effects-rx2-instances/src/main/kotlin/arrow/effects/SingleKInstances.kt b/modules/effects/arrow-effects-rx2-instances/src/main/kotlin/arrow/effects/SingleKInstances.kt index 8a00377a8fa..d15252d27dc 100644 --- a/modules/effects/arrow-effects-rx2-instances/src/main/kotlin/arrow/effects/SingleKInstances.kt +++ b/modules/effects/arrow-effects-rx2-instances/src/main/kotlin/arrow/effects/SingleKInstances.kt @@ -87,6 +87,9 @@ interface SingleKAsyncInstance : override fun async(fa: Proc): SingleK = SingleK.async { _, cb -> fa(cb) } + override fun asyncF(k: ProcF): SingleK = + SingleK.asyncF { _, cb -> k(cb) } + override fun SingleKOf.continueOn(ctx: CoroutineContext): SingleK = fix().continueOn(ctx) } diff --git a/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/FlowableK.kt b/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/FlowableK.kt index 23edee0646c..d3c4d6b7d8c 100644 --- a/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/FlowableK.kt +++ b/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/FlowableK.kt @@ -9,6 +9,7 @@ import arrow.higherkind import arrow.typeclasses.Applicative import io.reactivex.BackpressureStrategy import io.reactivex.Flowable +import io.reactivex.FlowableEmitter import kotlin.coroutines.CoroutineContext fun Flowable.k(): FlowableK = FlowableK(this) @@ -194,6 +195,24 @@ data class FlowableK(val flowable: Flowable) : FlowableKOf, FlowableKKi } }, mode).k() + fun asyncF(fa: FlowableKProcF, mode: BackpressureStrategy = BackpressureStrategy.BUFFER): FlowableK = + Flowable.create({ emitter: FlowableEmitter -> + val conn = FlowableKConnection() + //On disposing of the upstream stream this will be called by `setCancellable` so check if upstream is already disposed or not because + //on disposing the stream will already be in a terminated state at this point so calling onError, in a terminated state, will blow everything up. + conn.push(FlowableK { if (!emitter.isCancelled) emitter.onError(ConnectionCancellationException) }) + emitter.setCancellable { conn.cancel().value().subscribe() } + + fa(conn) { either: Either -> + either.fold({ + emitter.onError(it) + }, { + emitter.onNext(it) + emitter.onComplete() + }) + }.fix().flowable.subscribe({}, emitter::onError) + }, mode).k() + tailrec fun tailRecM(a: A, f: (A) -> FlowableKOf>): FlowableK { val either = f(a).value().blockingFirst() return when (either) { diff --git a/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/FlowableKConnection.kt b/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/FlowableKConnection.kt index 2ef3995ffe2..a05c0245c02 100644 --- a/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/FlowableKConnection.kt +++ b/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/FlowableKConnection.kt @@ -6,6 +6,7 @@ import arrow.effects.typeclasses.MonadDefer typealias FlowableKConnection = KindConnection typealias FlowableKProc = (FlowableKConnection, (Either) -> Unit) -> Unit +typealias FlowableKProcF = (FlowableKConnection, (Either) -> Unit) -> FlowableKOf /** * Connection for [FlowableK]. diff --git a/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/MaybeK.kt b/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/MaybeK.kt index e32b0a0c90a..45a638d9ff4 100644 --- a/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/MaybeK.kt +++ b/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/MaybeK.kt @@ -5,6 +5,7 @@ import arrow.effects.CoroutineContextRx2Scheduler.asScheduler import arrow.effects.typeclasses.ExitCase import arrow.higherkind import io.reactivex.Maybe +import io.reactivex.MaybeEmitter import kotlin.coroutines.CoroutineContext fun Maybe.k(): MaybeK = MaybeK(this) @@ -177,6 +178,23 @@ data class MaybeK(val maybe: Maybe) : MaybeKOf, MaybeKKindedJ { } }.k() + fun asyncF(fa: MaybeKProcF): MaybeK = + Maybe.create { emitter: MaybeEmitter -> + val conn = MaybeKConnection() + //On disposing of the upstream stream this will be called by `setCancellable` so check if upstream is already disposed or not because + //on disposing the stream will already be in a terminated state at this point so calling onError, in a terminated state, will blow everything up. + conn.push(MaybeK { if (!emitter.isDisposed) emitter.onError(ConnectionCancellationException) }) + emitter.setCancellable { conn.cancel().value().subscribe() } + + fa(conn) { either: Either -> + either.fold({ + emitter.onError(it) + }, { + emitter.onSuccess(it) + }) + }.fix().maybe.subscribe({}, emitter::onError) + }.k() + tailrec fun tailRecM(a: A, f: (A) -> MaybeKOf>): MaybeK { val either = f(a).value().blockingGet() return when (either) { diff --git a/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/MaybeKConnection.kt b/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/MaybeKConnection.kt index 8cd5e8a6655..7c8dd1809c8 100644 --- a/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/MaybeKConnection.kt +++ b/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/MaybeKConnection.kt @@ -1,11 +1,13 @@ package arrow.effects +import arrow.Kind import arrow.core.Either import arrow.effects.typeclasses.ExitCase import arrow.effects.typeclasses.MonadDefer typealias MaybeKConnection = KindConnection typealias MaybeKProc = (MaybeKConnection, (Either) -> Unit) -> Unit +typealias MaybeKProcF = (MaybeKConnection, (Either) -> Unit) -> Kind /** * Connection for [MaybeK]. diff --git a/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/ObservableK.kt b/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/ObservableK.kt index 45e658a6636..d3d7a9448fb 100644 --- a/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/ObservableK.kt +++ b/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/ObservableK.kt @@ -3,11 +3,13 @@ package arrow.effects import arrow.Kind import arrow.core.* import arrow.effects.CoroutineContextRx2Scheduler.asScheduler +import arrow.effects.typeclasses.Disposable +import arrow.effects.typeclasses.ExitCase import arrow.effects.typeclasses.* import arrow.higherkind import arrow.typeclasses.Applicative import io.reactivex.Observable -import io.reactivex.schedulers.Schedulers +import io.reactivex.ObservableEmitter import kotlin.coroutines.CoroutineContext fun Observable.k(): ObservableK = ObservableK(this) @@ -180,9 +182,7 @@ data class ObservableK(val observable: Observable) : ObservableKOf, Obs //On disposing of the upstream stream this will be called by `setCancellable` so check if upstream is already disposed or not because //on disposing the stream will already be in a terminated state at this point so calling onError, in a terminated state, will blow everything up. connection.push(ObservableK { if (!emitter.isDisposed) emitter.onError(ConnectionCancellationException) }) - emitter.setCancellable { - connection.cancel().value().observeOn(Schedulers.computation()).subscribe({}, {}) - } + emitter.setCancellable { connection.cancel().value().subscribe({}, {}) } fa(connection) { either: Either -> either.fold({ @@ -194,6 +194,24 @@ data class ObservableK(val observable: Observable) : ObservableKOf, Obs } }.k() + fun asyncF(fa: ObservableKProcF): ObservableK = + Observable.create { emitter: ObservableEmitter -> + val connection = ObservableKConnection() + //On disposing of the upstream stream this will be called by `setCancellable` so check if upstream is already disposed or not because + //on disposing the stream will already be in a terminated state at this point so calling onError, in a terminated state, will blow everything up. + connection.push(ObservableK { if (!emitter.isDisposed) emitter.onError(ConnectionCancellationException) }) + emitter.setCancellable { connection.cancel().value().subscribe({}, {}) } + + fa(connection) { either: Either -> + either.fold({ + emitter.onError(it) + }, { + emitter.onNext(it) + emitter.onComplete() + }) + }.fix().observable.subscribe({}, emitter::onError) + }.k() + tailrec fun tailRecM(a: A, f: (A) -> ObservableKOf>): ObservableK { val either = f(a).value().blockingFirst() return when (either) { diff --git a/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/ObservableKConnection.kt b/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/ObservableKConnection.kt index cc0b85154d2..dadfd0cb5e5 100644 --- a/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/ObservableKConnection.kt +++ b/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/ObservableKConnection.kt @@ -4,8 +4,9 @@ import arrow.core.Either import arrow.effects.typeclasses.ExitCase import arrow.effects.typeclasses.MonadDefer -typealias ObservableKProc = (ObservableKConnection, (Either) -> Unit) -> Unit typealias ObservableKConnection = KindConnection +typealias ObservableKProc = (ObservableKConnection, (Either) -> Unit) -> Unit +typealias ObservableKProcF = (ObservableKConnection, (Either) -> Unit) -> ObservableKOf /** * Connection for [ObservableK]. diff --git a/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/SingleK.kt b/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/SingleK.kt index ebf48c1af42..a58219a8c18 100644 --- a/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/SingleK.kt +++ b/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/SingleK.kt @@ -8,6 +8,7 @@ import arrow.effects.typeclasses.Disposable import arrow.effects.typeclasses.ExitCase import arrow.higherkind import io.reactivex.Single +import io.reactivex.SingleEmitter import kotlin.coroutines.CoroutineContext fun Single.k(): SingleK = SingleK(this) @@ -168,6 +169,23 @@ data class SingleK(val single: Single) : SingleKOf, SingleKKindedJ { } }.k() + fun asyncF(fa: SingleKProcF): SingleK = + Single.create { emitter: SingleEmitter -> + val conn = SingleKConnection() + //On disposing of the upstream stream this will be called by `setCancellable` so check if upstream is already disposed or not because + //on disposing the stream will already be in a terminated state at this point so calling onError, in a terminated state, will blow everything up. + conn.push(SingleK { if (!emitter.isDisposed) emitter.onError(ConnectionCancellationException) }) + emitter.setCancellable { conn.cancel().value().subscribe() } + + fa(conn) { either: Either -> + either.fold({ + emitter.onError(it) + }, { + emitter.onSuccess(it) + }) + }.fix().single.subscribe({}, emitter::onError) + }.k() + tailrec fun tailRecM(a: A, f: (A) -> SingleKOf>): SingleK { val either = f(a).value().blockingGet() return when (either) { diff --git a/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/SingleKConnection.kt b/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/SingleKConnection.kt index de433fa23ff..95f0842e27a 100644 --- a/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/SingleKConnection.kt +++ b/modules/effects/arrow-effects-rx2/src/main/kotlin/arrow/effects/SingleKConnection.kt @@ -6,6 +6,7 @@ import arrow.effects.typeclasses.MonadDefer typealias SingleKConnection = KindConnection typealias SingleKProc = (SingleKConnection, (Either) -> Unit) -> Unit +typealias SingleKProcF = (SingleKConnection, (Either) -> Unit) -> SingleKOf /** * Connection for [SingleK]. diff --git a/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/IO.kt b/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/IO.kt index 914a0aacb39..5135c304f00 100644 --- a/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/IO.kt +++ b/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/IO.kt @@ -15,7 +15,10 @@ import arrow.higherkind import kotlin.coroutines.CoroutineContext typealias IOProc = (IOConnection, (Either) -> Unit) -> Unit -fun Proc.toIOProc(): IOProc = { _: IOConnection, proc -> this(proc) } +typealias IOProcF = (IOConnection, (Either) -> Unit) -> IOOf + +fun Proc.toIOProc(): IOProc = { _: IOConnection, proc -> this(proc) } +fun ProcF.toIOProcF(): IOProcF = { _: IOConnection, proc -> this(proc) } @higherkind sealed class IO : IOOf { @@ -41,6 +44,21 @@ sealed class IO : IOOf { } } + fun asyncF(k: IOProcF): IO = + Async { conn: IOConnection, ff: (Either) -> Unit -> + val conn2 = IOConnection() + conn.push(conn2.cancel()) + onceOnly(conn, ff).let { callback: (Either) -> Unit -> + val fa = try { + k(conn2, callback) + } catch (t: Throwable) { + IO { callback(Left(t)) } + } + + IORunLoop.startCancelable(fa, conn2) { Unit } + } + } + operator fun invoke(ctx: CoroutineContext, f: () -> A): IO = IO.unit.continueOn(ctx).flatMap { invoke(f) } @@ -117,15 +135,15 @@ sealed class IO : IOOf { */ fun uncancelable(): IO = IOCancel.uncancelable(this) - fun bracket(release: (A) -> IO, use: (A) -> IO): IO = + fun bracket(release: (A) -> IOOf, use: (A) -> IOOf): IO = bracketCase({ a, _ -> release(a) }, use) fun bracketCase(release: (A, ExitCase) -> IOOf, use: (A) -> IOOf): IO = IOBracket(this, release, use) - fun guarantee(finalizer: IO): IO = guaranteeCase { finalizer } + fun guarantee(finalizer: IOOf): IO = guaranteeCase { finalizer } - fun guaranteeCase(finalizer: (ExitCase) -> IO): IO = + fun guaranteeCase(finalizer: (ExitCase) -> IOOf): IO = IOBracket.guaranteeCase(this, finalizer) internal data class Pure(val a: A) : IO() { diff --git a/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/IORunLoop.kt b/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/IORunLoop.kt index 0b34510adbc..b1770909469 100644 --- a/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/IORunLoop.kt +++ b/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/IORunLoop.kt @@ -16,14 +16,14 @@ private typealias Callback = (Either) -> Unit @Suppress("UNCHECKED_CAST") internal object IORunLoop { - fun start(source: IO, cb: (Either) -> Unit): Unit = + fun start(source: IOOf, cb: (Either) -> Unit): Unit = loop(source, IOConnection.uncancelable, cb as Callback, null, null, null) /** * Evaluates the given `IO` reference, calling the given callback * with the result when completed. */ - fun startCancelable(source: IO, conn: IOConnection, cb: (Either) -> Unit): Unit = + fun startCancelable(source: IOOf, conn: IOConnection, cb: (Either) -> Unit): Unit = loop(source, conn, cb as Callback, null, null, null) fun step(source: IO): IO { diff --git a/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/internal/ForwardCancelable.kt b/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/internal/ForwardCancelable.kt index 913053ec096..984833ba559 100644 --- a/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/internal/ForwardCancelable.kt +++ b/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/internal/ForwardCancelable.kt @@ -8,7 +8,7 @@ import java.util.concurrent.atomic.AtomicReference /** * A placeholder for a [CancelToken] that will be set at a later time, the equivalent of a - * `Deferred[IO, CancelToken]`. Used in the implementation of `bracket`, see [IOBracket]. + * `Promise>`. Used in the implementation of `bracket`, see [IOBracket]. */ class ForwardCancelable { diff --git a/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/internal/IOBracket.kt b/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/internal/IOBracket.kt index 5520b90dbe0..b9856a8a0a6 100644 --- a/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/internal/IOBracket.kt +++ b/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/internal/IOBracket.kt @@ -10,8 +10,8 @@ internal object IOBracket { /** * Implementation for `IO.bracketCase`. */ - operator fun invoke(acquire: IO, release: (A, ExitCase) -> IOOf, use: (A) -> IOOf): IO = - IO.Async { conn, cb -> + operator fun invoke(acquire: IOOf, release: (A, ExitCase) -> IOOf, use: (A) -> IOOf): IO = + IO.async { conn, cb -> // Placeholder for the future finalizer val deferredRelease = ForwardCancelable() conn.push(deferredRelease.cancel()) @@ -65,9 +65,9 @@ internal object IOBracket { } fb.fix().flatMap(frame) } + // Registering our cancelable token ensures that in case cancellation is detected, release gets called deferredRelease.complete(frame.cancel) - // Actual execution IORunLoop.startCancelable(onNext(), conn, cb) } @@ -77,8 +77,8 @@ internal object IOBracket { } } - fun guaranteeCase(source: IO, release: (ExitCase) -> IO): IO = - IO.Async { conn, cb -> + fun guaranteeCase(source: IO, release: (ExitCase) -> IOOf): IO = + IO.async { conn, cb -> // TODO on cats-effect all this block is run using an immediate ExecutionContext for stack safety. val frame = EnsureReleaseFrame(release) @@ -102,7 +102,7 @@ internal object IOBracket { releaseFn(a, c) } - private class EnsureReleaseFrame(val releaseFn: (ExitCase) -> IO) : BaseReleaseFrame() { + private class EnsureReleaseFrame(val releaseFn: (ExitCase) -> IOOf) : BaseReleaseFrame() { override fun release(c: ExitCase): CancelToken = releaseFn(c) } diff --git a/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/internal/ParallelUtils.kt b/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/internal/ParallelUtils.kt index 9b35e4ad980..3a289980c36 100644 --- a/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/internal/ParallelUtils.kt +++ b/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/internal/ParallelUtils.kt @@ -207,7 +207,7 @@ private sealed class Treither { abstract fun fold(fa: (A) -> D, fb: (B) -> D, fc: (C) -> D): D } -private fun asyncIOContinuation(ctx: CoroutineContext, cc: (Either) -> Unit): AContinuation = +internal fun asyncIOContinuation(ctx: CoroutineContext, cc: (Either) -> Unit): AContinuation = object : AContinuation { override val context: CoroutineContext = ctx diff --git a/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/internal/Promise.kt b/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/internal/Promise.kt new file mode 100644 index 00000000000..80593f1d8c0 --- /dev/null +++ b/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/internal/Promise.kt @@ -0,0 +1,103 @@ +package arrow.effects.internal + +import arrow.Kind +import arrow.core.* +import arrow.effects.Promise +import java.util.concurrent.atomic.AtomicReference + +fun Promise.Companion.unsafe(): Promise = UnsafePromise() + +internal class UnsafePromise : Promise { + + private val state: AtomicReference> = AtomicReference(State.Pending(emptyList())) + + override val get: Id + get() { + tailrec fun loop(): Id = when (val st = state.get()) { + is State.Pending -> loop() + is State.Full -> Id(st.value) + is State.Error -> throw st.throwable + } + + calculateNewGetState() + return loop() + } + + private tailrec fun calculateNewGetState(): Unit { + val oldState = state.get() + val newState = when (oldState) { + is State.Pending -> State.Pending(oldState.joiners) + is State.Full -> oldState + is State.Error -> oldState + } + return if (state.compareAndSet(oldState, newState)) Unit else calculateNewGetState() + } + + override val tryGet: Kind> + get() = when (val oldState = state.get()) { + is State.Pending -> Id(None) + is State.Full -> Id(Some(oldState.value)) + is State.Error -> Id(None) + } + + override fun tryComplete(a: A): Id { + val oldState = state.get() + return when (oldState) { + is State.Pending -> { + calculateNewTryCompleteState(a) + Id(true) + } + is State.Full -> Id(false) + is State.Error -> Id(false) + } + } + + private tailrec fun calculateNewTryCompleteState(a: A): Unit { + val oldState = state.get() + val newState = when (oldState) { + is State.Pending -> State.Full(a) + is State.Full -> oldState + is State.Error -> oldState + } + + return if (state.compareAndSet(oldState, newState)) Unit else calculateNewTryCompleteState(a) + } + + override fun error(throwable: Throwable): Id = + throw throwable + + override fun tryError(throwable: Throwable): Id = + when (state.get()) { + is State.Pending -> throw throwable + is State.Full -> Id(false) + is State.Error -> Id(false) + } + + override fun complete(a: A): Id { + when (state.get()) { + is State.Pending -> calculateNewCompleteState(a) + is State.Full -> throw Promise.AlreadyFulfilled + is State.Error -> throw Promise.AlreadyFulfilled + } + + return Id(Unit) + } + + private tailrec fun calculateNewCompleteState(a: A): Unit { + val oldState = state.get() + val newState = when (oldState) { + is State.Pending -> State.Full(a) + is State.Full -> oldState + is State.Error -> oldState + } + return if (state.compareAndSet(oldState, newState)) Unit else calculateNewCompleteState(a) + } + + internal sealed class State { + data class Pending(val joiners: List<(Either) -> Unit>) : State() + data class Full(val value: A) : State() + data class Error(val throwable: Throwable) : State() + } + +} + diff --git a/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/internal/Utils.kt b/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/internal/Utils.kt index 17bc4e8271d..5e6d2689033 100644 --- a/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/internal/Utils.kt +++ b/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/internal/Utils.kt @@ -5,6 +5,7 @@ import arrow.core.None import arrow.core.Option import arrow.core.Some import arrow.effects.IO +import arrow.effects.KindConnection import arrow.effects.typeclasses.Duration import java.util.* import java.util.concurrent.atomic.AtomicBoolean @@ -42,12 +43,13 @@ object Platform { } } - inline fun onceOnly(crossinline f: () -> Unit): () -> Unit { + inline fun onceOnly(conn: KindConnection, crossinline f: (A) -> Unit): (A) -> Unit { val wasCalled = AtomicBoolean(false) - return { + return { a -> if (!wasCalled.getAndSet(true)) { - f() + conn.pop() + f(a) } } } diff --git a/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/typeclasses/Async.kt b/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/typeclasses/Async.kt index b73cf8dc386..00afdea39e8 100644 --- a/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/typeclasses/Async.kt +++ b/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/typeclasses/Async.kt @@ -1,35 +1,378 @@ package arrow.effects.typeclasses import arrow.Kind +import arrow.core.* +import arrow.effects.CancelToken import arrow.core.Either -import arrow.effects.KindConnection +import arrow.documented import arrow.typeclasses.MonadContinuation +import java.util.concurrent.atomic.AtomicReference import kotlin.coroutines.CoroutineContext /** A cancellable asynchronous computation that might fail. **/ -typealias ProcF = (KindConnection, (Either) -> Unit) -> Unit +typealias ProcF = ((Either) -> Unit) -> Kind /** An asynchronous computation that might fail. **/ typealias Proc = ((Either) -> Unit) -> Unit /** * ank_macro_hierarchy(arrow.effects.typeclasses.Async) * - * The context required to run an asynchronous computation that may fail. + * [Async] models how a data type runs an asynchronous computation that may fail. + * Defined by the [Proc] signature, which is the consumption of a callback. **/ +@documented interface Async : MonadDefer { - fun async(fa: Proc): Kind + /** + * Creates an instance of [F] that executes an asynchronous process on evaluation. + * + * This combinator can be used to wrap callbacks or other similar impure code. + * + * @param fa an asynchronous computation that might fail typed as [Proc]. + * + * ```kotlin:ank:playground:extension:playground:extension + * _imports_ + * import java.lang.RuntimeException + * + * object GithubService { + * fun getUsernames(callback: (List?, Throwable?) -> Unit): Unit = + * callback(listOf("nomisRev", "raulraja", "pacoworks", "jorgecastilloprz"), null) + * } + * + * fun main(args: Array) { + * //sampleStart + * fun Async.getUsernames(): Kind> = + * async { cb: (Either>) -> Unit -> + * GithubService.getUsernames { names, throwable -> + * when { + * names != null -> cb(Right(names)) + * throwable != null -> cb(Left(throwable)) + * else -> cb(Left(RuntimeException("Null result and no exception"))) + * } + * } + * } + * + * val result = _extensionFactory_.getUsernames() + * //sampleEnd + * println(result) + * } + * ``` + * + * @see asyncF for a version that can suspend side effects in the registration function. + */ + fun async(fa: Proc): Kind = + asyncF { cb -> delay { fa(cb) } } + + /** + * [async] variant that can suspend side effects in the provided registration function. + * + * The passed in function is injected with a side-effectful callback for signaling the final result of an asynchronous process. + * + * ```kotlin:ank:playground:extension + * _imports_ + * import arrow.effects.typeclasses.Async + * + * fun main(args: Array) { + * //sampleStart + * fun Async.makeCompleteAndGetPromiseInAsync() = + * asyncF { cb: (Either) -> Unit -> + * Promise.uncancelable(this).flatMap { promise -> + * promise.complete("Hello World!").flatMap { + * promise.get.map { str -> cb(Right(str)) } + * } + * } + * } + * + * val result = _extensionFactory_.makeCompleteAndGetPromiseInAsync() + * //sampleEnd + * println(result) + * } + * ``` + * + * @see async for a simpler, non suspending version. + */ + fun asyncF(k: ProcF): Kind + + /** + * Continue the evaluation on provided [CoroutineContext] + * + * @param ctx [CoroutineContext] to run evaluation on + * + * ```kotlin:ank:playground:extension + * _imports_ + * import kotlinx.coroutines.Dispatchers + * + * fun main(args: Array) { + * //sampleStart + * fun Async.runOnDefaultDispatcher(): Kind = + * _just_(Unit)._continueOn_(Dispatchers.Default).flatMap { + * _delay_({ Thread.currentThread().name }) + * } + * + * val result = _extensionFactory_.runOnDefaultDispatcher() + * //sampleEnd + * println(result) + * } + * ``` + */ fun Kind.continueOn(ctx: CoroutineContext): Kind + /** + * Delay a computation on provided [CoroutineContext]. + * + * @param ctx [CoroutineContext] to run evaluation on. + * + * ```kotlin:ank:playground:extension + * _imports_ + * import kotlinx.coroutines.Dispatchers + * + * fun main(args: Array) { + * //sampleStart + * fun Async.invokeOnDefaultDispatcher(): Kind = + * _delay_(Dispatchers.Default, { Thread.currentThread().name }) + * + * val result = _extensionFactory_.invokeOnDefaultDispatcher() + * //sampleEnd + * println(result) + * } + * ``` + */ + fun delay(ctx: CoroutineContext, f: () -> A): Kind = + defer(ctx) { + try { + just(f()) + } catch (t: Throwable) { + raiseError(t) + } + } + + @Deprecated("Use delay instead", + ReplaceWith("delay(ctx, f)", "arrow.effects.typeclasses.Async")) operator fun invoke(ctx: CoroutineContext, f: () -> A): Kind = - lazy().continueOn(ctx).flatMap { delay(f) } + ctx.shift().flatMap { delay(f) } + /** + * Delay a computation on provided [CoroutineContext]. + * + * @param ctx [CoroutineContext] to run evaluation on. + * + * ```kotlin:ank:playground:extension + * _imports_ + * import kotlinx.coroutines.Dispatchers + * + * fun main(args: Array) { + * //sampleStart + * fun Async.invokeOnDefaultDispatcher(): Kind = + * _defer_(Dispatchers.Default, { delay { Thread.currentThread().name } }) + * + * val result = _extensionFactory_.invokeOnDefaultDispatcher().fix().unsafeRunSync() + * //sampleEnd + * println(result) + * } + * ``` + */ fun defer(ctx: CoroutineContext, f: () -> Kind): Kind = - lazy().continueOn(ctx).flatMap { defer(f) } + just(Unit).continueOn(ctx).flatMap { defer(f) } + /** + * Shift evaluation to provided [CoroutineContext]. + * + * @param ctx [CoroutineContext] to run evaluation on. + * + * ```kotlin:ank:playground + * import arrow.effects.* + * import arrow.effects.instances.io.async.async + * import kotlinx.coroutines.Dispatchers + * + * fun main(args: Array) { + * //sampleStart + * IO.async().run { + * val result = binding { + * continueOn(Dispatchers.Default) + * Thread.currentThread().name + * }.fix().unsafeRunSync() + * + * println(result) + * } + * //sampleEnd + * } + * ``` + */ suspend fun MonadContinuation.continueOn(ctx: CoroutineContext): Unit = - just(Unit).continueOn(ctx).bind() + ctx.shift().bind() + /** + * Shift evaluation to provided [CoroutineContext]. + * + * @receiver [CoroutineContext] to run evaluation on. + * + * ```kotlin:ank:playground:extension + * _imports_ + * import kotlinx.coroutines.Dispatchers + * + * fun main(args: Array) { + * //sampleStart + * _extensionFactory_.run { + * val result = Dispatchers.Default._shift_().map { + * Thread.currentThread().name + * } + * + * println(result) + * } + * //sampleEnd + * } + * ``` + */ + fun CoroutineContext.shift(): Kind = + delay(this) { Unit } + + /** + * Task that never finishes evaluating. + * + * ```kotlin:ank:playground:extension + * _imports_ + * + * fun main(args: Array) { + * //sampleStart + * val i = _extensionFactory_.never() + * + * println(i) + * //sampleEnd + * } + * ``` + */ fun never(): Kind = async { } + + /** + * Creates a cancelable [F] instance that executes an asynchronous process on evaluation. + * Derived from [async] and [bracketCase] so does not require [F] to be cancelable. + * + * **NOTE**: Only for a cancelable type can [bracketCase] ever call `cancel` but that's of no concern for + * non-cancelable types as `cancel` never should be called. + * + * ```kotlin:ank:playground:extension + * _imports_ + * _imports_monaddefer_ + * + * import kotlinx.coroutines.Dispatchers.Default + * import kotlinx.coroutines.async + * import kotlinx.coroutines.GlobalScope + * + * object Account + * + * //Some impure API or code + * class NetworkService { + * fun getAccounts( + * successCallback: (List) -> Unit, + * failureCallback: (Throwable) -> Unit) { + * + * GlobalScope.async(Default) { + * println("Making API call") + * kotlinx.coroutines.delay(500) + * successCallback(listOf(Account)) + * } + * } + * + * fun cancel(): Unit = kotlinx.coroutines.runBlocking { + * println("Cancelled, closing NetworkApi") + * kotlinx.coroutines.delay(500) + * println("Closed NetworkApi") + * } + * } + * + * fun main(args: Array) { + * //sampleStart + * val getAccounts = Default._shift_().flatMap { + * _extensionFactory_.cancelable> { cb -> + * val service = NetworkService() + * service.getAccounts( + * successCallback = { accs -> cb(Right(accs)) }, + * failureCallback = { e -> cb(Left(e)) }) + * + * _delay_({ service.cancel() }) + * } + * } + * + * + * //sampleEnd + * } + * ``` + * @see cancelableF for a version that can safely suspend impure callback registration code. + * F.asyncF[A] { cb => + */ + fun cancelable(k: ((Either) -> Unit) -> CancelToken): Kind = + cancelableF { cb -> delay { k(cb) } } + + /** + * Creates a cancelable [F] instance that executes an asynchronous process on evaluation. + * Derived from [async] and [bracketCase] so does not require [F] to be cancelable. + * + * **NOTE**: Only for a cancelable type can [bracketCase] ever call `cancel` but that's of no concern for + * non-cancelable types as `cancel` never should be called. + * + * ```kotlin:ank:playground:extension + * _imports_ + * _imports_monaddefer_ + * import kotlinx.coroutines.async + * + * fun main(args: Array) { + * //sampleStart + * val result = _extensionFactory_.cancelableF { cb -> + * delay { + * val deferred = kotlinx.coroutines.GlobalScope.async { + * kotlinx.coroutines.delay(1000) + * cb(Right("Hello from ${Thread.currentThread().name}")) + * } + * + * delay({ deferred.cancel().let { Unit } }) + * } + * } + * + * println(result) //Run with `fix().unsafeRunSync()` + * + * val result2 = _extensionFactory_.cancelableF { cb -> + * delay { + * println("Doing something that can be cancelled.") + * delay({ println("Cancelling the task") }) + * } + * } + * + * println(result2) //Run with `fix().unsafeRunAsyncCancellable { }.invoke()` + * //sampleEnd + * } + * ``` + * + * @see cancelable for a simpler non-suspending version. + */ + fun cancelableF(k: ((Either) -> Unit) -> Kind>): Kind = + asyncF { cb -> + val state = AtomicReference<(Either) -> Unit>(null) + val cb1 = { r: Either -> + try { + cb(r) + } finally { + if (!state.compareAndSet(null, mapUnit)) { + val cb2 = state.get() + state.lazySet(null) + cb2(rightUnit) + } + } + } + + k(cb1).bracketCase(use = { + async { cb -> + if (!state.compareAndSet(null, cb)) cb(rightUnit) + } + }, release = { token, exitCase -> + when (exitCase) { + is ExitCase.Cancelled -> token + else -> just(Unit) + } + }) + } + } + +internal val mapUnit: (Any?) -> Unit = { Unit } +internal val rightUnit = Right(Unit) diff --git a/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/typeclasses/Effect.kt b/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/typeclasses/Effect.kt index fcb6b4afaf4..dbca984b60d 100644 --- a/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/typeclasses/Effect.kt +++ b/modules/effects/arrow-effects/src/main/kotlin/arrow/effects/typeclasses/Effect.kt @@ -2,10 +2,18 @@ package arrow.effects.typeclasses import arrow.Kind import arrow.core.Either +import arrow.effects.IO /** * ank_macro_hierarchy(arrow.effects.typeclasses.Effect) */ interface Effect : Async { fun Kind.runAsync(cb: (Either) -> Kind): Kind + + fun Kind.toIO(): IO = IO.async { ioConnection, cb -> + runAsync { r -> + cb(r).just() + } + } + } diff --git a/modules/effects/arrow-effects/src/test/kotlin/arrow/effects/PromiseTest.kt b/modules/effects/arrow-effects/src/test/kotlin/arrow/effects/PromiseTest.kt index 52dc62fac2c..0a896bfa766 100644 --- a/modules/effects/arrow-effects/src/test/kotlin/arrow/effects/PromiseTest.kt +++ b/modules/effects/arrow-effects/src/test/kotlin/arrow/effects/PromiseTest.kt @@ -38,11 +38,9 @@ class PromiseTest : UnitSpec() { init { "tryGet before completing" { - forAll(Gen.int()) { i -> - promise().flatMap { p -> - p.tryGet - }.equalUnderTheLaw(IO.just(None), EQ()) - } + promise().flatMap { p -> + p.tryGet + }.equalUnderTheLaw(IO.just(None), EQ()) } "tryGet after completing" {