Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Arrow Fx #1263

Merged
merged 55 commits into from
Jan 27, 2019
Merged

Arrow Fx #1263

merged 55 commits into from
Jan 27, 2019

Conversation

raulraja
Copy link
Member

@raulraja raulraja commented Jan 23, 2019

Arrow Fx

Arrow Fx is a library that brings purity, referential transparency and direct syntax for effectful programs denoted as suspended functions.

Arrow Fx removes the need for all the combinators in the Functor hierarchy empowering programmers to express equivalent programs for all suspended capable runtimes like Kotlinx Coroutines, Reactor Framework, Rx2, etc bridging the gap of FP features a user must learn before
is productive defining algebras and interpreters.

The below program illustrates the Hello World of effectful programming with Arrow Fx:

Fx Hello World

Pure functions

val helloWorld: String =
  "Hello World"
  
helloWorld

Side effects

suspend fun sayHello(): Unit =
  println(helloWorld)

A suspend function denotes a suspended computation. All side effects should be suspended and non-evaluated in compositions to ensure purity and referential transparency of the effectful expressions.

Attempting to run a side effect in the pure environment is disallowed by the Kotlin compiler unless we prove that we know how to handle success and error outcomes from evaluating the effect once the suspension is resumed.

sayHello() //fails to compile

The Kotlin compiler won't allow side-effects denoted as suspend to run or compile until you provide a Continuation<A> that handles success and error cases.

Composing effects and pure expressions

import arrow.effects.extensions.io.fx.fx

fx {
  sayHello() // Does not compile because Arrow Fx forces uses to denote side effects in `effect` blocks
}
val x = sayHello() // Does not compile because suspended functions need to be in a continuation
import arrow.effects.extensions.io.fx.fx

val program = fx {
  effect { sayHello() } 
}

program

Executing effectful programs

All programs encapsulated in fx block yield a value at the end wrapped in the concurrent data type the user wishes to make its program concrete to.

Running effectful computations is restricted to runtime extensions of the Unsafe type class which IO provides extensions for.
Usage of unsafe is reserved to the end of the world and may be the only impure execution of a well-typed functional program:

import arrow.effects.unsafe
import arrow.effects.extensions.io.unsafeRun.runBlocking

unsafe { runBlocking { program } }

Arrow Fx makes emphasis in guaranteeing users understand where they are performing side effects in their program declaration.

Fx eliminates the need for F parameterized tagless style programs.

The following combinators illustrate how the Functor hierarchy functions are pointless in the environment given we can declare programs that respect the same semantics thanks to the Kotlin suspension system. These below are examples of a few of the most well known combinators that would disappear from your day to day FP programming and what others would look like flattened in the environment via automatic suspended binding after each combinator is applied in the suspended environment:

TypeClass Signature Arrow Fx signature
Functor.map just(1).map { it + 1 } 1 + 1
Applicative.just just(1) 1
Applicative.mapN mapN(just(1), just(2), ::Tuple2) 1 toT 2
Applicative.tupled tupled(just(1), just(2)) 1 toT 2
Monad.flatMap IO { 1 }.flatMap { n -> IO { n + 1 } } 1 + 1
Monad.flatten IO { IO { 1 } }.flatten() 1
MonadDefer.delay IO.delay { 1 } effect { 1 }
MonadDefer.defer IO.defer { IO { 1 } } effect { 1 }

Enhancing the ergonomics of Concurrent and Async FP Programs

The examples below demonstrates the expressive power that suspended functions and Arrow Fx brings to reactive programming by eliminating
effects nesting in a simple program that uses parallelism to process effectful computations in parallel and non-blocking.

Concurrent parMap

import arrow.effects.extensions.io.fx.fx
import arrow.effects.unsafe
import arrow.effects.extensions.io.unsafeRun.runBlocking
import kotlinx.coroutines.Dispatchers

/** A user declared side effect **/
//sampleStart
suspend fun getThreadName(): String =
  Thread.currentThread().name

