In this course you will learn how to apply the functional programming style in the design of larger applications. You'll get to know important new functional programming concepts, from lazy evaluation to structuring your libraries using monads. We'll work on larger and more involved examples, from state space exploration to random testing to discrete circuit simulators. You’ll also learn some best practices on how to write good Scala code in the real world.
Several parts of this course deal with the question how functional programming interacts with mutable state. We will explore the consequences of combining functions and state. We will also look at purely functional alternatives to mutable state, using infinite data structures or functional reactive programming.
Learning Outcomes. By the end of this course you will be able to:
- recognize and apply design principles of functional programs,
- design functional libraries and their APIs,
- competently combine functions and state in one program,
- understand reasoning techniques for programs that combine
functions and state,
- write simple functional reactive applications.
Recommended background: You should have at least one year programming experience. Proficiency with Java or C# is ideal, but experience with other languages such as C/C++, Python, Javascript or Ruby is also sufficient. You should have some familiarity using the command line. This course is intended to be taken after Functional Programming Principles in Scala: https://www.coursera.org/learn/progfun1.

MM

Picks up where the previous course left off... the last assignment is a bit more challenging than the others, well for me it was. Thanks to the people who put the course together.

De la lección

Timely Effects

This week we'll learn a number of important programming patterns via examples, starting with the observer pattern, and then going on to functional reactive programming. We'll learn how latency can be modeled as an effect, and how latency can be handled with Scala's monadic futures. We'll learn the important combinators on futures as well as how they can be composed to build up rich and responsive services.

Impartido por:

Martin Odersky

Professor

Transcripción

So in this module, we are going to look at some of the traditional ways to handle events. Event handling, of course, is something very old, almost as old as software itself. We have already seen one instance where event handling played an important role, that was simulation. And we are going to see now another, and that's user interfaces. The traditional way to deal with user interface is to handle the events there is based on what we call the observer pattern. We are going to explain what it is, and we are going to explain what it's good for, and also what some of it's shortcomings are. That will lead us then in the next two modules to a different way to treat events in these programs, which is called functional reactive programming, and where events are essentially summarized in signals. The observer pattern is commonly used when we have some sort of model that maintains a state of an application, and we need to have one or more views that essentially present the properties of the model in some way. Variants of the observer pattern are also called publish/subscribe or model/view/controller. The idea is always that we have some sort of model which captures the state of an application. And, we might have one or more views that present that state. And there would actually be a varying number of views. So, views can announce themselves to the model with an operation which we call subscribe. And the model will then, whenever there's a change, publish the fact that there is new information to the views. If you can then inquire the model about what the new state is, and change its presentations. And because essentially views announce themselves as published, there can be more than one view, so there could be another one that also publishes, subscribes itself, and gets the same published information. Sometimes in user interfaces, we have a have a third component, which is called a controller, Which somehow manages the interactions between the model and the view, but the controller, in fact, is optional. So let's see how we could put this into code. Here's is a trait for publishers, so it's expected that every publisher would inherit from that trait to gain the functionality of a publisher. What is that functionality? Well, publishers maintain internally a set of subscribers, which you see here. Initially, that set is empty. So you can add a new subscriber by calling the subscribe method of the publisher, which simply announces a given subscriber that's another trait that we will see next. And what it does is, it adds a subscriber to the set with the + =. The dual of subscribe, of course, is unsubscribe, so as subscribe, I can also announce that it's no longer interested in published info of that publisher. And then, the implementation of that would simply remove the subscriber from the set. And finally, a publisher has a publish methods. What that does it's simply it goes to all subscribers and invokes for each subscriber a handler method that the subscriber must provide with the current publisher as argument. So let's see the subscriber next. Subscribers are very simple, all they need to have is the handler method. And we pass the publisher that published new information as a parameter to that handler. So let's go back to bank accounts, which you have seen before in the module about functional programming in state. You see the example of bank account that we have here again, for a recall. So bank account has deposit and withdraw methods, and it maintains a private variable balance. And of course the deposit method add some amount to the balance whereas the withdraw method subtracts it. So what do we need to do to make bank account a publisher? I have already given you the extension, so bank account now extends publisher. What we need to do, of course, is invoke publish, because otherwise, nobody ever know about changes in the bank account. Where do we do that? Well, every time we change the state of the bank account. So, I would propose we put a publish here, And we put another publish here. One in this deposit, the other in withdrawal. We are almost done. The one thing missing here is yet, well, once we have published well this is a view of the bank account a subscriber, what's it supposed to do? Now probably wants to access important details about the bank account and probably the most important one here is the balance. So right now there's no way to access the balance directly, because balance is a private variable. So let's add an access method and call it current balance. Simply provides the current state of the variable. Of course, we could have made balance simply a public variable, but that's not advisable because that would mean that everybody could not only everybody read balance, but also write to it. And I believe lot of banks would get very nervous, if you could manipulate the balance of your accounts or anybody's accounts like that. So here you see the complete picture of bank accounts, I've added all the things that I drew by hand to the quote here, current balance, and the two publishers. So let's add a view to this picture. The thing I want to do is define a class consolidator that observes a list of bank accounts, and that would always be up to date with the total balance of all the bank accounts. So the sum of all the balances in the observed bank accounts. Consolidator is a subscriber. So what this consolidator does initially is, it subscribes itself to all observed bank accounts as an initialization action, observed for each subscribe, this. What we needs to do then is maintain a variable, which is the total sum of the balances of all the bank accounts. I've written here private var total: Int = _. That means that the variable is initially uninitialized, that's what the underscore does here. I initialize it by calling the compute method. So what does compute do? The compute method goes through all observed bank accounts, takes the current balance of each, and takes the sum of these balances, and stores the result in total. Computers also called by the handler method of the subscriber. So whenever one of the bank accounts changes, compute is invoked again to recompute the total balance. Of course, one could envision more efficient ways to do this. Maybe take the difference of the balance of this account, and apply that to the total variable. But for now, we're doing the most straight forward in simple way, even if it's not the most efficient. Finally, there's an access method again, totalBalance it gives you the current state of the total variable. So let's observe bank accounts with a little Scala worksheet, I've called it observers, and I've put it in the package week two dot publish subscribe, where I assume the bank account class is also located. So let's define a couple of bank accounts. We have Bank Account a, Bank Account b, and now let's define a consolidator that takes the two BankAccounts and always maintains their total balance. So we can find out what the total balance is, by just calling c.totalBalance, And of course, the total balance initially is 0. So let's do something with the accounts. Let's say we want to deposit 20 currency units in a. And, We want to find out what's the total balance now. And that would give us 20. Well, no big surprise, but remember the total balance actually does not by itself always recompute. So indeed that the only gives you the current variable total in the consolidator has updated itself as you can see there. Let's do another step, let's deposit 30 units in b, and total balance here. And we would get 50, as expected. So let's see how we would evaluate what we've done with the observer pattern. There's some good aspects. So one good thing is that, we have views that are decoupled from the state. We can have varying number of views of a given state, so that's good. And it was overall rather simple and intuitive to set up. But there are also some problematic parts to this design pattern. The first one is that, you have seen that all the fundamental operations, publish, subscribe, handle, they return unit as a result, so they must be imperative, everything they do has to be by imperative updates. The second problematic aspect is that, in fact, there are quite a few moving parts that need to be coordinated. So every subscriber has to announce itself to the publisher with subscribe then the publisher has to essentially handle these things in a data structure. There are calls back and forth and so on. That also makes things more complicated when you add concurrency. One particular problem is, if you have a single view that observes two different models that get updated both concurrently. In that case, the two models could call at the same time the handler method of the view, which gives us a possible rates conditions that have to be handled. A fourth disadvantage is that views are still tightly bound to the state that's represented in the model. Every view update is directly coupled to the state update. Once we update the state, the view gets updated immediately. Sometimes we want to have a looser, asynchronous relationship between a view and the model. There was one interesting study by Adobe from 2008, where they looked at the code base which is quite UI centric, and they found that indeed, event handling is a very important part. About one-third of the code in Adobe's desktop applications is devoted to event handling. And furthermore, that the code that uses event handling is also quite intricate. So more than that chance every one-half of all the bugs in the code were related to event handling. So that shows that, the traditional ways of doing events, by workable and standard, quite an industry standard but right now, is far from being perfect. It´s quite bulky, and it causes quite a lot of bugs. So in the rest of this course, we'll explore different ways in which we can improve on the imperative view of reactive programming that's embodied in the observer pattern. In this week, we are going to look at functional reactive programming as an alternative to treat the whole of these event sequences in a functional way. In the next two weeks, then, we will look at related, but different ways of abstracting over events and eventstreams with futures and observables. And then the last three weeks of this course, we will tackle concurrency head on. We will express concurrency and handle it using actors.