0:01

So in this session we are going to look at a different take of

event handling programs, a functional view of events that's embodied in

functional reactive programming.

You've seen that reactive programming is all about reacting to some sequences of

events that happen in time.

0:19

The functional view is that we can actually take such a sequence of events

and aggregate it into a signal.

So, a signal is a value that changes over time, and

it's represented as a function from the time domain to the value domain.

0:35

That means that instead of propagating updates to mutable state one by one,

we define new signals in terms of existing ones.

In a single operation,

we can define a new signal in terms of signals that we have already defined.

So let's make this concretely as an example.

Let's say we want to track mouse positions or

here we'll substitute the mouse with my pen.

1:11

And it would have to handle that event by updating its internal state and

all these updates would typically be imperative.

How can I list this into a functional view point?

So, the core idea is that I define a signal,

call it also mousePosition, which is now a signal of position,

which at any point in time represents the current mouse position.

So, it's a function from the domain of time values to this curve.

At the initial time value the position was here, and then it would go

progressively until the fin, at the final time value the position was here.

That gives me a function from time values to positions.

Functional Reactive Programming started in 1997 with the paper

Functional Reactive Animation by Conal Elliot and Paul Hudak, and

Conal also wrote a, a language called Fran,

which was implemented as an embedded library in Haskell.

2:06

There have been quite a few FRP systems since, both standalone languages and

embedded libraries.

The list is too long to give you a, a complete picture, so

I just give you some examples.

Flapjax is one.

Elm, Bacon.js both target JavaScript.

React4J is a Java library that does a minimal reactive programming framework.

Related but

not quite the same are the event streaming data flow programming systems such as Rx.

In fact we will see Rx more in two weeks.

They're related to FRP but

commonly the FRP in the strict sense is not used for this.

2:45

We will introduce FRP not with one of the existing frameworks but with a really

minimal class, which we will define ourselves, which we call frp.signal, and

we'll explain the implementation of frp.signal at the end of the next module.

frp.signal is modeled after the library Scala.react, which is described

in the paper Deprecating the Observer Pattern by Ingo Maier and myself.

And in fact the React4J librariy's also influenced by the Scala.react library so

it has abstractions that are a bit similar to what we are going to see here.

So let's have a closer look at signals.

There are two fundamental operations over signal.

First, I can obtain the value of a signal at the current time.

In our frp.signal library that's expressed by appli,

applying the signal to an empty parameter list.

So mousePosition open parenths closed parenths would give us the mouse position

at the current time.

The second fundamental operation is to define a signal in terms of other signals.

In our library that's expressed by the signal constructor.

So let's do an example.

Let's say I have drawn my curve.

4:04

And I want to define a new signal which is either true or

false depending on whether the curve of the mouse was in the rectangle or not.

So that new signal would look something this,

would start with false and at this point in time it would jump to true and

it would stay true for a while and it would go back to false.

So that's false.

That's true.

4:33

Here's how I would define it.

I would define the signal inRectangle which takes its parameters the coordinates

of the rectangle given as a lower left corner and an upper right corner.

As it's defined by this expression here.

So what that says is that at each point in time I return the signal

that looks at the mouse position at the same point in time, at the current point

in time, and returns whether that position is between the lower left, and

the upper right corners.

So, we've seen the signal syntax to define the rectangle signal in terms of the mouse

position signal.

But it can also be used to define a signal that has no dependencies and

always defines the same value.

So, for instance, Signal(3) would define the signal that was constant three,

that was always the number three.

So we've constant signals, but how do we define the signal that varies in time.

Well, we've seen already some of these varying signals are defined externally,

something like mousePosition that the system could give us.

We could also map over the external defined signals that vary in time and

that gives us new signals that vary in time.

Or the other way is that we can use a var.

A var is a subclass of signal that we are going to see next.

So far, all values of type signal are immutable.

A signal is an immutable function from time to the signal values.

6:03

But in fact our library also defines a subclass Var of Signals for

signals that can be changed.

The change is done by means of an update operation, which Var provides.

And that updater operation allows to redefine the value

of a Var signal from the current time on.

So if we look at this example here, we define sig to be a Var 3,

so that's a signal that, for now, is always the constant 3,

until the point where I define an update operation on that signal.

From that point on, it will always be 5, until, of course,

there's a next update operation, maybe happening in the future.

6:56

You've probably known, seen it already when working with arrays.

For array arr, you would write arr(i) equals 0.

And what actually happens here is that this expression,

that this assignment is translated to arr.update(i, 0).

And that would call the update method

in class Array which has this definition here.

So, update takes an index and the value of the element type of the array and

returns unit.

So, under the covers,

when you write an index assignment like that you really can't call to update.

Generally, an indexed assignment like f of E1 to En equals E

is translated by the scala compiler to f.update E1, En, E.

And that works not just for arrays, but for

all types that define an update method of the right error pin.

And that also works if n equals 0.

So if there are no indices, that means that we call f open parenthesis,

close parens equals E is a shorthand for f.update(E).

So since we have such an update method on signal, it means that sig.update 5,

a call like that can be abbreviated to simply sig () equals 5.

You probably notice that signals of type Var look a bit like mutable variables,

where sig() is dereferencing, reading the variable.

And sig() equals new|Value is writing the variable corrupted.

But there's a crucial difference we can map over signals,

which gives us a relation between two signals that's maintained automatically,

at all future points in time.

Whereas for variables, no such mechanisms exists.

You have to update all variables manually whenever some dependent variable changes.