val program = fx {
  // note how the receiving value is typed in the environment and not inside IO despite being effectful and
  // non-blocking parallel computations
  val result: List<String> = 
    Dispatchers.Default.parMap(
      { getThreadName() }, // each one of these runs in parallel
      { getThreadName() }
    ) { a, b -> 
      listOf(a, b) 
    }
}
//sampleEnd
fun main() {
  unsafe { runBlocking { program } }
}
  • Includes new ank operator ank:fail which prints errors in the markdown. /cc @Guardiola31337

# Conflicts:
#	modules/docs/arrow-docs/src/main/kotlin/EffectsHelper.kt
#	modules/effects/arrow-effects-data/src/test/kotlin/arrow/effects/IOTest.kt
#	modules/effects/arrow-effects-data/src/test/kotlin/arrow/effects/MVarTest.kt
#	modules/effects/arrow-effects-data/src/test/kotlin/arrow/effects/PromiseTest.kt
#	modules/effects/arrow-effects-extensions/src/test/kotlin/arrow/effects/RefTest.kt
# Conflicts:
#	modules/docs/arrow-examples/src/test/kotlin/arrow/DataTypeExamples.kt
# Conflicts:
#	modules/core/arrow-typeclasses/src/main/kotlin/arrow/typeclasses/MonadError.kt
# Conflicts:
#	modules/effects/arrow-effects-data/src/main/kotlin/arrow/effects/internal/IOBracket.kt
#	modules/effects/arrow-effects-data/src/main/kotlin/arrow/effects/typeclasses/Async.kt
#	modules/effects/arrow-effects-data/src/main/kotlin/arrow/effects/typeclasses/Concurrent.kt
Copy link
Member

@nomisRev nomisRev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Little confused with binding and fx. Do we want to keep both?

Still, need to review the docs. Awesome work! 🥇

@@ -165,7 +165,7 @@ object ConcurrentLaws {

fun <F> Concurrent<F>.cancelableFReceivesCancelSignal(EQ: Eq<Kind<F, Int>>, ctx: CoroutineContext) =
forAll(Gen.int()) { i ->
binding {
fx {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@raulraja correct me if I am wrong but binding does not include direct syntax or suspended syntax but binding is being deprecated.

import kotlin.coroutines.startCoroutine

interface MonadSyntax<F> : ApplicativeSyntax<F>, Monad<F> {
override fun <A> f(fa: suspend () -> A): Kind<F, A> =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't this named effect? Or could we name it to something that is more explicit about what it does? liftSuspend { }?

@raulraja
Copy link
Member Author

@pakoito @nomisRev @JorgeCastilloPrz I addressed all your comments. Let me know if there is anything else you want to discuss. We should probably discuss names over slack. The only one that is important to keep is the fx monadic block but the rest can be anything.

# Conflicts:
#	modules/docs/arrow-docs/docs/docs/effects/fiber/README.MD
#	modules/docs/arrow-docs/docs/docs/integrations/kotlinxcoroutines/README.md
#	modules/docs/arrow-docs/src/main/kotlin/EffectsHelper.kt
#	modules/effects/arrow-effects-data/src/test/kotlin/arrow/effects/PromiseTest.kt
#	modules/effects/arrow-effects-kotlinx-coroutines-data/src/test/kotlin/arrow/effects/coroutines/DeferredKTest.kt
#	modules/effects/arrow-effects-kotlinx-coroutines-extensions/src/main/kotlin/arrow/effects/coroutines/extensions/deferredk.kt
#	modules/effects/arrow-effects-rx2-data/src/main/kotlin/arrow/effects/rx2/FlowableK.kt
#	modules/effects/arrow-effects-rx2-data/src/main/kotlin/arrow/effects/rx2/MaybeK.kt
#	modules/effects/arrow-effects-rx2-data/src/main/kotlin/arrow/effects/rx2/ObservableK.kt
#	modules/effects/arrow-effects-rx2-data/src/main/kotlin/arrow/effects/rx2/SingleK.kt
Copy link
Member

@JorgeCastilloPrz JorgeCastilloPrz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good to me, thanks for clarifying the doubts. I've added a couple more comments but can't find a strong reason to not merge and iterate. Great job.

Copy link
Member

@pakoito pakoito left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't review the docs but I trust you :D

@raulraja raulraja merged commit bfc12f0 into master Jan 27, 2019
@raulraja raulraja deleted the rr-suspended-tagless branch January 27, 2019 00:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants