Pure JavaScript
This is a modal window.
Das Video konnte nicht geladen werden, da entweder ein Server- oder Netzwerkfehler auftrat oder das Format nicht unterstützt wird.
Formale Metadaten
Titel |
| |
Serientitel | ||
Anzahl der Teile | 110 | |
Autor | ||
Lizenz | CC-Namensnennung - keine kommerzielle Nutzung - Weitergabe unter gleichen Bedingungen 3.0 Unported: Sie dürfen das Werk bzw. den Inhalt zu jedem legalen und nicht-kommerziellen Zweck nutzen, verändern und in unveränderter oder veränderter Form vervielfältigen, verbreiten und öffentlich zugänglich machen, sofern Sie den Namen des Autors/Rechteinhabers in der von ihm festgelegten Weise nennen und das Werk bzw. diesen Inhalt auch in veränderter Form nur unter den Bedingungen dieser Lizenz weitergeben | |
Identifikatoren | 10.5446/50962 (DOI) | |
Herausgeber | ||
Erscheinungsjahr | ||
Sprache |
Inhaltliche Metadaten
Fachgebiet | ||
Genre | ||
Abstract |
|
NDC Oslo 201235 / 110
1
2
3
5
7
9
11
12
15
19
20
23
24
27
28
29
31
32
33
35
36
37
38
39
41
43
46
47
51
52
56
59
60
61
62
63
65
67
70
71
74
75
77
79
80
81
83
87
91
92
93
94
95
96
97
98
100
103
106
108
110
00:00
QuellcodeTropfenProgrammiererFunktionale ProgrammierspracheWeb-ApplikationImplementierungFreewareSoftwaretestMusterspracheUnternehmensarchitekturSkriptspracheFokalpunktProzess <Informatik>Projektive EbeneTorusStrömungsrichtungComputeranimation
00:59
SoftwaretestMultiplikationsoperatorSoftwaretestProjektive EbeneProgrammbibliothekÜberlagerung <Mathematik>BitWeb SiteOpen SourceTest-First-AnsatzLesen <Datenverarbeitung>Computeranimation
02:09
ProgrammiererFunktion <Mathematik>LogarithmusEreignishorizontFunktionale ProgrammierungFunktionale ProgrammierspracheSpeicherabzugSkriptspracheResultanteSoundverarbeitungLoginFreewareElement <Gruppentheorie>Ein-AusgabeEreignishorizontTouchscreenOrdnung <Mathematik>SchnelltasteObjekt <Kategorie>ParametersystemProgrammierspracheProgrammierungBitArithmetischer AusdruckKartesische KoordinatenKlasse <Mathematik>MereologieSpielkonsoleHochdruckZeichenketteZahlenbereichPRINCE2Rechter WinkelCASE <Informatik>Befehl <Informatik>SystemaufrufMultiplikationsoperatorProgrammiergerätComputeranimation
05:51
ProgrammiererProgrammschleifeProgrammschleifeIterationFunktionale ProgrammierungSpeicherabzugDatenstrukturSchnittmengeFunktionale ProgrammierspracheOrdnung <Mathematik>Algorithmische ProgrammierungEinsÄußere Algebra eines ModulsBefehl <Informatik>Computeranimation
06:42
ProgrammschleifeProgrammiererNormierter RaumArray <Informatik>Mailing-ListeSpielkonsoleInhalt <Mathematik>Innerer PunktPrototypingDickeLogarithmusTransformation <Mathematik>SoundverarbeitungDigitalfilterSoundverarbeitungCASE <Informatik>LoopMailing-ListeFunktionale ProgrammierspracheFokalpunktSystemaufrufKartesische KoordinatenAutomatische IndexierungMusterspracheMechanismus-Design-TheorieZeichenketteProgrammschleifeTwitter <Softwareplattform>Array <Informatik>Physikalischer EffektTransformation <Mathematik>DateiformatSpielkonsoleSchaltnetzProgrammierspracheTermObjekt <Kategorie>EinsZahlenbereichCodePunktIterationKategorie <Mathematik>Airy-FunktionMathematikSinusfunktionBruchrechnungKontextbezogenes SystemForcingFlächeninhaltOvalDatenstrukturComputerspielBitrateKonditionszahlComputeranimation
10:41
ProgrammiererPrototypingTwitter <Softwareplattform>Vollständiger VerbandFunktion <Mathematik>Kategorie <Mathematik>SoundverarbeitungMAPLoopSkriptspracheUmwandlungsenthalpieSystemaufrufIterationKategorie <Mathematik>ZeichenketteFunktionale ProgrammierspracheMailing-ListeGrenzschichtablösungCodeArithmetischer AusdruckObjekt <Kategorie>FehlermeldungEinsTwitter <Softwareplattform>ProgrammierspracheNichtlinearer OperatorBitAutomatische IndexierungGüte der AnpassungGeradeFokalpunktKraftIndexberechnungCASE <Informatik>Mapping <Computergraphik>DickeAbstraktionsebeneComputeranimation
14:52
OrdnungsreduktionProgrammiererTotal <Mathematik>Puffer <Netzplantechnik>DickePufferspeicherFunktion <Mathematik>Mailing-ListeMAPMessage-PassingZeichenketteZahlenbereichPhysikalische TheorieFunktionale ProgrammierspracheOrdnungsreduktionKategorie <Mathematik>AbstraktionsebeneDickePuffer <Netzplantechnik>Rechter WinkelSummierbarkeitSchnittmengeMusterspracheGeradeObjekt <Kategorie>TaskLoopEinflussgrößeNichtlinearer OperatorSystemaufrufGruppenoperationCASE <Informatik>Deskriptive StatistikAussage <Mathematik>BeobachtungsstudieSchreiben <Datenverarbeitung>Computeranimation
17:53
KontrollstrukturOrdnungsreduktionParallelrechnerMAPKontrollstrukturIterationDeskriptive StatistikGamecontrollerObjekt <Kategorie>ProgrammierparadigmaProgrammierspracheDatenflussBitSkriptspracheTaskBrowserAiry-FunktionProgrammierungFrequenzComputeranimation
18:53
TaskSkriptspracheAbfrageInhalt <Mathematik>Funktion <Mathematik>Exogene VariableFunktionale ProgrammierspracheSkriptspracheCodeLoopSystemaufrufBrowserAutomatische IndexierungSoftwareResultanteEinfach zusammenhängender RaumInhalt <Mathematik>Exogene VariableAlgebraisch abgeschlossener KörperProgrammschleifeRechenschieberGüte der AnpassungRandwertParallele SchnittstelleZeichenketteSichtenkonzeptHardy-RaumRechter WinkelFlächeninhaltComputeranimation
22:03
ProgrammiererSynchronisierungProgrammbibliothekPASS <Programm>Analytische FortsetzungMessage-PassingRechenschieberProgrammbibliothekFunktionale ProgrammierungPunktSchnittmengeSystemprogrammImplementierungMAPFunktionale ProgrammierspracheMusterspracheSystemaufrufAnalytische FortsetzungParametersystemVererbungshierarchieComputeranimation
23:27
Funktion <Mathematik>MultiplikationInhalt <Mathematik>SynchronisierungMAPGamecontrollerSkriptspracheAbstraktionsebeneFunktionale ProgrammierspracheServerMultifunktionIterationSystemaufrufAutomatische IndexierungLoopZahlenbereichEin-AusgabeMAPOrdnung <Mathematik>Inhalt <Mathematik>Mailing-ListeFehlermeldungResultanteUniversal product codeParallele SchnittstelleWort <Informatik>Überlagerung <Mathematik>Grundsätze ordnungsmäßiger DatenverarbeitungZeichenketteGerichteter GraphGruppenoperationDruckspannungMechanismus-Design-TheorieExogene VariableComputeranimation
25:15
Partielle DifferentiationFunktion <Mathematik>DatentypElement <Gruppentheorie>Gebäude <Mathematik>Attributierte GrammatikFormation <Mathematik>ZahlenbereichAbstraktionsebeneCodeKartesische KoordinatenPartielle DifferentiationFunktionale ProgrammierspracheParametersystemTypentheorieTeilbarkeitZeichenketteTexteditorTemplateCASE <Informatik>Formation <Mathematik>ProgrammbibliothekZahlenbereichSoundverarbeitungCodeEreignishorizontGraphfärbungDatenstrukturUmwandlungsenthalpieElement <Gruppentheorie>ProgrammierungSkriptspracheHardy-RaumSystemaufrufBestimmtheitsmaßInklusion <Mathematik>Kategorie <Mathematik>Klasse <Mathematik>MultiplikationsoperatorPunktProdukt <Mathematik>Fächer <Mathematik>Arithmetisches MittelObjekt <Kategorie>Physikalischer EffektWort <Informatik>AuszeichnungsspracheProjektive EbeneCliquenweiteVariableDeklarative ProgrammierspracheComputeranimation
29:49
Hardy-RaumProgrammiererElektronisches ForumFreewareZellularer AutomatFunktion <Mathematik>Komponente <Software>VektorrechnungElement <Gruppentheorie>Transformation <Mathematik>Innerer PunktAbstraktionsebeneMAPVektorpotenzialProgrammschleifeNatürliche ZahlFunktionale ProgrammierungFunktionale ProgrammierspracheBitRechenschieberZusammenhängender GraphObjekt <Kategorie>CodeKategorie <Mathematik>AggregatzustandSystemaufrufRechter WinkelFamilie <Mathematik>ImplementierungSichtenkonzeptFlächeninhaltPhasenumwandlungGeradeTransformation <Mathematik>Mailing-ListeGebäude <Mathematik>GenerizitätLoopMathematische LogikVorzeichen <Mathematik>UmwandlungsenthalpieZirkel <Instrument>Mapping <Computergraphik>Automatische HandlungsplanungCASE <Informatik>Mechanismus-Design-TheorieVisualisierungMAPGamecontrollerZeichenketteInformationBildgebendes VerfahrenProgrammbibliothekMomentenproblemDatenstrukturMereologieMinimumRenderingKartesische KoordinatenProgrammiererElement <Gruppentheorie>Dreiecksfreier GraphBildschirmmaskeComputerspielApp <Programm>MultiplikationsoperatorATMMessage-PassingLeistung <Physik>Deskriptive StatistikSchnittmengeAbstraktionsebeneDivisionInterface <Schaltung>Domain <Netzwerk>Offene MengeKontextbezogenes SystemKomplex <Algebra>Computeranimation
39:34
Funktion <Mathematik>DivisionKomponente <Software>MAPEreignishorizontImplementierungGamecontrollerMereologieGeradeObjekt <Kategorie>Mailing-ListeMAPKategorie <Mathematik>Funktionale ProgrammierspracheSpieltheoriePartielle DifferentiationEreignishorizontTransformation <Mathematik>AggregatzustandZusammenhängender GraphFunktionale ProgrammierungAutomatische DifferentiationFlächeninhaltInstantiierungMathematische LogikMereologieMultiplikationsoperatorResultanteE-MailUmwandlungsenthalpieGüte der AnpassungCASE <Informatik>Gesetz <Physik>MinimumVererbungshierarchieMixed RealitySchreiben <Datenverarbeitung>SystemaufrufAusnahmebehandlungBrowserEin-AusgabeBitMechanismus-Design-TheorieParametersystemImplementierungGenerizitätKontrollstrukturValiditätRechter WinkelKonfiguration <Informatik>Prozess <Informatik>PunktZahlenbereichAutomatische IndexierungMultiplikationZellularer AutomatNichtlinearer OperatorResolventeAbstraktionsebeneGebäude <Mathematik>GamecontrollerSystemzusammenbruchDivergente ReiheKartesische KoordinatenProgrammbibliothekDivisionZeiger <Informatik>Ordnung <Mathematik>DifferenteCodeComputeranimation
48:46
ProgrammiererFunktion <Mathematik>AbstraktionsebeneCOMLastBitAbstraktionsebeneFrequenzWeb SiteFunktionale ProgrammierungBenchmarkBrowserMultiplikationsoperatorTermProfil <Aerodynamik>RelativitätstheorieGrößenordnungMessage-PassingRechter WinkelLoopCodeSoundverarbeitungProgrammschleifeSchnittmengeKartesische KoordinatenEinsZahlenbereichNP-hartes ProblemNichtlinearer OperatorPunktOffene MengeBenutzerschnittstellenverwaltungssystemImplementierungWeb-SeiteImaginäre ZahlFunktion <Mathematik>StichprobenumfangGruppenoperationComputeranimation
52:11
Twitter <Softwareplattform>ProgrammiererObjekt <Kategorie>ParametersystemFunktionale ProgrammiersprachePartielle DifferentiationPrototypingSchnelltasteMereologieComputeranimation
53:16
GamecontrollerEreignishorizontImplementierungParametersystemEin-AusgabeFunktionale ProgrammierungFunktionale ProgrammierspracheVersionsverwaltungMereologieMessage-PassingTVD-VerfahrenFlächeninhaltCASE <Informatik>Computeranimation
54:26
Funktion <Mathematik>AbstraktionsebeneTwitter <Softwareplattform>ProgrammiererComputeranimation
Transkript: Englisch(automatisch erzeugt)
00:01
OK. Can you hear me? Yeah? So thanks for coming to my talk this late on a Friday. You're probably very tired. Hopefully I'll be able to keep your attention for one more hour. So this is going to be a talk about functional programming with JavaScript.
00:21
And I'm going to show you some techniques from functional programming and how you can apply them to your JavaScript, even if your JavaScript is typically object-oriented. And so this will be kind of an implementation patterns talk. My name is Christian Johanson. I work at a company called Gitorious.
00:44
I help create a free open source web application for hosting and collaborating on Git projects. Currently our main focus is on the enterprise or helping companies run this internally. I've always also done a book called Test-Driven Development
01:04
with JavaScript. Has anyone read this book? Someone has read this book. Thank you. It was recently translated to Japanese, which I feel is a bit of an honor. And I think it's going to be a success because it features a book on the cover.
01:20
Check out those sweet colors, baby. This is actually my book. It's quite true. And it's even popular on Amazon in Japan. And I do some open source libraries as well. My most popular one is sign-in.js, which is a stubbing and mocking framework for JavaScript.
01:42
My current project is BusterJS, which is a kind of ambitious test toolkit project that we've been working on for quite a while and will be working on for quite some time more. And then lastly, I do some courses. So if you think any of this is interesting, you want to
02:01
learn more about JavaScript or TDD with JavaScript, I do courses. Check out this website. I do them both in Norwegian and in English. But this talk is really about functions. So this is probably the simplest function you could write in JavaScript.
02:21
I thought I'd start this talk off by introducing some core concepts in functional programming. So obviously, the function is very important in functional programming. But there are some qualities about the function that I also want to emphasize. And JavaScript as a language is very well suited for functional programming because it has all these qualities.
02:42
And the first quality is that this function is actually an object. You can use it as data. That's what's called a first class function. So if I change the expression a little bit into a function that's being assigned to a variable, hopefully that becomes clearer. It means that the function is a value just like a string is
03:01
a value, an object, an array, whatever. So that's important for functional programming. Another concept that's very important that's also related to the title of my talk is something that has to do with the behavior of this function. So you can see that this function takes in two arguments,
03:20
adds them together, and prints the results to the console. So that printing there, that's called a side effect. And it's called a side effect because it's not a direct result of using my function. So it doesn't happen to whoever calls my function. It happens to the screen somewhere else, right? So anything that is not the input to the function or
03:41
the return values is considered a side effect, even if the side effect is the intended effect. So in functional programming, people tend to prefer functions that don't have side effects. And those functions are called pure functions. So a pure function is a function that only accepts
04:02
arguments and works with them and returns a value. It does not touch any global objects. It does not have any side effects of any kind. So obviously, you cannot make a whole program side effect free because that would be nothing, right? You have to have some side effects for the program to have any value.
04:21
So in this case, what I did was I took the console log statement, I pulled it out of my function, so now I have a pure function, and then I can have an impure part of my application that can log this value. So you can contain the side effect for functions in one place and try to write as much as possible using pure functions.
04:43
So this is also obviously the title of my talk, Pure JavaScript, that comes from here. The third concept that's very important for functional programming is higher order functions. And there's two things that can make a function a higher order function. The first one is a function that returns another function.
05:04
So who has experience doing stuff like this? Who has ever written a function that returned another function? Most of you guys, okay, so I'll speed up. Anyway, this function, you give it a number, returns a new function, you can call that with another number, and it will add them together. Very exciting.
05:21
The second thing that makes a function a higher order function is accepting functions as input to your function. So the add event listener method for DOM elements is a higher order function because it accepts the listener function, right?
05:40
You may also know this as on in jQuery or bind or delegate or live or any of the numerous other ways of attaching an event listener. Okay, so these are like the core concepts pertaining to functions in functional programming. We're gonna go through a set of examples now to see how we can use higher order functions
06:04
in particular to create some interesting solutions to common problems. And the first thing we'll do is to look at loops. And my stance is that you should avoid imperative iteration structures like this one
06:21
and this one and all the other ones. You should avoid them as much as possible. I'm not going to say that you should never use them, but mostly it's not the right answer to use one of these. So in order to defend that statement, we'll have to check out what the alternatives are. But first, I wanna take a look at why we use this.
06:41
Why do we loop? So the most common use case for a loop is to enumerate the items of an array or a list and cause some side effects. So again, I'm using the terribly exciting side effect of logging to the console. I probably should have spruced this up and used prompt or alert or something,
07:01
but you get the point. Typically, this will be some call into your application that causes something to happen that's visual to the user or at least meaningful. But the concept is walk over each item in the array and then make something happen. Another quite common use case is this.
07:21
So here I have a list of tweets. Who knows what a tweet is? Why, there's only three people? A tweet, in this context, is an object representing a Twitter user, okay? So this is an array of Twitter user objects. So I wanna take that list and then I wanna produce a new list
07:41
that's just a list of their names. So I wanna take a list of objects and produce a list of strings. This is also a very common looping pattern. Here's yet another common pattern. I have a list of, in this case, some strings, possibly produced by my previous slide, and now I wanna produce a string
08:01
that represents that list. So I wanna take all the items and format them somehow and produce one single string that I can put into the DOM. Okay, so what's wrong with these loops? Well, now I showed you some examples of why you loop. Now I'm going to show you what the alternative is.
08:23
So this guy, the side effects loop, has a, in JavaScript, you have methods on the array that correspond to many use cases for loops. And one of them is the for each method. This is a method on arrays. You call it to basically invoke some side effects
08:41
with each item in the array. This is a specific method solving that problem. In this setup, the array basically loops itself, and then it calls your function once with every item in the array. Okay, so unlike some other languages like Ruby,
09:01
there's not much saved in terms of syntax in this example. So it's approximately the same amount of syntax for both of those examples, right? So that's clearly not the huge gain. So why would you prefer one or the other here? Well, if you take a look at the focus of each of these examples, you can see that the first one is overly focused
09:22
on the mechanics of iteration. You have a starting index, you have a loop condition, you have to keep your index up to date, and indexing your array is very focused on the mechanics of just getting through your array. Whereas the second example has forced its focus onto the side effect. So this is about console logging each goddamn item.
09:43
That's what this does. And that's where the focus should be, because that's what this code is supposed to do. So it's clearer in its intent, and it will also lead us onto a path where we'll be more able to recognize good abstractions. And I'll back that up with some more examples.
10:04
So to summarize, we loop to cause side effects, to transform data structures, to filter sometimes. I didn't mention this, but let's say you have a list of tweets, and then you want only the cool tweets. That's the ones that follow you. So you wanna filter the list somehow.
10:21
Maybe you wanna combine the items, and any other number of ways of using a loop. And often too, you'll find combinations of these patterns stuffed into one loop. So it may be hard to recognize which one of these categories it falls into.
10:42
Okay, so I showed you the side effect loop called forEach in JavaScript. Let's take a look at map, which is the method that corresponds to transformations. So this is the example again, having a list of tweets, producing a list of strings with their names. This is called map, and it can look like this. So you take the array, you call the map method,
11:02
you pass it in a function. Now JavaScript will call this function once for each item, and then the return value from each of those calls will be put into a new array. So this actually produces a new array of the same length as your original one, where the return value from the callback function in each spot.
11:23
So the first user object passed into this callback, the return value there will be the first item in the new array. So this has already taken away a little bit of scaffolding, the empty array that we initialized and so on. And it also helped move the focus, right? We wanna extract the name property.
11:44
And because this function is very short, or we have a short scope, that means we can live with short variable names to still understand what it means. So we can one-line this. And one-lining is a double-edged sword, I guess, but I like to look at the one-liner
12:04
as a indicator of the expressiveness of an API or a language or whatever. So the fact that I can actually transform the list from a list of objects to a list of strings, extract it from those objects in one line, tells me something about the level of expressiveness
12:23
of both the language and this API. And I think this is a good level of expressiveness. So I showed you this one. No, I didn't, this is a new example. It's almost like the previous one. So in this case, I have a list of tweets again that probably mentioned something I'm interested in.
12:41
So I wanna build a string that says, mentioned by, and then username A, comma, username B, comma, username C, right? So this loop is even more focused on iteration specifics because now I also have the check to make sure that we don't add the comma after the last item. So the chances of introducing an off by one error
13:03
in this code example are, I'd say, pretty high. But if you look at what's happening, we're really extracting the usernames from each object and then we're combining them back together with a separator. And those are two operations that are already well abstracted in the language itself.
13:23
So we could use map to extract the properties and then use join to put that separator between each item. So now there is no I variable at all. There's no less than last index minus one. There's no off by one chances in this code. It's just neat, elegant code.
13:42
But it's still split over three lines. So I'd like to improve this example. So to improve this example, we'd have to introduce a new abstraction. So now that we've gotten away with the loop, I have, I'm plugging a function into that map call.
14:00
That function is doing something very generic. It's extracting a property from an object. That's not inherently coupled to tweets. It's not inherently coupled to anything other than objects. So we could create a reusable abstraction that does that for us. It could look like this. So this prop function, you give it a property name,
14:21
which is a string, and then it will return a function that you can call with an object, which will return the name property from the object. Okay? And you can use it like this. And once again, I can one line it, right? Still a very good level of expressiveness.
14:40
And if you read it, it's probably easier to understand it than if you looked at this code. Tweeps map property name, then join with a comma. Cool, I can live with that. Okay, final example is reduce. Who has ever used reduce or fold left or fold?
15:01
Okay, only a few of you. So reduce is the message you use when you have a list of data and you want to produce one new value that's not as directly linked to the original list as in the map example. Typically, you'll take a list of strings and produce a string, take a list of number,
15:21
produce one number, something like that. But in theory, you could produce any kind of value. You could produce a new list as well. The example I'll show you here is a list of numbers. So in this case, I have a set of buffers. And each buffer has a length. And then I want to figure out what's the total length of all my buffers.
15:41
So you bust out your trusty old for loop and the i variable and all that falls. And then you sum up the numbers, right? So this is actually, again, a two-step operation. First, I have a list of objects. I need a list of numbers. And then there's the task
16:01
of combining those numbers into a sum. So extracting the numbers, I can do with map and a function that returns the length property. Summing the numbers, I can do with reduce. So what reduce does is that it will start with the two first items in my array
16:21
and then we'll pass them into my callback. The callback will return one value. Reduce will then take that value and the next item from my array, pass them into the callback, that will reduce into one value. And it will take two and two values like that, producing one value until we land at one single value.
16:42
So in this case, this will actually sum up all the numbers in the array. But we can take this one step further. We can start recognizing patterns. This one, it looks a lot like the first function I showed you. Just adds two numbers. If I already have a named abstraction
17:01
doing that exact same thing, I don't have to really recreate it here. So we can use the name function for the reduce. Now, what about the second one? That looks a lot like the prop function, right? Something that extracts a property.
17:21
And now, guess what I can do? I can one line it. Okay. Again, not actually that important to be able to one line it, but it still is a good measurement. So in this case, you map the buffers on the length property and then you reduce those numbers with the add function. That may sound a bit cryptic
17:41
if you're not used to working with these methods, but once you get to know map and reduce, this is gonna make total sense. Trust me. All right. So that's the example of some iterator methods. So we took a look at foreach, map, and reduce.
18:01
And then there's other useful things like sum, which will tell you if any one item in a collection fits a certain description. You can do filter. There's many other methods. Go to Mozilla Developer Network and check out the array object and you can see all of the iterator methods. What I'm going to do now is to take a look
18:21
at how this iterator method also extends into other paradigms. So if you've done any kind of asynchronous programming with JavaScript or with any other language that doesn't have syntax level support for it, you're gonna be familiar with the fact that flow control is a bit of a pain
18:41
when it's asynchronous. Has anyone felt the pain of asynchronous control flow on their body? A few of you, okay. So let me present you a task, a not uncommon task. We're in the browser and I wanna fetch several scripts and then I wanna combine them and then I wanna preserve the ordering of the scripts.
19:05
And this may sound trivial, but the kicker is that my network connection in the browser XML HTTP request Ajax is asynchronous, or you should use it asynchronously. So the naive way to solve this would be to get the first script,
19:21
wait for it to download, put in the result, get the next one, and so on. But that's going to be slow. So we actually wanna fetch them in parallel or as much in parallel as the browser can handle and then I wanna combine them when I have all the results. So it's gonna look like this. So you give it an array of scripts to fetch
19:42
and then this callback function is going to get called with one string, which is the combined contents of all those scripts. So let's just stumble right into the naive approach and see how far we can get. So I create a data array. That's going to be my results.
20:01
I'll put each result into the array and when we've got all the results, I'll combine the members of the array. Then I bust up my trusty old for loop, loop over the script, and then I do jcr.ajax. So this is going to make sure that the calls are parallel because these calls will just trigger immediately
20:21
that they're asynchronous, so they're not gonna come back until they've finished. The success function, I get the response as a string. So I stick it into the data array in the corresponding index from where I found the script. Pay attention.
20:40
And then if we have all the results we need, we call the callback by joining the data. This slide says yuck. Who thinks this code is not ugly? Good. So if you write code like this, you really should be ashamed of yourself. This is horrible, horrible code.
21:01
And also, it doesn't work. There's a few reasons it doesn't work. One of the most glaring reasons is that we're using that i variable across a function boundary. So we defined it in the outer function, the combined function, and then we're using it inside the success callback. So if you have any kind of experience
21:22
with scoping in JavaScript, you're gonna know that that doesn't really work. So we could work around that by injecting a new scope with an anonymous closure. Now it's even more sexy.
21:40
Obviously, that's even more horrible. One way to solve this would be to replace the for loop with a for each method. Because the for each method already has a callback, right? So that has its own scope. So for loops does not have its own scope. The for each method does. But seriously, this is still horrible.
22:01
This is very, very horrible. And it still doesn't work. If you're curious as to why, download the slides and take a look at it and tell me when you figured it out. So we're not gonna do that. We're gonna use the library instead. And the point here is that async
22:21
is a very neatly little library that defines a set of utility functions. And basically what it is is asynchronous implementations of all the iterator methods. So for each, map, filter, all those methods, implement it in an asynchronous fashion. So how is it able to do that?
22:41
It uses something called CPS, which is an abbreviation from functional programming that stands for continuation, passing, style. Have you heard this before? This is just a very, very fancy name for a very simple concept. It describes a pattern where your function
23:01
accepts a callback as its last argument. And when the function calls that callback, that's the signal that it's done. So the return value is useless because the function is asynchronous, but when the callback is called, the function is effectively saying, I'm done. So we almost have this with the jQuery Ajax thing,
23:20
only that the callback is called success. So it's not directly CPS friendly. But we can make it CPS friendly by simply wrapping it like this. So the Ajax function now has a callback as its last argument, and when that callback is called, it means that we're done.
23:43
So the clever people in the room will notice that I'm not doing any error handling, and so this is just an example. This is not production code. All right, so using this CPS friendly function, I can now use async's map, because I actually want to take a list of strings,
24:03
script paths, and I want to map them to their contents on the server. Doing that looks like this. Async.map, then you pass in the collection, which is an array of scripts or path names. Then you pass in the asynchronous function, which is called Ajax.
24:21
And then you pass in a callback, which async will call when all of your calls have completed in order. And what it does is basically the same thing that I tried to do previously. It makes sure that the contents is an array where the result in index number zero corresponds to the input in index number zero.
24:41
So it maintains that responsibility, but it can do it in parallel. So I could have done that previously, but this function is not called map, it's called combine. It's supposed to combine scripts so that those mechanics, I've taken them away, put them somewhere else, and I'm using a higher level abstraction.
25:00
And now you can see that I'm able to actually reuse my knowledge about the iterator methods to do something like asynchronous control flow. Something that I cannot do with a simple loop. Righto. Okay, we're now going to move
25:21
into partial function application. And I want to ask you, how many here has any experience with partial function application? Okay, so for this exercise, I'm going to show you this API. This is from a project that I did with a nice fellow sitting over there.
25:42
This is a DOM builder API. So the call is just the name of a library that we wrote. So call.dom.l creates a DOM element, just like document.createElement does. But where it gets interesting is that you can pass it attribute properties like this
26:02
to set the class name or ID or whatever. And you can also pass it children. And children can be a string, which will cause call to create a text node, which will escape markup and script and stuff like that. Or it can be other elements or basically any kind of node.
26:23
And the reason we built this API is that none of us are really that much fans of client-side templating. So we tried to create a nice API to create DOM elements and use that instead of templates. Because that means that we can still lean on our editor for syntax highlighting, indentation,
26:41
and all the stuff that's nice about not being inside strings, right? And you can nest them to get the width of something looking like HTML. So, small detour. Partial function application is where you produce a new function from an old function
27:01
by prefilling some arguments. Remember this, I showed you this very early on. This function creates an adder function. So if you refactor this a little bit and extract the return value here and do this, now you can see that make adder, what it basically does is that it gives you a function
27:22
that prefills the first argument to add. And that's basically what partial function application is. So with call, it will look like this. Call.partial, you pass it in a function, in this case the add function, and then as many arguments as you need to prefill.
27:42
In this case, just the first. And this will return a new function, in this case that adds two to any number. So using partial function application, we can actually take our DOM creating, DOM element creating API and produce a pretty neat DSL.
28:03
So you can partially apply the element builder with some tag names to get methods that are specifically creating specific types of elements. And then it looks like this. And now you can see how we can replace templates with this kind of code. It's basically the same structure, but we keep the programmatic approach,
28:22
and you can also include event handlers and stuff like that in here. However, we were using this all our application, and we didn't really want to repeat this step all over the place, like recreating the DSL. So that meant that at some point we decided
28:41
to just bundle the whole DSL thing inside the library itself. And we did that this way. So we took all the tag names, we enumerated them with forEach because we're gonna cause side effects with each of them, and the side effect is to define a new function inside a call.dom.l object.
29:03
So that means that the DSL is preloaded, but now it's gotten cumbersome to use because I have to prefix every tag name with call.dom.l. So you can minimize that problem by using a local variable. So instead of recreating the entire DSL, you can just throw this variable declaration
29:21
in where you want to use it, and then it looks like this. It's not as elegant, but it's elegant enough, and it's very practical, and it's very limited on duplication. So it's a trade-off from all words. Not too much duplication, pretty enough. You can train your eyes to just ignore that e dot
29:41
in front, so yeah. So that's one usage for partial function application. Composability, so this slide is actually a bit ambiguous because composability can mean two things, and I am going to talk about both of them a little bit.
30:01
So one thing is that functions, at least the kind of functions that I've shown you so far, small, simple, generic functions are highly composable by nature, and then there's the more technical aspect of function composition. If you're confused now, that was kind of my intention. It wasn't really, but I'm gonna clear it up.
30:24
I know that this slide is very disappointing. It doesn't have any code on it, but still I have to give you some context for this next example. So this is the app that we were working on when we wrote this code. So it's a fancy form, a multi-step form.
30:42
And in this case, it has five panels. You can see that the first two panels are closed. We're done with them. And we're currently working on the third panel, and then there's two more panels coming up. And the way we did this in our code is that we created panel objects. And a panel object can have two view states.
31:02
So that only accounts for two of these states. The two to the bottom that are not initialized yet are not initialized yet, so they're not part of this life cycle. So the two view states are the summary mode, which is the mode you see on the first two steps, and the second mode is the editing mode,
31:22
where you can, yeah, edit. And each panel has a collection of components. Very generic terms here. But a component, like in the open step here, is like the title, that's one component. It can be validated and saved on its own. The description is a component.
31:41
It can be validated, saved. And then you have two complex date components, and there can be any amount of components inside a panel. When you hit the bottom to the lower right there, it's gonna push the panel into the summary mode and open the next one in editing mode.
32:02
So what I'm gonna show you is the implementation of showing a panel in summary mode. So there's actually more than one implementation of this code because some panels are different, but this is like a generic implementation that's used for 80% of these panels.
32:22
So I have to remind you of this function, the prop. That gives me a function that takes an object and returns a name property from it, right? In call, we also have this guy's brother, which is called func. And func gives you a function that calls a function on an object and returns the value.
32:41
Almost the same, right? So to render the summary for a panel, ta-da, looks like this. Once again, it's just one line. So we're using the element builder interface to create a div. And then we're passing in an array, which means that will be the children inside that view.
33:03
And what is that array? Well, it's all the components mapped with the build summary func. Basically what that means is that it will loop all the components, it will call build summary on each one of them, expect to get a DOM element back, which means that we get a list of DOM elements,
33:20
one for each component, which will be placed into the div. And this is my first definition of composability. So we have a set of simple, very generic functions that can work together, compose, to create a very highly specific abstraction for my problem domain.
33:42
But all the implementation behind this code has nothing to do with my problem domain in specific. Which is a good sign, because that means that, okay, this is UI, so it's not that important, but overall this means that my business logic is going to be clear and visual in the code,
34:01
because all the mechanics are stuffed away in another library. Okay, so to take this example one step further, we actually had a couple panels where this implementation did not really do the trick. So the build summary returns a DOM element.
34:21
So that allows the component to basically render itself. In some cases that was not the desired approach. So the components also have a method called get summary. And get summary returns a plain object that has some various properties. It has a text property.
34:41
In some cases it has some images and other kinds of stuff. But basically it's the same information not rendered as DOM elements. So one of our panels needed to use this one, because it specifically wanted to create P elements for each component. I'm just going to show you how we did this,
35:02
the naive approach first, and then we're gonna try to clean it up using some of our more higher level abstractions. So you create a div element for your container, and then we have a P variable which is going to be used for each one of the components. Bust up my trusty old for loop, loop all the components.
35:21
And then you create a P element, and then you call get summary on the component and access the text property. That's the text we're looking for. Stick it inside the P, and then append it to the div. Does more or less the same thing, except that now the panel is in control of how the components are rendered.
35:41
In the previous example, the components themselves were responsible for their own rendering. But this is not good enough. We kind of live with this kind of code. So, okay, my first approach, if I'm not able to recognize what kind of looping we're doing here, I always reach for four each just to see if that can get me going.
36:02
In this case, it doesn't really help. So let's try another approach. We can use the element builder for the outer list and then use a map, because we're still taking a list of objects and converting it to a list of DOM elements. It's just that the transformation is a little bit more manual now than in my previous step.
36:23
So this is not too bad, but the two of us working on this code are refactoring nerds, and we cannot stop at this. So let's try to analyze the transformation here. I want to call the getSummary to get the summary object. I want to extract the text property from that object,
36:41
and then I want to create a P element that contains it. That's what we're trying to do. So let's try three maps. To do this in three steps, right? So first we take all our objects, and then we call getSummary. That produces a list of summary objects, and then we map that list
37:00
by extracting the text property that will create a list of strings, and then for each string, we create a P element. So this is, I don't know what to say, this is intellectually satisfying, but now the code is uglier and more verbose than it used to be. So it's both better and worse at the same time.
37:24
But what if we could use named abstractions? I already have some of these concepts implemented before. Like calling a method, that's called func. Extracting a property, it's called text, prop. And then that last one, creating an element and sticking some text inside it,
37:42
that's what we have the element builder for. So now it's looking a little bit better. This is actually a pretty elegant solution in my mind. But it may be a problem, it may be a problem that we're basically looping this collection three times.
38:01
It doesn't have to be a problem, it may be a problem. Let's say that it was, how could we keep the elegance of this approach and avoid code like this, but not loop it three times? That's where function composition comes to play. Take a look at this. I'm going to explain this in detail how it works,
38:22
but this is basically the solution. So compose is another function from the call library that takes a set of functions and creates one function that calls all of them. So to get a better grip on this, let's give some names to things.
38:41
So that func getSummary, that's actually called call getSummary. That's what it does. And the second one is getText, that's what that does. And then we have summarize, which is the composition of call getSummary, getText, and the p function. And what happens is that when you call that summarize function, let's say you call it with an object,
39:02
the first thing that happens is that we call call getSummary, which means that object.getSummary gets called. That will return an object. That object will be passed to getText, which means that we will extract the text property from the summary object. That string is going to get passed
39:21
to the p element builder, which means that we will create a DOM element with the text inside. So we now have one function doing three transformations. And I can plug that directly into map. And now it looks like this. And now we're down to two lines. It's starting to get that worn tingly feeling in my stomach.
39:41
And now, take a look at that compose thing. That doesn't use anything that pertains to my object in specific. It's just completely generic, simple functions, which means I can get it out of there. I can prepare it ahead and give it a name, and then I can reuse it as many times as I want.
40:01
It doesn't know anything about my panels or components or whatever, it's just a composed transformation. And now, build summary. Once again, one line. Okay, but really, I'm not really overzealous about one lines, but it does feel good
40:21
when you get to reach something like this. Okay, so we have some more time. I'm gonna show you one more example. Oh, by the way, so you can see that in this case, we're calling this.components.map. That's the native way of doing it. So that's only supported in certain newer browsers,
40:42
which means all browsers in use, except for the old IEs, of course. So our call library has its own map, which does work across browsers, and it's a little bit different. You can see that in this case, we have the collection, map, and then the transformation.
41:00
In call, we reverse the ordering. So we have, obviously, we have the map first, but then we have the method first, and then the collection, rather than the other way around. And the reason is that if you're using an inline function, then it would look better if you had the function last, because then you don't get line breaks
41:20
in the middle of the call. But when you're mainly using named abstractions, having the method first makes more sense. And you'll see why if you try to read this out loud. So this is the div containing a collection where you map, summarize over components. Summarize components.
41:40
Yes, thank you, that's what I'm doing. So if you're using named functions, this ordering works better. If you're inlining the function, the other ordering probably works better. In our case, most of our uses use named functions, so this made sense. Okay, last example. So in our app, we're using a lot of mixins.
42:02
Who knows what a mixin is? So roughly, a mixin is... You can think of mixins as multiple inheritance, basically. In this case, we have some mixins. They are objects that implement some functionality,
42:23
but they don't have state of their own. So it's an abstract, or it's not abstract, it's a concrete implementation of some functionality that you can mix into your stateful object to gain that ability, right? So we have components and panels and stuff.
42:42
So we have composite mixins, save composite, validate composite, and event emitters. So these are generic implementations of logic that deals with saving, for instance, because saving is asynchronous, so the mechanics of saving is trapped inside the save composite object.
43:01
So if you need to create a component that can be saved, you can mix in that object, and you're in the game. The event emitter is something that allows you to register the listeners and emit events. Okay, so there's a problem. So the approach that we took in JavaScript
43:21
is that we have these mixins are just objects, and the mixin function just takes a number of objects and combines all their properties into one new object. But then you may have, there may be a crash. If more than one object defines a method of a given name,
43:40
then the last one is going to win. Like, take a look at this example. So here I'm creating a controller, which is a mix of five objects. It's the composite, the save composite, the validate composite, and the event emitter, and then it's my concrete implementation, which has some state and interesting things, right? So if more than one of these objects have,
44:02
for instance, the add function, then the last object in this list is going to overwrite the existing add function. And that's actually the case here. Both the save composite and the validate composite have the add function because they need to do stuff when you add subcomponents.
44:23
So, no good. Luckily, we can use function composition to work around this problem. So this is what we did to solve it. So we have our mix in with our four generic objects, the one specific object, and then there's one inlined,
44:41
weird little object to the bottom. So we're using the fact that we're writing properties from left to right. So the last object will overwrite the existing add property. And it will add a function that will call both save composite and validate composite's add methods. So there's one requirement for this to work.
45:02
Because the input is piped into one function, returned to the next one, you have to make sure that all the add functions return their argument, so all the functions see the same argument. If that doesn't work, you could create another composed method that gives the input argument to all the functions
45:21
rather than chaining them. So you have many options here, but that's one way to do it. We opted to just make all the add functions return the components they're adding, then we can map it like this. So this is pretty good. It's not perfect. So what if at a later point we decide
45:41
that we need to add an add method to the composite object? Then I'd have to backtrack and update everywhere we have code like this to include that in the list. That doesn't really sound good. So we need to be able to just take a generic list of objects and combine all the add methods where they exist.
46:02
We have to account for the fact that in some places there is no add method. So now here comes the Chrissy bar. This is the actual solution. Again, we have a list of objects, four generic objects, and one specific object,
46:21
and then we push in our composed add. And what's that? We compose, and then we call map def. What's that? Map def, what's that? Okay, map, right? You take a list and then you produce a new list. Map def does exactly the same thing, except that it skips all nulls or undefines.
46:43
So in the original list, if any of the items are null or undefined, they are never passed into the transformation function. And also on the other side, if the transformation function ever returns null or undefined, it's also rejected. So this will only produce a list of the defined functions. Basically, all the add methods are available anywhere.
47:05
And just for fun, I'm gonna show you how we built map def because that's also just a series of deferred implementations. This is what I said. So the first helper we have here is the define function. So the define function returns true if its argument is not undefined and not null.
47:25
Simple function, right? And then we have a function called select, which is also called filter in some implementations. So this works this way. You give it an array. In this case, I have an array of numbers, one, two, three, four, five, six, seven, eight. And you give it a function,
47:40
and for every index where the function returns true, the item will be included in the new array. So this will produce a new array with the members two, four, six, eight. So how can I go about to take a list and only select the define parts?
48:00
I can use my define function and then the list. So select defined, that's a pretty common operation. Let's just use partial function application to create cell def. So cell def is a function you can pass to the list, and it will give you all the items except the nulls or undefines. And then map def uses cell def on the original collection,
48:25
does the map, and then applies cell def once more on the result, which will completely eradicate any nulls or undefines on any side of this part. Okay, so that last part is just for fun.
48:41
Who thought that was fun? Ah, come on. All right. So one little note before I quit. Often when I talk about these things, particularly when I tell people to skip their loops and use the method instead, people start bitching about performance.
49:00
So let's take a little bit of a look at performance. So this is from a site called jsperf.com. So if you don't know it, it's a very simple website where you can create benchmarks, mostly to compare the relative performance of stuff.
49:20
So I created a loop and piped it into jsperf, and it tells me that with this code, my browser can get 5.2 million operations per second. That's probably good. If I use for each, that number is reduced to 1.5 million operations per second, and that sounds terrible.
49:41
That's a lot worse. What about reduce? Basically the same story. So that doesn't really look too good. But then I remember that my app, it has a UI, which is using the DOM API. Okay, how does that compare? Oh, right. So that's 56,000 operations per second.
50:02
So that's orders of magnitude slower. So this is a bit of a joke, but what should take with you from this is that loops is probably not going to be the bottleneck in your application. It may be in some very hot spots,
50:22
but most of the time your rendering stuff is going to be your main problem in terms of speed. So of course you should keep an open mind, pay attention to what's happening, and use profilers and benchmarks to keep tabs on your performance, but don't prematurely optimize too much.
50:43
So this stuff is really out there compared to some of the DOM stuff and other browser APIs that are pretty expensive to use. So this brings me to my conclusion. Hopefully I've shown you a set of techniques.
51:02
Hopefully every one of you saw at least one new thing that you hadn't used before. And the conclusion is simply this. Whenever you can, try to prefer pure functions. Try to group the functions that have side effects away from the functions that don't.
51:20
Because the ones that don't are easier to reason about, easier to test, and just easier to deal with. And when you can, prefer generic abstractions. Like you've seen in all my examples, most of the code has been very generic, very isolated and simple, and then they can be beautifully combined to make very specific implementations.
51:44
And also, related to that, prefer small abstractions. Smaller abstractions are more likely to compose well. So if an abstraction does too much or is too specific or basically just too weird, it's gonna be hard to compose it with other stuff. If it's very simple and very small and very generic,
52:02
it's going to be easy to compose with other functions to create interesting stuff. So that's my main message for you today. And with that, I want to thank you for paying attention for so long. And are there any questions?
52:30
Is there any reason I'm using call.partial instead of function.prototype.bind? Yes, I don't care about this. Or more importantly, I don't want to fix this.
52:41
So bind does partial function application, but it also fixes the value of the this object, right? The partial function only fixes the arguments. So it maintains the dynamic binding of this. So when you want that, you use partial.
53:00
If you don't want that, you can use bind. More questions? Yes, I think I do. You're thinking about this one.
53:22
So the part about the composing in the arguments here is that the compose function will create a function that when you call it, it takes the argument and passes it first to the validateComposite.add. Then it takes the return value from that function and pass into saveComposite.add.
53:41
So if you want this to actually work, both of those functions, or at least the validateComposite, have to return its argument. Otherwise, the saveComposite.add is going to get undefined as its input argument. In this case, only going to work if you have one input parameter, that's correct.
54:02
But I also said that if that doesn't really work out, you can create another version of compose that will take the original arguments and pass to all the functions instead of chaining them. And then you could return an array of return values. There's many variations over this theme of composing functions, this is just one example.
54:24
More questions? No? Okay, have a great weekend and thank you for your attention.