So, for instance, if we have a variable a,

8:53

initialized to 2 and then b would be 2 times a.

And then we would update a equals a plus 1.

And the value of b does not automatically get updated together with the value of a.

So, b would still be four even after that assignment, we'd have to up update it

manually to say while b is now again two times a, so that would give it six.

Where as if we take the same thing with signals, it would looks like this.

So we would have a signal which is assumed to be a var signal, constant two.

The signal b, it's assumed to be 2 times a and that,

9:36

assignment would establish already essentially the relationship between b and

a forever in the future.

So if now a is defined to be 3 then,

the signal b would be automatically updated to 6.

We're going to repeat now the BankAccount example we've seen in the last section

with signals.

We'll add a signal called balance to BankAccounts and we will define a function

consolidated which produces the sum of all balances of a given list of accounts.

So I have on screen my class BankAccount from

essentially the original example without any event rendering.

10:18

Deposit and withdraw method and this variable balance.

How do we make this into a source of an FRP signal?

Well, one approach would be to say well let's make balance a signal.

So, it would be a val, and that would be a Var of 0,

so our balance is still a variable, but now it's a signal.

So now, we say, well if the deposit method would update that signal,

let's just write it like this for now.

11:20

So let's test that with a worksheet.

I have written a worksheet accounts and

have given already the header of function consolidated which should return the sum

of all the balances of the accounts and just lists here, so.

So, the type of the consolidated would be then the signal event and

it's the finished end would be to define a signal and

the signal is defined by means of mapping over all our accounts for

each one we would take the balance.

12:07

So that gives us the function consolidated.

What we do now is we define,

as before, a number

of bank accounts.

[SOUND] Then we want to find out the total balance in consolidated so

the value of consolidated at the current time.

All right, so we get zero as expected.

Let's deposit some amount in A and try again.

12:57

Let me just bring that up.

Cyclic definition, cyclic signal definition, that's what it says.

So, what have we done wrong?

So, it must be in the bank account.

Let's bring that bank account up again.

So in fact the arrow appeared at this line here.

And if you look at this line then you must conclude that indeed it does,

makes no sense whatsoever.

What we've done here is to say, well, the signal balance which is a function over

time, is the same as itself plus amount, where amount is greater than 0.

So, obviously an equation like that has no solution.

You can't define a function that at each point in time,

is the value of the function itself plus amount.

So, that didn't work.

And, in fact, the system has called us out by

13:57

So how do we fix it?

Well, what we need to fix, what we need to do to fix it, is we have to pull out

the balance signal into a separate definition here that

pulls out the current value of balance and then just does this thing here.

14:31

So how is that different?

Well, what we do now is to say we take the current value of balance,

call it b, and then define the new balance after that to be that value plus amount.

So what we did, do now is not define a cyclic definition,

not to have a cyclic definition, but indeed define a constant function which

will return at all points in the future, the value of this expression here.

15:00

So you see that the interaction of state and

functions is very subtle as we have observed at several points before.

It makes an obvious difference but

the balance here is defined as the right hand side of a signal definition.

Or pulled out into its own [INAUDIBLE].

Let's redo the worksheet with that example.

15:22

And in fact, now we get, the correct result, the a deposit as 20.

Let's deposit as before 30 in, b.

Call it again.

And that would give, give us this here.

Let's go a little bit further and say we want to have another signal,

which defines an exchange rate.

Let's call

16:37

And now we would have a different Signal inDollar which has this value here.

Now if we changed, let's say, b again, b withdraw 10,

and look at the result inDollar.

Then you, then you see that the deduction in b is reflected in our inDollar results.

So at the first the signal c got updated, and

then the signal inDollar got updated, as well.

17:07

So that was the bank account example redone with signals instead of

subscribers.

If you compared the two solutions then you will notice that the solution with

signals is much shorter.

And you could also argue much cleaner because there is much less state updates

than in the observer solution, which is inherently imperative.

17:29

We've also seen in the example that there's an important difference

between variable assignments such as this one here, and

a signal update, such that the one that you see here.

In, in the first case that, in that variable assignment,

the new value of v is the old value of v plus 1.

So implicitly, there is a notion of old value vers, versus new values.

When you update a signal, there's no such notion.

So what you are trying to say here is that in fact, in fact that the signal s

is the same as the signal s plus 1, which obviously makes no sense.

18:06

So here's an exercise for you.

Consider those two code fragments.

The first one says num equals Var(1).

And we have a signal that is num times 2, and then we update num to be 2.

And the second one is quite similar, so we start with a var num equals Var(1).

The signal twice is as before.

But finally, we define num to be equal Var(2).

Are those two code fragments equivalent?

That means do, would they yield the same final value for twice?

So if I evaluate twice here and I evaluate twice here,

would I get the same value, yes or no?

18:48

So let's visualize how these two code fragments behave.

In the first case, I have the num signal,

which is constant 1.

And the twice signal, which is constant 2.

19:17

So that's the update of the first code fragment.

Let's have a look at the second one.

So again, I have num equals Var(1).

And twice is 2.

But what I do now is I define a new signal,

num, and assign it to the variable.

So after this point here, num in fact is,

points to a new signal that is has the value 2.

Whereas the twice signal, in fact, would still depend on this signal here

that I have created up here, so it would stay 2 forever.

So while I have here 2 as the final value, in the first code, my fragment, it was 4.

And the two fragments are indeed different.

It just shows a little bit the subtlety

that you have with signal update versus variables.

It gives you another aspect of that same difference.