JORDAN JOZWIAK: Hi, everyone.
Thanks for coming and welcome to this talk
on reactive programming and RxJava.
I'm Jordan.
I'm part of the Harvard class of 2014.
I was a social studies concentrator with a CS secondary,
and I was a CS50 TF for three years, but probably
before most if you took the class.
And most importantly, I lived in the best house, Mather House.
Just want to get that out there.
Post-college, I worked at DoubleDutch and ERO as an Android app developer.
And now, I'm at Google working on the API that help control car
functionality in Android Auto.
OK, so let's dive right in.
Reactive programming is a programming paradigm
that allows us to manipulate streams of data with functions.
And everything works asynchronously, meaning
that we don't have to wait for the result of operations.
But we'll talk more about this as we go.
So, what is reactive programming really?
Well, it's an approach problem solving, and it
focuses on three aspects, which were kind of conveyed in that sentence
before.
One, data streams.
So this is a flow of data objects that start somewhere and are
consumed elsewhere.
Functional programming, and you're used to using functions
in your normal style of imperative programming.
We'll get into that term later.
And so we use functions throughout programming,
but functional programming means using functions in a certain way.
And in this case, we're talking about applying functions
to manipulate a stream of data.
So for instance, we might combine multiple streams of data together.
Maybe we'd filter out some results, or we
might manipulate the data in some way to transform it to something else.
Functional programming is kind of a central focus topic for CS51,
so for those of you who are going to take that or will take it,
this will maybe give you a little bit of a primer for it.
And the last part is asynchronous observers.
So unlike synchronous programming, where one line of execute waits until--
well, we wait for one line to finish executing
before the next line executes.
With asynchronous observers, it means that our code has--
some parts of our code will execute at different times than other parts.
So if you're familiar with callbacks, maybe you
set an onClick listener for something and it
calls back later, that's asynchronous.
And with asynchronous observers, then we also
have these callbacks that happen later.
And we'll talk about all this more in a little bit.
So what are the actual applications of this?
Well, for instance, maybe we would want to retrieve data
from a database, filter out some results based on the user setting.
So one reason this would be a good example of reactive programming
is because we have those few elements here.
So for instance, we have a stream of data or some data coming out
of a database.
And because it's from a database, this is an operation
that might take a little bit of time.
Maybe we're making a request over a network.
Or even just sometimes, database queries can require some time
with joining and looking up in multiple tables.
So because it takes a little bit of time,
we would want to avoid blocking other operations.
And it would be a good case for handling this asynchronously.
We would also, in this case, want to manipulate
our data by filtering out some results.
So we might have some sort of filter function
that says we will exclude certain values.
Some other examples might include rendering a UI that combines data
from multiple data sources, creating a real-time model of stock prices,
showing auto-complete search results for a user,
and maybe logging data from a temperature, wind, or pressure sensor.
In each of these cases, there's some sort of data
and an opportunity for functional manipulation of that data stream.
And they might benefit from being handled asynchronously.
So they could probably be handled without reactive programming, as well,
but reactive programming makes these sort of problems easier to deal with.
And are there any questions on why these examples would
kind of fall in the bucket that I'm talking
about in terms of these three areas?
That's OK, there were plenty of time for questions later.
OK, we'll move on.
The reactive programming isn't just theoretical.
People actually use it.
It's great for mobile development, because there are many network
requests that shouldn't block the UI.
So we're frequently getting data from some server,
manipulating it, and showing it in the UI.
I know that Google uses it, in some cases,
for data requests, database requests.
And the reactive website, which we'll get into this more in a little bit,
also lists a number of companies that use it.
I know Netflix was an early adopter and has written some blog
posts on how it's helped them.
And reactive programming, when I've talked about it so far,
is kind of this theoretical idea.
We have these principles and reasons that we
might want to approach programming in a certain way.
But many languages don't have these concepts built in.
So maybe like Java, C, it doesn't necessarily
cater to this paradigm and way of solving problems in its own nature.
So we would need some sort of library that's
an extension on the language itself.
And anyone could implement a reactive extension library,
but the most popular one is reactivex.io specification,
and that's the one we'll be talking about today.
And there are implementations for a number of languages,
including Java Swift and JavaScript.
So I'll be focusing on RxJava today.
So let's go for an example of a data stream.
I've kind of tossed around this term, a flow of data, some events submitted.
And we'll actually look at an example.
So this line here represents a timeline of events.
Data will be emitted on this timeline of events,
and there don't have to be any data items for this stream to exist.
There could be nothing that ever happens.
Kind of pointless, but there could just be one or a million.
They could be emitted at any time and no regular intervals.
So, for instance, suppose we have some imaginary data
source that's emitting numbers.
And here, it emits 4, 2, 1, 7, 6 at kind of a regular interval.
And now, with that data stream, as these events were being emitted,
suppose that we had some function that could transform the data in some way.
So maybe this function is called filter.
And right here, we have the function filter
that takes an input x, which would be any one of these individual values.
And it will check if that value's even, because x mod 2 equals
0 will check if the value is even.
And so this will return true or false depending on the input of each value.
So if we apply this function to each data item,
only the even numbers will be in our output.
So 4 mod 2 equals 0, this is an even number.
2 is also an even number.
1 is not, so no event would be emitted there.
7 is not, but 6 is, so that one would also be emitted.
And these data streams could live anywhere.
So, for example, this stream emitting the events
could be some cloud application, some server.
And maybe we're subscribing to the events on a phone.
And this could be like a mobile device rendering a UI.
And the technical terms here to describe the stream that's emitting events
is called the subject.
So this is the one that's actually producing the events.
It could produce zero items, one, or millions.
In this case, it produces five.
And the stream at the end that's actually consuming the event
is called the observer.
And we aren't limited to just one observer.
This is one of the cool things about reactive programming,
is we can actually have multiple ones.
And I'll get into that in a little bit.
So in this simple example, to come back to it now that we know these terms,
there is a subject emitting data, a function manipulating the data,
and an observer consuming the output.
This idea is also similar to publish/subscribe,
or pub/sub, if you heard that before.
It's technically called the observer pattern,
because we have a subject producing data.
And we can have multiple observers, which
is one of the Gang of Four patterns, if you've heard of those before.
But no worries if you haven't.
And as I mentioned, we could have multiple observers.
So this one, maybe it uses the same function,
and this is just some other application, maybe a different element of the UI,
that's also taking in the same data input.
So in our example, we used a function called filter,
but there are many commonly built-in functions to reactive
extension specification, which should cover pretty much all your use cases.
So, for instance, there is also filter, map, count, max, min, delay,
and they're probably pretty self-explanatory.
Map, if you haven't heard that term before,
is a way of saying that we want to transform a data into a different type
of data or a different value.
So maybe we take in an integer and we return a string.
And that would be mapping data into something else.
So, basically, it's some function that takes some amount of time.
And a lot of these operations, like filter,
that'd be a very fast operation.
But it would be OK if we do some operation
they would take a bit more time.
Because as I mentioned, these are asynchronous operations.
So our observer doesn't necessarily expect the data to be emitted quickly.
It'll handle it whenever it actually gets the result.
And remember that there is no regular interval for the data items emitted.
They could have been at totally different times,
and there might not have been any events.
And our data event doesn't need to be numbers.
It could be strings, floats, complex objects, maybe coordinates,
or maybe some user data.
In this case, characters.
And that data is manipulated by some function that transforms the data.
It could even adjust the interval at which the data is output.
So, for instance, here, maybe the data--
the observer doesn't see the exact same number of events at the same time.
Notice here that it's shifted a little bit.
And perhaps that's because there is some sort
of like delay in that particular item.
But we can basically do any sort of transformation we want.
And as I mentioned, furthermore, there can
be multiple functions that can be chained together
between the subject and the observer.
So you might ask, why do we need multiple functions?
Can't you just have one function that does all the transformations
that you need all at once?
Well, at the core of it, it's because we don't
want our functions to be too complex.
Functions should do one thing.
And maybe to perform the final operation we want,
we want to do some combination of filtering out some values,
mapping some values, and maybe grouping things.
These are all operations that we should think of as separately
but we can chain together.
And so maybe this is an example output.
And what creates the subject data stream?
Because I've kind of been saying like the data comes from somewhere.
But something actually has to produce it with this idea of a stream in mind.
And these streams in our Java are called observables.
And don't worry about this observable nomenclature.
It's more important that you kind of understand
this high-level idea of reactive programming
rather than the specific terms used in RxJava and the implementation.
But when these streams are created, there's
just some create method or some fromIterable,
something that says we'll take in some input and start returning a stream.
And applying this sort of principle to existing object-oriented code
can be a little messy sometimes.
So that's part of the reason that it's usually
nice to kind of abstract that away.
And there are many open source projects out there
that have many stars on GitHub that just wrap some existing library in an Rx
library, and then they're very popular.
For instance, this one here is just a list of other Rx libraries,
and it has 500 stars on GitHub.
And kudos to that person who made it.
But I'm just saying there's a lot of interest in reactive programming
out there.
And just sometimes applying this sort of clean--
abstracting this messy start away is really helpful,
and people appreciate it.
So, hopefully, now you understand high-level principles
of reactive programming, some applications for when we'd use it,
what a data stream is, how a function can manipulate a data stream,
and how observers listen to asynchronous events and what that means.
And so we'll get into some more background and examples later,
but I do want to pause for questions here.
I know that's a lot, and I threw out a lot of terms.
So can I help clear anything up?
Yeah?
AUDIENCE: Java has map collective filter.
Is it just inheriting that from something?
JORDAN JOZWIAK: Yeah, that's a great point,
and I don't think I talk about this elsewhere.
So starting in Java 8, they added more functional aspects to Java.
And Java itself does have some streams.
One reason that it's still popular, RxJava
happens to be popular in the Android community,
is because a lot of that functionality isn't available.
But in addition to that, streams in Java are pretty good,
but I think people have some concerns about them.
They could be easier to work with.
At least in terms of contained within the stream, it might be fine.
But RxJava here has the benefits of it has a little more flexibility in terms
of the observer pattern.
It has more options in terms of the threads that you can subscribe on.
So I would say it's just kind of a next step up.
Anything else?
Yeah?
AUDIENCE: For the term functional reactive programming,
where that term is kind of [INAUDIBLE].
JORDAN JOZWIAK: So I am not like a theorist in terms
of understanding all the programming paradigms
and how they relate to each other.
I know when I've read the reactive extension specifications,
they say this is not functional reactive programming.
That is something else.
But honestly, I don't think I could clarify you what exactly that is.
But maybe we could talk afterward.
Anything else?
OK, well, we'll just keep going, then.
So, before we get a little bit farther, I just
am curious what languages are you all familiar with?
You can just shout it out.
And I know some of the languages you learn in CS50,
so if you don't say those, I'm on to you.
AUDIENCE: Python.
JORDAN JOZWIAK: OK, nice.
AUDIENCE: Java.
JORDAN JOZWIAK: Java.
AUDIENCE: Swift.
JORDAN JOZWIAK: Swift, oh, fun.
AUDIENCE: C++.
JORDAN JOZWIAK: C++.
AUDIENCE: R.
JORDAN JOZWIAK: R.
AUDIENCE: C.
JORDAN JOZWIAK: C, yeah, there we go.
The one everyone should be learning.
A lot of those languages mentioned come from imperative or multi-paradigmatic
background.
And what that means is they usually involve some sort
of conditional logic and state.
And we'll get into that more in a little bit.
Some of these languages also have functional aspects to them,
and that's part of what makes them multi-paradigmatic.
So, for instance, Java now has functions built into the language,
and Swift also has a lot of functional aspects.
And functional programming has a very steep learning curve
I know that when I was first getting introduced to it,
it took a while to understand.
And RxJava, what I'm talking about today,
took even longer to understand on top of that.
So good for all of you for showing up today and grokking some of this.
No worries if it's a little difficult to follow.
That's also a little bit on me for presenting it.
So when I was learning this, I also asked myself,
why are there different language paradigms?
Why isn't there one language that just works for everything?
And part of this is there are different use cases for programming.
It's just like a toolbox.
There are different tools that you have to pick the right one for the job.
But part of this is also kind of an organic immersion of ideas.
So, for instance, even in linguistic languages,
there are differences in how people end up communicating.
So, for instance, grammatical structures.
In some languages, I might, say, refer to my house as Jordan's house,
and in other languages, it might translate to be the house of Jordan.
There's also, in Spanish, there are multiple tenses for the past tense.
There's a recurring past tense and a finite one.
And not all words have the same sort of translation across languages.
You don't just have a single word.
And there are also dialects within a language.
So I kind of throw this out there as an example
of just to say that in language, there's no one language that
is suitable for all purposes.
And also, we've all had similar life experiences globally,
but different languages have emerged.
And so, in some ways, that's similar to computer programming,
that languages are multifaceted, but they also evolve over time.
So, for instance, imperative is one sort of language paradigm,
and that's probably what you've used for a lot of the languages
you've worked with.
In reactive programming, what we're talking about today,
is kind of a more specific programming paradigm.
But that's based on, or partially inspired
by, functional programming, which is used in reactive programming,
and I'll talk more about it.
So I just tossed out these terms--
imperative, functional.
Imperative is, as I was saying, kind of based on state and conditions.
So let's kind of compare these.
And technically, imperative and declarative
are the comparison points here, but we'll use functional for simplicity.
Imperative programming, we describe how the program operates.
And that means we have statements to change the state of the program.
We have conditional statements, loops.
We might even have inheritance and these ideas
of components that can be put together to create the whole piece.
It's usually an easier learning curve.
That's kind of an intuitive way to think about programs, that there's
some checkpoint that we understand this is the state we're in,
and then we manipulate it.
And some examples include C, PHP, and Java.
But I'm not trying to pick a fight with anyone
if someone would argue that these languages are more multifaceted.
And this is a really simple example.
We have if value mod 2, checking if it's even, we do something.
And here, you can kind of see that we already have some idea of state.
There was a variable coming in, and there's an if statement
to manipulate something.
In functional programming, we describe what the program should accomplish.
And that means it's, ideally, stateless, and it's
using functions that can create expressions instead of statements.
So we can chain together functions that are, ideally,
pure, meaning that they don't have any other side effects of changing state.
It's usually more difficult to understand conceptually,
because you don't have that idea of where am I right now,
and what are the components involved?
And some examples of languages that are pretty more strongly functional
are Scala and Haskell.
And honestly, I haven't used either, but I know some people
who use them really love them.
And for example, this is just a function that could be used anywhere.
But I'm trying to convey here that rather than maybe just have
some sort of if statement checking and manipulating the data, we could apply
a function, which would be used elsewhere.
And imperative programming, originally a computer utilized vacuum tubes
and had limited memory.
They were written with step-by-step instructions,
including in FORTRAN, which was introduced in 1954.
And my understanding is it was the first usage of compiled language
with variables and complex expressions.
So in the past, maybe it was easy to understand,
especially in terms of how we use computers,
that we would kind of manipulate a state and iterate on that state.
And then there have been other languages since then.
And object-oriented programming really grew in the 1980s.
And we'll get back to that.
So in functional programming, it's based originally on lambda calculus.
And also, don't ask me about the nuances of that, because I'm no mathematician.
But at its core, it's a study of computation with functions.
And the first functional programming language
was LISP, which was developed by IBM in the 1950s.
And it stands for List Processor, LISP.
It's still in use today, and it's only one year younger than FORTRAN.
So from the beginning, there was already kind of a thought about different ways
to approach these problems, an imperative
style and a functional style.
And back from the '70s, there was this guy
named John Backus, who gave a lecture at the Turing Awards on why
imperative programming is bad and functional programming is good.
And if you read about this online, you'll
see there's still a lot of people out there
who pretty strongly believe that imperative programming is
part of the reason we experience so many bugs in programming.
And that if we all just use functional programming,
we would have a better developer-- we would have better coding experience,
and we would encounter fewer problems in the real world.
And part of the reason that functional programming didn't take off
in popularity is just Java and object-oriented languages with
encapsulation really dominated for through the '80s and the '00s.
But when JavaScript grew in popularity, especially as a web language,
it is a multi-paradigmatic language which
makes use of passing around functions as first class objects or first class
citizens.
And I think that's probably part of the reason
that functional programming has regrown in popularity.
So reactive programming, we talked about these three areas, of data streams,
functional programming, and asynchronous observers.
So data streams, it's the flow of data objects from one function to the next.
Ideally, immutable, but that's a different discussion.
It's focusing on the idea of objects shouldn't change.
They should be stateless.
Functional programming.
Well, I mentioned that functions are meant to be pure.
In our example, we had a filter function that took in some input.
It happened to be an integer.
And it returned a Boolean for whether the value should
be included in the rest of our stream.
And it had no additional side effects.
All its logic was contained to that particular function.
So, for example, we could also have an add function that takes in two values
and returns the result. And we'll also look a little bit
later at a map function, as I was mentioning, that could maybe
take in an integer and return a string.
It can transform the data in however way we want.
And with the asynchronous observers, that
uses the observer pattern with a subject for data sources that notify observers.
And one thing that I just want to mention but we won't focus on much
is that there are errors that can happen in these data streams,
and those are propagated through the whole flow of data.
In the programming that you're familiar with,
maybe you have some error checking at the beginning.
In Java, maybe you throw an exception and you have to handle that.
With these stream of data, the error goes all the way through,
and it's up to the consumer to either handle it or ignore it.
And reactive programming is not its own language, I mentioned on that.
It's a library that we apply to some languages, like Java.
And it's also not React, ReactJS, or ReactNative, if you've heard of those.
Those are some libraries that were made by Facebook and kind of focus
on improving UI development in mobile and web applications.
So RxJava, I've mentioned, is the reactive library
that I'm the most familiar with, and it's
the one we'll be using our examples with today.
It's pretty popular, and I've used it in my own work for some side projects
and at work, as well.
So this specification is also applied to other languages, like RxJs and Swift.
And it is helpful due to complicated stream and asynchronous APIs
that would otherwise exist in Java.
And it brings functional design to a language
that's traditionally object oriented, even though it has
some more modern functional aspects.
And RxJava is also very helpful for mobile development,
as I mentioned for Android.
On Android, developers normally have to work within the constraints of Java 7,
with some Java 8 functionality.
And so it's very helpful there.
So that's enough discussion of the background and history.
So let's look at some actual code.
Maybe that will help this all sink in a little bit more.
But before we move on, any questions on any of this?
Making sense so far?
Yeah?
AUDIENCE: You mentioned that in JavaScript,
for empty functions, that's first class citizens.
Could you clarify that?
JORDAN JOZWIAK: Yeah.
So in JavaScript, which it's been a little while since I worked with,
but you can pass a function from one thing to another.
And in Java, everything has to be a class.
So when we pass a function, we're actually
passing a class, which has an implementation of a function.
In JavaScript, you can just pass a function itself.
And so that's the way that it's--
so in the argument of something, you can pass in a function.
And that's what I mean by first class citizen.
Anything else?
Yeah?
AUDIENCE: You also mentioned that functional programming is stateless.
What does that mean?
JORDAN JOZWIAK: So state means--
and we'll look at this in some examples later.
In a class, you have some variables that might live
outside of your particular function.
So maybe you're working on something, and at the top of your file,
maybe you have some global variables.
Maybe you also have something that--
like let's suppose we're making some game,
and we have some variable for like hasGameStarted.
And that's something that will kind of live there
and we can access elsewhere in our code.
And that's some state.
And so when we talk about stateless, that
would mean we somehow eliminate all those problems,
that there is no external state that we'll be reading in.
And that all the data that we care about lives within our data stream.
Anything else?
So suppose we want to observe some data.
We'll use this term subscribe to mean observe
the final result of a data stream.
And here, we'll get into comparing the imperative
approach and the reactive approach.
And so we look at the imperative approach here,
and maybe this is something that would look somewhat familiar.
We have some data source.
We'll get the data.
It's a list.
If you're not familiar with Java, this is kind of like an array,
and it means that we have integer types here or like int types.
So we have some list of data in a for loop.
We'll just go through all of it, and we'll print it out.
And this is how we might take some data and take some action on it.
And so, for example, our data input here could be 4, 2, 1, 7, 6,
and we print out 4, 2, 1, 7, 6.
So that's the data source, and we print out the values.
And because we're using a for loop, that's one of the signs
that this might be an imperative approach.
Now let's look at the reactive solution.
So note already that instead of getting a list of data,
we have a stream of data.
Remember our subject and observer from earlier?
Well, this data source is our subject.
Technically, this is returning an observable in RxJava,
but again, I don't want to get caught up in the nomenclature.
So we'll just see that it has some data stream.
And our data stream has this built-in function called subscribe.
This is part of the functionality that comes with using the ReactiveX library.
And the subscribe method takes an observer.
So this means we're done transforming our data,
and we want to do something with it.
Of course, we don't have any transformations at this time,
but we will in the next example.
So the observer technically takes in a class called a consumer,
but this is what I was actually getting to earlier
when we were talking about the first class citizens for languages, where
functions are first class citizens.
In Java, at least Java 7 and earlier, we always
have to pass in some class which has an implementation of a function.
So we'll get rid of this in a second, and then we
don't have to worry about this name called consumer.
And this is the function that the anonymous class actually implements.
And again, we'll get rid of that in second.
The important thing is that this method takes an individual value from the data
stream called value, an integer value, and it does whatever it wants with it.
So in this case, it just prints it out.
And so to simplify this expression, we'll
use something called a lambda in Java.
Don't worry about the name if you're not familiar with Java.
But see now that we can kind of clean up-- this
is what you might think of as passing in a function as an argument.
So here, we just have the input, and we take some action on it.
The arrow means that we're expressing a function.
So the argument value is on the left.
The body of the function is on the right.
And the code does the same thing as that previous consumer class,
but this is easier to understand.
We can also use a method reference here, but that's a Java thing,
and don't worry about it.
So these are the imperative and reactive ways to print out a list of data.
In the imperative case, we get all the data at once, and we use a for loop
to print it out.
In the reactive case, we get a data stream, and we observe each item.
And in each case, we would print out 4, 2, 1, 7, 6.
And we can do more than print out our values.
That's just an example.
We could maybe update a UI with these values or maybe save to a database.
Reactive programming is an approach with many applications,
so we can kind of do whatever we want with this.
So now let's talk about a scenario that definitely merits a reactive approach
suppose we want to get all data changes from a data source.
So we want to observe the changes.
In Java, there might be simpler ways to do this,
but this is one that I found as a solution.
And it's kind of long and complicated.
It involves some threads for scheduling updates to listen for data changes.
And honestly, I don't even understand how all of that works.
Basically, it's complicated, and there's a better way.
So here's the reactive approach.
Does it look familiar?
It's the same as the other example.
Why?
Well, our data source is already a stream.
So there is a base assumption that the subject emitting data
events will notify us of all changes.
That's what it was doing the whole time.
Every time we got a data event, we would have it emitted there.
So our observer will be called for all data changes going forward,
for the first one, for the second one, until we
decide to stop listening to data.
OK, any questions on that before we move on?
The subscribe idea, data changes, make sense?
AUDIENCE: So those two examples you have, they aren't really equivalent,
because the thing on the left is actually
doing the work of pulling to obtain new data and so on and so forth.
Whereas the one on the right, it's like, oh, magic happens.
And I know that there's new data and I can act upon it.
So there's a lot of framework that has to be surrounding that, right?
JORDAN JOZWIAK: True, true.
And that's why, earlier on, I was kind of mentioning
that the subject of getting that data stream, there's
a lot of work that goes in there.
And so you're right that, in some ways, this isn't a fair example.
Because this code, something like this code,
might have to live somewhere else in how we create the data stream.
Yeah, and that that is kind of up to the implementation of that.
In some of these examples, especially when
I portray reactive programming as the great way of doing things,
you do have to keep in mind that somewhere behind the scenes,
there might be something messy going on.
Anything else?
Any questions?
Yeah?
AUDIENCE: You mentioned earlier about multiple observers.
JORDAN JOZWIAK: Yeah.
AUDIENCE: So for that, [INAUDIBLE] subscribe,
or would you do another [INAUDIBLE]?
JORDAN JOZWIAK: Yeah, they're assuming we
don't want to get multiple instances of the data stream.
We could have saved this as a variable, which maybe then we're
getting into some imperative approach, but we can talk about that later.
So we save this as a variable, and we subscribe to it.
And we could just have another thing right here for the same--
I mean, we could just get the data stream again.
Kind of depends on what this reference is.
But then we can just say subscribe and another one
and take some other action on it.
Yeah?
AUDIENCE: Can you chain subscribe calls?
JORDAN JOZWIAK: No, that's the terminal call.
But there is this idea of doOnNext.
So we could have multiple things that happen for each event.
And then, you could just subscribe without any action, which means
start doing everything I said to do.
So you could chain multiple doOnNext and then have that subscription start.
But if we want them totally independent, then they probably
should be different subscriptions.
Kind of depends on whether there might be an order to the tasks
that we want to accomplish.
Anything else?
So now, let's actually apply a function to our data stream.
I mentioned filter a few times, so we'll use that as our example.
In the imperative approach, we will do pretty similar to what we did before.
But now, we include this if statement to only print out the even values.
Because again, this value mod 2 equals equals 0 will check if a value is even.
And so rather than 4, 2, 1, 7, 6, we'll print out 4, 2, 6.
I kind of showed an example of this before when I had the animations.
This is an actual image from the reactive spec.
And we call these marble diagrams, because they're these little circles
that might resemble marbles.
And some of them through, other ones don't.
So rather than use that if statement, we could think of that check
is a function, which we can always just write that function separately and use
it as an imperative way.
But we're going to think about it in terms of manipulating a data stream.
So similar to the timeline and data events from the beginning,
this marble diagram shows how we could filter based on values greater than 10.
But in our example, we're going to filter based on even numbers.
So this is the reactive code.
And here, we'll apply a function called filter,
which takes the valley from each item in the data stream as the input.
And it checks if the value is even.
It implicitly there returns true or false.
Value mod 2 equals equals 0 will return true or false.
And this Boolean result is used within the internals of the filter mechanism
to limit which items are passed through to the rest of the data stream.
So, again, the implementation of filter might--
well, it's probably fairly straightforward,
but it could always have something that maybe looks like this under the hood.
And filtering is an interesting example, because it
restricts which items are emitted into the rest of the stream.
But suppose we wanted to change all values in a consistent way.
So if we were to, for instance, double all the values in our stream,
we might take an imperative approach like this, where we don't
have to save a temporary value here.
But we just multiply the value by two and print out the result.
But again, we could think about this as a way of manipulating a data stream,
and we'll apply some function called map.
This example marble diagram has all the values multiplied by 10.
And so the reactive code looks like this, pretty similar.
Instead of the filter function, we have a map function,
which is also one of the built-in functions to the reactive extensions
library.
And it takes each value from the stream as input.
We calculate the new value--
in this case, multiplying it by two.
And the result of that expression is implicitly returned,
and then we subscribe to the results and print them out.
OK, last example.
This one is maybe a little more complicated.
So I just want to show maybe one of the times
where reactive programming could save a little bit of trouble.
So this is the imperative code for grouping some data.
And there are a number of different ways to do this.
This is just one example.
At a high level, we'll just kind of gloss over this.
We scan through each item in the list, put it
into a map with the appropriate key for even or odd,
and then we'll print out the results at the end.
So loop through each value, find out what the key is, put in the value
into the key.
This code is fine.
It works.
But there also some sort of different functions and tasks involved.
There's some maybe grouping here based on different values.
There's looping.
The keys are some variables that are used outside of the loop.
And so, in some ways, you might say that this code is a little muddled together.
And we could definitely clean it up in an imperative way.
But we can also think about it maybe as kind of a-- if we have a stream of data
and we really want to separate these streams, we could group by this--
use a function called groupBy and separate our original stream
into two different ones.
And since grouping is an important part of data processing, of course,
it's built into the reactive specification.
So the reactive solution looks like this.
We start with our data stream.
We apply a grouping function that will create the name for each group.
So if the value is even, then we'll return the string even.
And if it's odd, we'll return the string odd.
And that is going to be used as our key for the rest of the stream.
And I am cheating a little bit here.
The collectInto function, which is also built in
and is a way of kind of merging the result of a stream,
this ends up still looking a little messy,
kind of because we're putting a stream back into some stateful-- well,
into some object.
So kind of gloss over that.
But keep in mind that the grouping part there is pretty easy.
So there are a lot of examples.
There have been a lot of examples to get us here.
But hopefully, this gives you a glimpse into how
reactive programming can simplify the approach for certain problems.
And there are many more operators.
We talked about filter, map, and groupBy,
but here are some of the other ones.
Many are self-explanatory in their name.
Hopefully, they make sense.
I think a really powerful one that's especially
helpful in mobile programming is called debounce,
which means if a user is typing, for instance, in a search field,
we wouldn't want to show them autocomplete--
we want to show them autocomplete results,
but not for every new character they search, right?
Especially if that's something that maybe involves querying some server.
We don't want to send 20 server requests if they type a 20-character query.
So debouncing allows us to set a window of time where we listen for changes
and then take action on the most recent one.
So if the user is typing really fast but then they pause,
then we'll show the results for that.
And this is, again, something that under the hood may be a little messy.
But because we're using the reactive extensions library,
debounce is just built in.
And if this all seems complicated, it is.
It's hard to learn.
It takes a little while getting used to.
Even when I thought I understood RxJava and reactive programming,
I look at a diagram like this and stare blankly.
To be honest, I still don't really know what this is saying.
I'm not sure why red circle comma cloud equals red cloud.
So if you become an expert in reactive programming and RxJava,
please explain this to me.
But this is one of the examples for the operator join.
So we started theoretical.
Then, we looked at some code examples with actual functions.
And so now, I want to do a quick demo to really emphasize
how powerful RxJava can be.
But first, before we dive into that, any questions on any of the examples
or still what is a data stream, what am I talking about?
Anything?
OK.
There'll still be time for questions at the end.
I'm just going to mirror my displays now.
I made this.
I have been an Android developer, so I thought
it might be fitting to use an Android app as a quick example.
Pretty sure this is a similar one.
OK so I made a little Android app here that is, hopefully, going to run.
There we go.
That's what I get for setting it to be five seconds.
So this little app here is taking in--
it's using a FlakyTemperatureSensor, a FlakyWindSensor,
and it's showing us the current temperature, wind speed,
and then what it feels like.
And these are like fake temperatures.
They're just generating some random values
within a somewhat appropriate range.
And the reason they're called FlakyTemperatureSensor and wind sensor
is because sometimes, as you might have noticed here,
they return the value negative one, which
is just because they're some hypothetical cheap sensors that
don't always return useful values.
And in this, I'm using the term presenter here
as just some way of taking in these values and updating our UI.
We have a sensor view, which allows us to set the temperature,
wind, and feelsLike speeds or values that we see there.
And this presenter, when it starts, it's going to add a listener.
This probably applies across multiple languages.
In Java, note that we have this anonymous class here,
which it's telling me it could be simplified to a lambda.
But I just want to put in the whole anonymous class as the example.
So we take our temperature sensor.
We add some listener, and it's going to keep
track of the last temperature value, the last wind value for the wind sensor.
And then, whenever one of those is updated,
it's going to say handleValuesUpdated.
So notice already here, because you can see with the feelsLike temperature,
it's actually combining the values of the temperature and the wind speed
and calculating some other temperature value.
So that's why we are keeping these as some state.
And to get back to that comment about state and stateless,
this is an example of having some state in a class.
These are some values that are living kind of outside of any function.
For some reason, they're in this class.
Maybe they shouldn't be.
But this is one implementation.
And so we're listening to these values.
And in our handleValuesUpdated function here, first of all, we check--
if we don't have a temperature value or wind value,
then we're just not going to do anything.
Because when I calculate the feelsLike temperature, I need both values.
And they're both initially going to be null,
so this is a way of saying we're just ignoring this stuff at the beginning.
Then, we're going to show the raw values in the view, the temperature
value and the wind value.
And then, I also mentioned that these are flaky sensors, right?
So we're going to ignore the negative one values.
And so we have some conditions here, all just to kind of get set up.
And then we're converting a Celsius temperature
to a Fahrenheit temperature.
We have some function for that.
And then we're calculating the feelsLike temperature
based on the Fahrenheit temperature and the wind value.
And then we're finally setting the value.
So in order to accomplish this, we had some state which lived in our class.
We have some conditional checks, and we have some functions
that are transforming some data.
And you look at this and you say, well, this accomplishes what I want.
But in some ways, it's a little imperfect.
And so I'll just kind of go through this reactive example.
So I'll try to go through this kind of quickly.
Yeah, sorry.
If any of you have to go somewhere, you're free to leave.
So if suppose, maybe, we have our temperature sensor.
And instead of setting a callback, we'll get the data stream.
And this will be our temperature stream.
And this happens to be an observable.
And for our wind sensor, we'll get our wind stream.
And so now we're already subscribing to all changes in the data.
And if we want to actually print out these values, like the raw values,
as we were doing in the imperative approach,
now we can do one subscription to them.
So, for instance, in the temperature stream, we can subscribe.
And for each value, we will set the temperature.
So here, we have one subscription.
And for the wind one, we can also set the wind value.
And so if I just change to use our reactive presenter and run this,
we won't get the feelsLike temperature yet, but we will get the other values.
And so now with the feelsLike temperature,
we want it to actually filter out some values
and convert a Celsius temperature to Fahrenheit.
So another function that we have with our reactive observables
is combineLatest.
And this will allow us to take two different streams
and combine them together.
So, for instance, with our temperature stream,
we can first we'll filter out the values that we don't want,
which will be all negative one values.
And we also want to convert the values to Fahrenheit.
And we'll still use that helper function we had before for convert.
And then, we'll also use our wind stream and filter out
the values that we don't want, which is the negative one values.
And then, lastly, we have to combine these two streams together.
So if we have a tempWind, we can use some function on them.
We can calculate the feelsLike temperature
and, lastly, actually subscribe to all this
with whatever the feelsLike value is.
So now, if we run this--
AUDIENCE: There's a bracket that's not closed or something.
JORDAN JOZWIAK: Oh, thanks, yeah.
Now, we also calculate the feelsLike temperature.
So remember, in the other example, we had some callbacks.
We were listening to some data values.
We had some state for the last values, some conditional checks.
And here, this is an example where we have two subscriptions.
We just show the raw values here with the observable.
But separately, we also use this combination function combineLatest,
which is taking these two different streams,
merging them together with this function,
where we have temperature and wind.
And we calculate the feelsLike temperature on that.
And then we subscribe to this particular value and set it on the view.
Any questions on that?
So, to wrap up, reactive programming has data streams, functional programming,
and asynchronous observers.
When would you use it?
Well, you'd use it when you're dealing with some of these things, when
you're working with streams of data or you want to notify some set of things
asynchronously.
And so I'd like to thank you all for coming.
I know this went a little long, so thanks for sticking around.
Thank you, Ryan, for organizing; the CS50 staff for having me;
and for all of you for showing up and learning
about something that is maybe kind of different from what you normally do.
I'll be around afterward to answer any questions.
Thank you.
[APPLAUSE]