Opening title So first up, we have Rob Howard.
Rob's a software engineer at a company called Ambiata. And he's going to tell us, not the things that you can do, but all about the things that you can't do. So to kick that enigmatic session off, please welcome Rob Howard.
(applause) - Cool, so I bet you've been wondering what the hell this title is actually about.
Well it's actually pretty simple, I'm gonna break it down for you right now.
Things you can't do include you can't hold us, you can't stop the feeling, you can't wait to be king, and my personal problem, I can't figure out when the hell to tap off on these damn trains.
(audience laughter) Anyway, so hi, I'm Rob.
And I'm gonna talk to you about some things you might have heard of recently.
ES6, Flow and TypeScripts, functional-style programming and immutability. Kinda really, some of the point of it all.
I'm gonna try and start this easy, but I'm gonna ramp it up over time, so don't worry if you get a little bit lost. This talk is kind of a smorgasbord of different things. Whole bunch of little jumping-off points for you to have a look into and try at and see for yourself. And there will be a link at the end in the final slide, which will basically give you a bunch of places to start looking.
So, without further ado, let's go to the first slide, Loopy-Doop, I honestly couldn't think of a better name. So, please, just don't hold it against me, but we're gonna talk about it right.
So, I've got an array here, it's got three items, one, two, and three, and so we're gonna loop over this. Now, who in the audience has seen one of these things before? I'm honestly curious, I came across some PHP in 2001, so my baseline is way off.
For the people who aren't familiar with it, all we're doing is we're gonna loop through the items and it's gonna start by making a variable I. I'm gonna assign it to zero, which is the point of the first, like it points to the first item in the array. In this case, it's one.
What we're gonna do is we're gonna loop until we get to this other case, where i is reaching the end of the array.
We've run out of elements to point to, and along the way, we're just gonna keep bumping up i until it's done. For every item in this, we're gonna print it out. So, we're gonna look up which item we wanna print out, and then print it out with console.log, pretty simple. So for our one, two, three, we're going to just print out each of the items in the array.
And that's pretty simple.
So, this is alright, but it's pretty easy to screw up. Anyone who's done one of these things before may accidentally use the wrong comparison and something or you start at the wrong variable, so you get a falloff at the end, or in this case, we started too late in the array.
And so, it's kinda really hard-- It's error-prone, it's easy to screw up.
So, it has too much power.
It is really powerful, for example, we can go backwards through an array.
We can start at the end and work backwards, which the other constructs can't do without copying the array, or go through it backwards. So, this is kinda handy, but when we think about all the things that for could possibly do, it could represent by this circle, and we care about, for our purposes here, for printing out things, that this much of it.
We don't need all that.
So, let's look for a simple version.
So, we've got another thing we can use called, for with in.
In is just giving back a list of keys or indices, like zero, one, two, which is appointed to our items, one, two, and three.
And it's pretty simple to use, it totally works. It's what we're after here, great, except it totally works with everything except for where it doesn't.
Who's personally run through this one before? Yeah, so it turns out in acts a return some extra stuff, like zero, like in this case, zero, one, and then some other things that function.
Whatever, you can fix it by doing this.
It's just, it's something that's just too-- It does too much for what we actually wanna care about, which is looping over lists of things and printing out each one.
I mean, can we just please have something that's simple and works for that? We can, it's something called forEach.
We got an array, so we got Array.forEach, which takes a function.
So we're gonna go over the array and for every item, it's gonna take the first item, give it to this function, and it's gonna print out the item.
So one, print out that, print out, print out, and we're done.
And we've basically hit the sweet spot for what we actually care about.
We shrunk down all the possibilities down to the smallest thing we kind of actually care about, for our particular example.
So, let's go on to another example.
We've got an array, one, two and three, and we wanna turn that into another array, like a two, three, four.
We're gonna do this with forEach.
So, we have added a destination array, what's gonna be the result of this entire thing. And then for every item in the original list, one, we're going to take this, add one to it, and push it onto the new array.
And so, we're gonna repeat this, and then we're gonna print it out, the result, the end. So, one, two, three just transformed into two, three, four. Great, it's cool, but again, it's kind of easy to screw up. I've personally done this more times than I care to admit. Concat push modifies this new array, whereas concat is just returning and just goes away into the ether, somewhere. Or you could target the wrong array, you can push it on to the wrong array accidentally. For what we actually care about here, it can do too much, it's a little bit too powerful. We can choose something that's a better fit for this. So, instead of something like forEach, we can have something like, and we can use something called map.
And map is a function on an array itself, and what it does it takes a function and what it's built for is going from an array of sequel size and shape to an array of the same size and shape.
We're just doing a transformation on the individual items. And so, for every item, we're gonna go through and take the return value of that function and then go and put it in the array.
Much, much harder to screw up and it's basically the sweet spot for what we wanna do here, transformation of numbers to numbers, or whatever sort of calculation there.
And there are all sorts of things, like this is a filter, which is something that takes a list of things and turns it into a list of things that is the same size or smaller.
Like, great! So, it's like a fit for purpose, sort of thing, for doing that sort of operation.
Other things like reduce, which is like taking an array and shrinking it down to, in this case, we're gonna sum up the entire thing.
All these reduce power fit for purpose sort of functions that hit a particular sweet spot. So, this is sometimes called functional, sort of, style programming because these things tend to crop up a lot in the functional languages and we can sorta take them and run away with them and use them for ourselves. And it's kinda helpful and useful.
But, I'm gonna press on.
We got ES6.
I kinda lose track.
Can anyone, you know, anyway, whatever.
So, we've got this for-loop example again.
And we have i, our variable.
The thing is, i is useful inside this block, but then through happenstance, it just happens to get a value at the bottom here, which I was never three inside this loop.
It doesn't make any sense for i to be three. So, we still have access to this i, even though it doesn't make any sense to use it anywhere, ever.
So, we've got something good in ES6 called let, where we can say, scope i down to just the block. 'Cause you know otherwise, it'll escape and run across the hillside and cause a destruction to mankind, I'm assuming. So, we're gonna lock it down into just the full lock, and you can't use it outside of that.
And if you try to, you won't even run.
Like, this is my goal, is my error is to stop the programming even running in the first place, like holy grail, cool. So, thankful I can do it with this.
Not much else, but this, minor victories.
And so similarly, we've got const where we say that i is equal to one, and we say that i is equal two, and const is like, "No, no, i is one, chill out." You're already told this, stop, so, that's also useful. And again, it's not gonna let your program run if you happen to screw this up.
You have to assign something twice in the same function. Once at the top and then further down.
If you happen to forget that you already assigned it and causes problems and things.
It'll just stop that one in its tracks, like nice! So, let and const.
Pressing ahead, immutability.
So, I've got an example here.
And I spent a lot of time on this, particularly this function here, bigList.
It's a very expensive operation.
It gives me back an array of 12 items, one to 12.
I'm very proud of it, it takes like 40 seconds to run. I'm looking at this example here.
So we've got even numbers, which is pulling back all even numbers from that list, and ends in zero, pulling back all the tens from the list. And that's fine, but re-factor alarms are going off in my head, like, oh my god, don't repeat yourself.
I'm repeating myself like I'm in next line. Whoa, I gotta fix this.
So, what I'd do is I'd pull it out to list to bigList, and then I'd go and feed it to even numbers. And then I'd feed the same list that ends in zero. And it totally works, except for where it doesn't. Something happened, what happened? I don't know, so it's gonna find out.
It turns out that even numbers is a terrible function. I also write this just, you know.
The code is, suffice it to say, is changing the array that you gave it.
It took in a list and it did something to the list and returned its results, but you got a list that uses the list somewhere else, and it totally doesn't work.
There's something called immutability.
Immutability is where things can change.
Either things that you intentionally change yourself, or things that unexpectedly change, like in this case here.
And it's like, you're lending a lawnmower to a neighbour, or something, and they use it and they mow the lawn, and they take out the engine and give it back to you. You're like, thanks, thanks, that's wonderful, what? So, we can try to prevent this sort of thing with this particular library called, Immutable Seamless, it's by Mr. Feldman.
And what it does is, we go and do our big expensive one to 12 calculation thing, and then we wrap it in something.
Let's us, if we try to modify the array, it will totally explode and say, "No, no, no, no, no, you should never actually do this." As opposed to finding out, much much later, it tries to front load the problem.
There is an error somewhere, but it tries to say, "No no, stop, you shall not pass." And so, we run out of code and it totally does explode, like fantastic, that's what I'm after.
I mean, I would like if I couldn't even run this script if it had this sort of error, but you know, this is the next best thing.
So, it should hopefully show up on a test, or what have you. So, this is seamless-immutable, which is one example, but there's other things as well.
Immutable.js by the Facebook people.
When you have these immutable objects in order, creating a new version, you tend to copy the entire thing. And what immutable.js, lets you have your immutable cake, but also create this, copy it more efficiently. It's all hard to use, the API is a little bit weird if you're not used to this sort of thing before. And so, I'd definitely try it.
Immutable.js is really good for things like Redux, where you're continually copying and changing a subtree, changing a subtree just over and over and over again. Anyway, have a look at both of them.
See what you like, give it a try.
Moving on, so, The Kinds of Things.
I write some terrible code.
I've already shown you some terrible code, but this is really bad, like sometimes, I don't even know how I stay employed.
I mean, just have a look at some of this stuff. It's just breaking.
I mean, some of the terrible, actually, is that terrible? Is this gonna break? Do we know if this is gonna break? We've got this reverse string here, which is like saying, it should be reversing a string. Who remembers Hungarian notation from 1998 or something, hands up, yeah old people, woo. I really don't miss Hungarian notation.
So, let's have a look inside this function. We've got a reverse string here, it's got some x and the things we're doing to it, kind of, infers that it's a string.
But I had to look inside the function to actually know what it was taking, what it was returning out.
It'd be really nice if we could write this down and then have someone checking this stuff for it. Someone at our backs.
Computers are really good at that sort of thing. So, we're gonna use a tool, called Flow and TypeScript, as well, are good for this. The example is gonna be in Flow.
We're gonna use Flow to encode this, to write this down and then have the computer check it for us, so for example, this example here, it takes in a string and only a string, not undefined, not a number, not a null, not anything, only strings, and we'll always only ever return a string and never undefined et cetera, et cetera.
It's really nice to make this reverse string function more fit for purpose.
It's constraining the inputs it can get and what it can produce.
And so, we run it with hello, totally works. This example doesn't work.
And this example doesn't work.
Like great, it's what I'm after here.
So, what I'm saying, like checking it.
So, we've got our file, I'm just gonna cat it out here. You're going to use Flow in the command line to check. So, it's gonna go look at our code to see if it's consistently lines up, see if we're using it correctly.
And it says you have zero errors, you're a wonderful person. Thanks, cool.
So, and then we can go around the code itself and it produces whatever the hell have you, hello backwards. Or we give it something that's intentionally bad, for example, we've got nine, eight, seven, as our input here and it says, "No, no you can't do that, stop." And tells me exactly where I screwed up and exactly how I screwed up, which is perfect, magical, it's exactly what I'm after.
So, we've got our reverse string example, which is all very well and good.
But we can do other things as well, for example, this sort function takes an array of numbers and returns an array of numbers and again, it's not allowed to do any undefined, or chuck anything else in there.
And so, it totally works.
The string definitely doesn't, it wouldn't even compile, it wouldn't even get past the checking phase whenever it'll start to run.
And so we can constrain this even further.
We can make, in this case, reverse is operating on array of act type, that it's not allowed to know, and returns an array of that same type.
For example, an array of strings to an array of strings. Array of numbers to an array of numbers.
It's not allowed to know what that type is. It's intentionally said, "It's a thing, you're not "allowed to know, so therefore this function isn't allowed "to do anything, but operate on the array itself." It's not allowed to know about what the elements are, so in order to produce working code, code that will actually compile and pass the checks. It's not allowed to actually touch any of the items inside the array.
So, it can swap things around, we can delete elements. We can totally write reverse, but we can't just add one to every element because we felt like it.
We're reducing the power of what this function can do with. Basically, making it fit for reversing.
And so again, we've got, one, two, three, or we can sort reverse characters, or whatever it does.
It doesn't matter at all 'cause we're only operating in the array itself.
So, we've got all the constraints we can have. For example, we've got greet, take some x, and we're saying that x is an object that might at least has the key username with the value that's a string.
And we're using at a side, and we started greeting Mr. Valjean and we're passing an object which includes the username, which includes his name as well an ID code, that he totally never refers to anymore.
I'm sorry, there will be no more musical jokes in this talk, I promise.
So, we're gonna talking about Nullability now. So, we've got first paramater which takes the string, and again, no undefined or anything like that. And the second paramater, which maybe a string or it also might be undefined, at which point, Flow says, "You need to check that, I need to make sure "that you're checking for an undefined thing in here, "otherwise, I'm not gonna let you run your program." So, it makes you check.
Handle the case, where it is undefined.
Otherwise you've got the other one.
So, in this case, we've got default to with if adjective's undefined in this example here, it'll use brave, otherwise, it'll use the adjective. Again, it only operates on these little constrained values. And so, I'll get to the last of these examples. We can specify different types, like this thing might be a Boolean or a number. And this is a pretty silly example because it's like a string representation of the Boolean or number, whatever.
You're not gonna do this in real life, but you're gonna do this in real life.
This is Redux, so I've got an action at the top here. And the actions may be like, a type of LOGGED_IN with some additional user information, or type of LOGGED_OUT.
And with our reducer here, we're gonna take a state in this action, and not only are we gonna check that we spelled LOGGED_IN correctly, we can only use user, the user information from inside the branch where we've checked where it's been LOGGED_IN. It's checking the structure as well, it's just little string names and things.
Incredibly handy and the larger your application is, the more useful this really is.
So, taking it back to our initial reverse string example, string to string, let's go, it seems like a pain in the ass and it kind of is. So, it can figure out most of the stuff for itself. It can figure out that this is actually talking about a string or at least something that has a .split, a .split that returns an array.
As long as the types line up, it'll start working. Sometimes it gets ambiguous though.
So we got add, it adds two numbers together, x and y. Adds them, returns them, so you know, one and two totally work, a string and two totally-- Damn it, we're back to our big circle.
It turns out the operator plus, it does too much.
We don't actually care about this string halfer, we only care about the numbers 'cause we're adding in this case, it's a string operator, but really we just could have meant the number one. So, we tell the computer, "Okay we actually only "meant numbers in this one." So we lock it down, we constrain it further. We get the computer to check our assumptions here that only works on numbers, we encode it, get the computer to check it for us and we're on our way.
Back to the sweet spot.
So, that was Flow and TypeScript.
So, I'm gonna wrap up this talk now.
And throughout all this, I've been referring to things in terms of being fit for purpose. I just described this as most restricted this little least powerful goal and it has a different name. It's called the rule of least power and it's about the least power you can get away with to solve your problem.
It's about not reaching for the biggest most powerful stick, as a first.
And so, you're not reaching for that stick until you really need it.
And there are things that you use everyday that are the result of this rule of least power sort of thinking.
So, think about JSON, there's no logic to speak of. It's just a way to encode information.
But to read JSON, you don't need to SandBox JSON to read the value correct without stuff going wrong potentially somewhere. And HTML is a similar sort of thing.
It's a markup language intentionally reduced in what you can say in HTML.
And CSS again, most of this-- This is pretty much the same as the other ones. CSS can't read from a file, it can't loop forever. It can't even generate a random number.
You have tools that can generate CSS, but CSS is a restricted thing.
It's a little language for the browser and for other tools to interpret as well, like PostCSS. It's intentionally restricted.
But SQL, which is a language that, weird extensions aside, is the least powerful thing they can come up with to express querying and inserting and updating. So back to our examples.
So, we used forEach to do our restricted loop, where we only care about going through even items once and not dancing around between different things or reversing it.
We used map to when we always want to return things in the same shape that you put into it.
You used let to where you wanna scope things down to where they're only useful. You used const if you never want to reassign something. You used immutable for stuff you want to generate once and never change and you want to restrict your code to accepting and returning the smallest thing it could possibly do to do its job.
'Cause we're about using the least amount of power you can get away with to get the most predictable result, and it's the things that you can't do with your chosen tool that makes it harder to misuse, which means fewer bugs and fewer screw ups. This isn't a call to action, so much as a call for consideration.
Think about making your code as hard to misuse as possible 'cause everyone has enough to think about already. I'm Rob Howard, I work at a place called Ambiata. And if any of this sounded interesting to you, come and have a chat with me later.
Here's a link that'll point to all of the things that I mentioned today.
Thank you very much.