An overview and introduction to the Functional Reactive Programming paradigm, including Arrowised FRP, and a small peek at a simplified implementation of AFRP in Scala based on Monadic Stream Functions.
Given at Scala eXchange on 13th of December 2018.
1. Introduction to Functional Reactive
Programming with Scala
Diego E. Alonso
Scala eXchange 2018
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 1 / 29
2. What and Why?
What this talk is not about?
Any API or implementation of common Scala streaming libraries,
such as FS2, Scalaz-Stream, Monix, akka-streams
This talk is about
The Functional Reactive Programming paradigm, its inception,
concepts, arrows, a small implementation, relation to streams
Why do I care about FRP?
Unexpected applications for FP
FP lore: streams, monads, arrows
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 2 / 29
3. Reactive and Functional?
Transformational Programs are short-lived
Input
Program
Output
Takes all input before start
Starts and runs for some time
While running, neither takes input nor gives output
Terminates and give all output
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 3 / 29
4. Reactive and Functional?
Reactive Applications are Long-lived
Input
Program
Output
Runs for long time, never ends
Always takes new inputs
Always gives new output
Output depends on all past inputs
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 4 / 29
5. Reactive and Functional?
What is Functional Programming (simple answers)
Input
Program
Output
FP is programming with functions
Function: a computation that relates every input value to an
output value that is only determined by the input
Does not depend on mutable inner state
This denition looks very transformational
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 5 / 29
6. Reactive and Functional?
Reactive Applications are Stateful
Input
Program
Output
Does the past has real existence? Does the past exist
concretely? Is there somewhere or other a place, a world of
solid objects, where the past is still happening?
Past inputs only exists on the state
Present inputs changes that state
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 6 / 29
7. Reactive and Functional?
Reactive Applications care about timing
Input
Program
Output
Not just what inputs or outputs but also when
Output may feed back to input, in short time
But time is a side eect (clock)
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 7 / 29
8. Reactive and Functional?
What is Functional Programming?
FP is Declarative: what things are, not what they do
Denotational semantics: give meanings to programs
Domain: a world of ideas, or mathematical objects
An expression is just a name for an idea in domain
Compose meanings of composite expressions
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 8 / 29
9. Functional Reactive Programming
What does a Reactive Programming mean, functionally
Input
Program
Output
Entities in Domain can be innite or dense
We can see all input throug time as entity
All output of a program though run-time as one entity
A Reactive Program is map of input(t) to output(t)
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 9 / 29
10. Functional Reactive Programming
What's in a program in an FRP language?
X(t) +
Z(t)
F V (t)
G
Declarative approach to build reactive applications
An FRP program describes signals and signal functions
Signals are values that may vary over time
Signal functions show how signals depend on each other
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 10 / 29
11. Functional Reactive Programming
Interpretation of FRP program by sampling
X(t) +
Z(t)
F V (t)
G
To run an FRP program we need an interpreter
It builds a long stream of samples (with times)
Each signal function is run as a stream transformer
FRP splits time-sampling from continuous logic
Sample at the edge of the program
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 11 / 29
12. Arrowised Functional Reactive Programming
Simple FRP uses combinators of signals
X(t) +
Z(t)
F V (t)
G
Classic FRP works at the level of Signals: Sig[X]
Transform value of signal using function
Combine values of xed independent signals
Use value of signal to build another signal
Use value of signal to control which other signal follows
These is done with operations of Monad typeclass
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 12 / 29
13. Arrowised Functional Reactive Programming
Arrowised FRP: functions instead of signals
X(t) +
Z(t)
F V (t)
G
Arrowised FRP works only at level of Signal Functions (SF)
In AFRP, you start from some basic SFs
You combine them using Arrow operations
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 13 / 29
14. Arrowised Functional Reactive Programming
The Arrow Type Class
X(t) +
Z(t)
F V (t)
G
Monad is class for types F[V ] of values V in eect F,
it gives operations to make F-values from F-values
Arrow is class for types A[I, O] of A-functions from I to O
with operations to transform A-functions into A-functions
Mix A-functions into an A-function on tuples or eithers
Compose A-functions into a single one
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 14 / 29
15. From Lists to Monadic Stream Functions
Starting from a Scala List
sealed trait List[T]
case class Nil[T]() extends List[T]
case class Cons(head: T, tail: List[T]
The data type marks end or continuation
Backed by a data structure
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 15 / 29
16. From Lists to Monadic Stream Functions
Scala Lists as a trait with two methods
trait List[T] {
def head: Option[T]
def tail: Option[List[T]]
}
Methods need not be backed by data structure
The Option marks if it is the end of list
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 16 / 29
17. From Lists to Monadic Stream Functions
Lists as traits with a single methods
trait List[T] {
def uncons: Option[(T, List[T])]
}
trait InfList[T] {
def uncons: Id[(T, InfList[T])]
}
In eect, Option means may-terminate, and Id that not
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 17 / 29
18. From Lists to Monadic Stream Functions
Monadic Streams: generic sequence of eectful steps
trait MonSt[F[_], T] {
def uncons: F[(T, MonSt[F, T])]
}
Monadic streams are generic on the eect
The eect covers both the head and the tail of the stream
Instances of MonSt can include state, and pass new state in tail
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 18 / 29
19. Monadic Stream Functions
Denition of the Monadic Stream Functions trait
trait MoSF[F[_], In, Out] {
def apply(i: In): F[(Out, This)]
type This = MoSF[F, In, Out]
}
takes an input In, and runs computation in F
that returns value Out and a continuation
An instance of MoSF can have inner state
the instance of continuation has next state
Generalises lists, monadic streams, and sinks
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 19 / 29
20. Monadic Stream Functions
Basic and stateless MoSF
Basic constructors: make a MoSF[F, I, O] from
Value Type Function Type
Eectless pure O arr I β O
Eectful liftM F[O] arrM I β F[O]
pure is an MoSF that always returns given value
arr is an MoSF that always applies given function
liftM is an MoSF that always perform given F
arrM is an MoSF that applies given computation
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 20 / 29
21. Monadic Stream Functions
arr: return result of function
case class arr[F[_]: App, I, O]( fun: I = O)
extends MoSF[F, I, O] { self =
def apply(i: I): F[(O, This)] =
( fun(i) - self ).pure
}
case class liftM[F[_]: Functor, I, O](fo: F[O])
extends MoSF[F, I, O] { self =
def apply(i: I): F[(O, This)] =
fo.map( o = o - self )
}
// type This = MoSF[F, I, O]
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 21 / 29
22. Monadic Stream Functions
ap: merge input-wise two independent MoSF
def ap[F[_]: App, In, Mid, Out](
sffun: MoSF[F, In, Mid = O],
sfmid: MoSF[F, In, Mid]
): MoSF[F, In, Out] =
(i: In) = App[F].map2(sffun(i), sfmid(i)) {
case ( (fun, fkont), (arg, mkont) ) =
fun(arg) - ap(fkont, mkont)
}
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 22 / 29
23. Monadic Stream Functions
andThen: pass output to another MoSF
def andThen[F[_]: Monad, In, Mid, Out](
mosf: MoSF[F, In, Mid],
post: MoSF[F, Mid, Out]
): MoSF[F, In, Out] =
(i: In) = for {
(m, mosfK) - mosf(i)
(o, postK) - post(m)
} yield o - andThen(mosf, postK)
Continuation is composition of continuations
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 23 / 29
24. Monadic Stream Functions
rst: pair inputs and output of MoSF with extra
def first[F[_]: Functor, In, Out, eX](
mosf: MoSF[F, In, Out]
): MoSF[F, (In,eX), (Out,eX)] = {
case (i, x) =
mosf(i) map { case (o, cont) =
(o, x) - cont.first
}}
applies mosf to the rst element
pairs it with the second element
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 24 / 29
25. Monadic Stream Functions
left: handle case Left, pass Right through
case class left[F[_]: App, I, O, X](
mosf: MoSF[F, I, O]
) extends MoSF[F, I or X, O or X] { self =
def apply(iox: I or X): M[(O or X, This)] =
iox match {
case Right(x) =
(Right(x) - self).pure
case Left(i) = mosf(i).map {
case (o, cont) = Left(o) - cont.left
}
// type This = MoSF[I or X, O or X]
} // type or[A,B] = Either[A,B]
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 25 / 29
26. Monadic Stream Functions and Monadic Streams
Can we make a MoSF from Monadic Stream
trait MoSF[F[_], I, O] {
def apply(i: I): F[(O, MoSF[F, I, O])]
}
trait MoSt[G[_], T] {
def uncons: G[ (T, MoSt[G,T]) ]
}
// type MoSt[G[_], T] = MoSF[G, Unit, O]
// type MoSF[F[_], I, O] = MoSt[ ******* , *]
So, MoSt[G, T] is just a MoSF[G, Unit, T]
Can we make a MoSF on top of a MoST?
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 26 / 29
27. Monadic Stream Functions and Monadic Streams
YES, if we integrate the input into the eect of stream
trait MoSF[F[_], I, O] {
def apply(i: I): F[(O, MoSF[F, I, O])]
}
trait MoSt[G[_], T] {
def uncons: G[ (T, MoSt[G,T]) ]
}
// type MoSt[G[_], T] = MoSF[G, Unit, O]
// type MoSF[F[_], I, O] = MoSt[ Kl[F,I,?], O]
// type Kl[F[_], I, O] = I = F[O]
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 27 / 29
28. Summary
FRP is useful abstraction to describe reactive applications
Arrows is useful type-class for composing eectful computations
We can implement and use FRP in Scala
Next work: follow trend to do Monadic Stream Functions on FS2
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 28 / 29
29. Thank you to: I
Functional Reactive Programming, Refactored. Perez, Barenz,
Nilsson. Haskell Symposium, 2016.
Genuinely Functional User Interfaces. Courtney, Elliott.
The Essence and Origins of Functional Reactive Programming.
Elliott. LambdaJam 2015.
Diego E. Alonso Introduction to Functional Reactive Programming with Scala 29 / 29