THE HANSELMINUTES Episode 583 [PODCAST]
From OO to Clojure Workshop!
Watch my free workshop to help you learn Clojure faster and shift your paradigm to functional.
I want everyone to know that they, too, can get a job as a functional programmer. While functional programming might feel intimidating, in this show I shared with Scott a number of practical techniques and ways to think about functional programming that might just help you with a change of career.
**Scott Hanselman**: [00:00] Hi. This is Scott. I really appreciate our sponsors because they make the show possible.
[00:05] Today's show is sponsored by Developer Express. Become a UI superhero with DevExpress Controls and Libraries. Deliver elegant .NET solutions that address customer needs today. By leveraging your existing knowledge, you can build next-generation touch-enabled solutions for tomorrow.
[00:21] You can download your free 30-day trial at dx.hanselminutes.com. That's dx.hanselminutes.com.
[00:27] [background music]
**Scott**: [00:44] Hi. This is Scott Hanselman. This is another episode of "Hanselminutes," and today I'm talking with Eric Normand. He's the chief instigator at PurelyFunctional.tv. That's PurelyFunctional.tv. Very nice top-level domain there.
**Eric Normand**: [00:58] Hey. Thanks a lot.
**Scott**: [01:00] That's pretty cool. There is a ton of resources up there — video courses and courses with free content. There's a whole section on getting jobs around functional programming. You're building your own portal here to introduce people to functional programming, specifically as a career.
**Eric**: [01:17] That's right. Thanks for making me feel old, by the way, about portals.
**Scott**: [01:21] [laughs]
**Eric**: [01:21] Yeah, I'm trying to help people who want to get a job in Clojure, transition their career to functional programming and just provide a ramp for that.
**Scott**: [01:34] Why do I want to transition my career? Can I have my cake and eat it too?
**Eric**: [01:40] There's a lot of people out there who have been doing Java or some other language, and been unhappy with it. They're just like, "This is all there is." Now, with all this booming of new languages, they actually have a lot of choice. They are older, they have a family, so they can't really risk a pay cut.
[02:05] There's a lot of fear about changing careers at this point in their life. I'm trying to be there and hold their hand, and show them that it is possible.
**Scott**: [02:18] Why Clojure? Why did you focus on Clojure as your entry drug into the functional world?
**Eric**: [02:24] That's a good question. I've always been into Lisp. I started back in college, 15 or 16 years ago. I was a smug Lisp weenie, using Common Lisp. Then, when Clojure came out, I gave it a shot, and haven't looked back. I just really like Clojure.
**Scott**: [02:49] I'm going to parse something maybe more than I need to. You said smug. I think that it's OK to say this, that sometimes people who aren't functional programmers feel a little afraid to get into functional programming.
[03:05] Not just because it's scary and they have to figure out what a monad is, but that there's a certain amount of barrier to entry around the personalities, or what may be perceived as personalities, in the functional programming world. Do you think that that's true?
**Eric**: [03:21] I definitely see that the perception is there, and I can see why.
**Scott**: [03:29] How do we break that down and why is that perception there? Is it just because mathematicians go into functional programming? How did we get that stereotype and how do we break through that stereotype?
**Eric**: [03:40] I wish I could break through the stereotype. The reason I see it's there is that functional programming, because it's more mathematical, has been much more a target of academic research. A lot of the people who write stuff online, who write papers, they're actually researching new computer science techniques and concepts.
[04:13] They're just used to speaking in a more academic way and much less practical. They're not even worried about the practical at the moment. They're just trying to get a paper published. When you read a blog post it could be someone who's exploring this stuff. They could be a researcher. It might seem like it's totally inaccessible.
[04:37] Half the words that they use, you don't even understand. It would take you 10 years to read up on all the stuff that they know to get to the point where they're at. But the thing is, functional programming is very practical. It doesn't have to reach those heights of novelty that you get in the academic papers.
**Scott**: [05:00] I don't have to be an academic. I don't have to have a Master's degree if I want to get started and thinking about functional programming. I can really do practical work. People who are listening might be doing text boxes over data, or do a website, and they want to do it in a different way, and maybe a better way.
**Eric**: [05:18] For sure. One of the things that Clojure has, just as an example, is it's just got this pragmatic philosophy. It's built to be hosted on the JVM. One of the reasons behind that was to take advantage of all of the JVM libraries that are out there.
[05:38] It has really good interop. If you just need to send some Web requests or read some files off of your disk or whatever, you can just do it by calling into Java. You don't have to worry about whether you've got the right types and things like that, like you would in some functional languages.
**Scott**: [05:59] Do you think that the — and this is going to be a trick question — not a trick question. I'm setting you up here.
[06:06] Do you think that we made a mistake as a community or as a practice, that maybe imperative programming shouldn't have won and that functional programming should have been the thing that became mainstream, that things should be inverted?
**Eric**: [06:25] You're opening up a can of worms.
**Scott**: [06:27] Well, but an opinion is an opinion, and it's a valid one. Betamax was a superior videotape format, but VHS won. That doesn't change the fact that Betamax was, in fact, better.
**Eric**: [06:40] Yeah, but when you say that about Betamax, you don't have a flame-war like ready to start.
**Scott**: [06:46] [laughs] Well, I don't want to start a flame-war.
**Scott**: [06:48] I want to enable people who are listening to make a decision that maybe there's a better world out there. I haven't decided yet for myself. Maybe the answer is somewhere in between.
**Eric**: [06:57] Sure. Well, all joking aside, I think that procedural is actually a pretty good model for how things work. What I would say is it's the object-oriented that is not so great. That procedural is great for single-threaded. Just go through step-by-step. Get something done.
[07:23] A lot of my code is just like that, even in Clojure. What happens is once you have multiple threads, you actually want the language to help you a lot more. You want immutable values. You want pure functions. You want some primitive, some concurrency primitives, to help you put the...you have all these threads working.
[07:46] You need to put the answer back together once they're all done. You need something to help do that. I would say that it's the object-oriented where you put a little bit of state in every object and just make this big graph of objects. I would say that that was the mistake.
**Scott**: [08:07] Interesting. That doesn't seem nearly as controversial as I think people would think. It sounds to me like if you want to...Chuck, maybe we can back up a little bit, and juxtapose functional programming and non-functional programming. It sounds like it all starts at side effects. That's the big argument, state.
**Eric**: [08:26] Yeah. State, side-effects, and just to be clear, no one thinks that side effects are bad. They're actually necessary. If your program, all it did was use the CPU, and then you never knew what happened, it wouldn't be very useful.
[08:44] What's happened is that we've realized that having side effects all throughout your code that aren't necessary, are actually making it harder to reuse your code. They're making it harder to think about what your code is going to do. By separating out the side effect or the effect...they're not all side effects. Some of them are why you call the function.
[09:14] By separating it out and having a pure calculation over values, you can actually do a lot more reasoning. You can put the calculation on different threads. You can put it on different machines. That's really the umbrella under which the functional programming works.
**Scott**: [09:43] The state, when people say that the problems with non-functional languages are managing state, it's not the state or the side effects, it's the complexity that comes from those state machines that we ended up managing. That you add a Boolean and it's fine. OK, there's two states. Now, you add five or six Booleans and now it's this Cartesian product of complexity.
**Eric**: [10:05] Yeah, for sure. When you have something that is supposed to read a value, but it actually also counts the number of reads, or it is supposed to send a message, but it also writes something to a local variable or a global variable, those things you can't separate them out.
[10:28] You think, "I'm just sending an email," when you're also changing all these other things. When you are actually just doing a calculation like, let's say you're...I don't think anybody does this anymore — I hope people don't.
[10:44] When I was writing C back in the day, we would reuse global variables like, "Oh, this is an int. I need an int. Let me just use that one." Now it seems silly, but that's what we did back in the day.
[11:03] You would set it to zero and then start counting into it, and then you would be done with it. The other code was just supposed to know, "Don't touch that while I'm working." Those kinds of assumptions require discipline.
[11:23] If you could turn that discipline into just a constraint that the language imposes on you, you actually don't have to worry. You can relax a lot more. You can use that same construct in different ways.
[11:37] You don't have the mutability, so you're able to count lots of different stuff instead of just that one loop that you had that's supposed to be counting. You can count different things at the same time.
**Scott**: [11:52] There's inputs, and there's outputs, but I understand that one of the things that we forget about when we're writing declarative code or imperative code or programmatic step-by-step code is hidden inputs, or things that are not explicitly called out as an input.
[12:11] For example, if you new up a date in the middle of a function — even if that date is going to change — it's now become a hidden input, but it's buried 10 lines in somewhere, and because you're not explicit about it, it's a dependency in there that you're not realizing that is hard to test.
**Eric**: [12:27] Yeah, exactly. It's super hard to test, because every time you call it, it's a different time. In a functional world, what you would do is you would pass that date in, and so then, you could construct the date in your test however you want, and that way your test is always going to do the same thing.
**Scott**: [12:52] Can that be built into the language so you could be forced not to do that? Is that how we do end up doing things like inversion of control and dependency injection in non-functional languages?
**Eric**: [13:04] Yeah. The way that you would do that is using the type system. If you had a Haskell style type system that includes type inference and a concept of IO, that's part of the type system and also part of the language itself.
[13:26] IO is supposed to capture all of that, whether it's IO like I/O, Input/Output, so anything coming off the network, writing to disk, anything that the process in memory doesn't control. Also the time, the clock is another input.
[13:47] In Haskell, the way it works is if you want to do something like that, you have to be very explicit, and you actually have to parse that value in from the top. You can't just in the middle of a function that's supposed to be summing some numbers grab a time or a random number or something like that.
**Scott**: [14:11] Is that what's called a pure function?
**Eric**: [14:15] Good question. Yeah, a pure function is a function that is only a calculation from the arguments, and all it does is return the value.
**Scott**: [14:27] If all the inputs are declared or called out as inputs and nothing's hidden, then that is pure or "Superior" to one that might have hidden inputs or outputs.
**Eric**: [14:38] [laughs] Right. Superior and in the way that you get some guarantees. You know that every time I call this, I'm going to get the same answer back, and I can call it as many times as I want, and I'm not going to have effects on the world.
[14:55] The classic example is firing a missile. You can't call that function twice, because it'll shoot off two missiles. You want to have a function that, say, calculates the number of missiles that it needs to fire separate from actually firing them.
**Scott**: [15:18] If I know about side effects, if I know about what's bad and what's good, and what's pure and what's impure from a functional perspective, couldn't I go and write Java or C# as if I were writing a functional programming language?
[15:32] I could make sure all my inputs and outputs are called out, I could change my style. Would I be turning it into a functional programming language, or would I just be leaning in that direction?
**Eric**: [15:43] You can definitely do that. A lot of people have tried. There are success stories in that area. Especially now that Java has Lambdas, it's a little bit easier to write those kinds of things.
[16:02] What I've heard though from the non-success stories is that it's more code, because you're actually going to be writing around the idioms that have been optimized in Java, and then also, your teammates are going to be mad.
**Eric**: [16:24] Because they've learned all these other idioms and stylistic choices, and now you're breaking all of that, and they don't know what your code is supposed to be doing anymore. They don't want to take the time to learn all the stuff that you know.
**Scott**: [16:42] That implies that a functional programming language is one that is encouraging me or pointing me in that direction to avoid side effects and to avoid impure functions without it messing up everyone else's flow.
**Eric**: [16:55] That's right. It's more a matter of defaults and, like you said, pushing you in that direction. For instance, in Clojure writing a function is really easy. All the data structures are immutable by default, so you get that.
[17:16] There's no guarantee that your function is pure, but it's super easy to write one that's pure because the functions are very short, and it's easy to see that you're not calling something like a print line or something.
**Scott**: [17:35] There's a great blog by Kris Jenkins about functional programming, and in it they say that a functional programming language is actively hostile to side effects.
**Eric**: [17:48] [laughs] Yeah, that might be a good way to put it. I like to think of it like...If you want to go to the store and buy some stuff...You're going grocery shopping, and let's say you need two gallons of milk.
[18:07] You go to the store once, you buy a gallon of milk, you come home, and then you go back to the store, and you buy a second gallon of milk and come home. If all you had was a function like "Buy" or a method "Go buy a gallon of milk," that's how you'd have to buy two things.
[18:24] What you could do instead is sit at home and on a piece of paper you make a list, and you're doing all this calculation in your head like, "What do I need?" You're making a note, you're making a representation of all the stuff you want to buy, and then you have a function that takes you to the store and you give it a list, and it will run through the list and buy everything on your list.
[18:50] That separate calculation is something we do every day in our lives, but somehow in computer programming we mix it all up. We're, "Oh, I'm right in the middle of this loop. I might as well just like fire off this email," instead of calculating all the emails I'm going to need ahead of time.
**Scott**: [19:13] Let's talk about this in the context of getting a job. This isn't a matter of just go and read about Clojure and say, "I wanna learn all of the keywords."
[19:25] It's how do you express, as you're trying to go out there and transition your job, that you really understand this from an idiomatic perspective, that you understand what it means to be functional as opposed to just being able to hack something together.
**Eric**: [19:39] That's a really good question. Understanding this idea of pure functions is super important. If you were in an interview, you would probably be asked to explain what's a side effect, what is a pure function.
**Scott**: [19:55] Good. I'm glad you listen to this show.
**Eric**: [19:57] [laughs] Yeah, now you know. You got your job, right?
**Scott**: [20:01] Yeah, yeah.
**Eric**: [20:03] The other thing is that when you start to build more complex systems in something that just calculates one value, you start to build data transformation pipelines, and it's like when you're doing Bash scripting on the command line or you're piping commands together.
[20:26] You have one function that's calculating something, and then another thing is taking that value that it calculated and turning it into something else, and then another thing that's turning it into something else. At the end, you can take that and maybe just run a loop over it and send off your emails or whatever you're doing.
[20:49] That's another milestone on your journey when you're becoming a functional programmer, so I would talk about that at the interview. The next thing is getting to higher order programming, and that means functions that take functions as arguments.
[21:11] Because you're using pure functions, you actually feel very safe giving your function to another function to run for you. This is often called dependency injection in the object-oriented world, except you're not injecting an object, a piece of state with methods.
[21:32] You're injecting the function, so you might say, "Hey, I have a list. Give me a new list but with all the even numbers removed." You're passing in to the filter function, but if you want all the even numbers removed, you would pass in the odd predicate, so a function that returns true if the number is odd.
[21:57] Filter odd and then your list, and you get back a new list, because it's immutable, of only the odd numbers. Once you start thinking in terms of that, you realize that there aren't that many useful shapes of data.
[22:16] There's ordered lists, there's associative data structures like HashMaps, Dictionaries where you have a key value, and then basically there's sets which are like bags of stuff, non-ordered, and then there each item is unique.
**Scott**: [22:38] When I hear this word, "Predicate," and I hear all these different computer sciencey words, those are the ones I think that scare people off like we said. A predicate's like a filter criteria, right? It's the thing that returns true or false, and then you use it to filter lists of things.
**Eric**: [22:56] That's right. It's just a regular function, a pure function that will take an argument and return true or false, so it does some check on the argument.
[23:07] You could have a function called "Odd" that takes a number or an integer, and it returns true if it's an odd number. You could have one called "Even" that returns true if it's even. You can then pass this into a function that expects a predicate like "Filter."
[23:26] Filter will take a sequence or some collection and return a new collection with only keeping the stuff that passes the predicate. If you have a list of numbers and you pass it to filter with the odd predicate, you'll get a new list of numbers with only the odd numbers in it.
[23:48] Because there's only certain shapes of data, so filter works on a sequence, and then there's associative shapes. Sequences have an order, associative doesn't have an order, but it has this relationship between keys and values.
[24:05] Then there's sets which are just collections with no order, but you can check if something is in there, and you can also put the same thing in twice and it only gets put in once. Because there's only these certain shapes, you just learn a bunch of these higher order functions that operate on the shapes and put them in a pipeline, and you're just piping stuff through.
[24:37] If your audience is familiar with some of the Unix commands, like the early Unix commands, where ls just lists all the file in the directory, one on each line, and then you grep it and it shows you all the ones that match.
[24:53] Then you pipe it to something else, and maybe you sort them and you count them. You can do all sorts of stuff with them. You're chaining up these commands together.
**Scott**: [25:08] That's a really clean way to explain it to people. ls doesn't change anything. ls is guaranteed to never change stuff and grep doesn't change stuff either.
**Eric**: [25:17] Right. All it does is it gets in lines, and then only picks out a subset of those lines.
**Scott**: [25:22] This brings us to function composition, where I go and take five or six functions and awk and sed and grep and jsawk or whatever, and combine them, and I can make them their own function and then combine that.
[25:39] Now you're building stuff up except you're doing it with pieces of functions and functions of functions, as opposed to [laughs] as I like to say "Multiple nested for loops," which is what it ends up being in non-functional languages.
**Eric**: [25:52] Exactly. What happens in Unix is all you really have are lines of text. That's what everything understands, so it's very flat. If you need to do anything more sophisticated than just read it as text, maybe like it's a JSON per line, then you need another program that can go in and read the line of JSON.
[26:16] I don't even know how you do this in Unix, but you need to run that command for every line, and then inside that it's like now it's nested.
[26:28] You have to do all this stuff inside of that, and it gets really complicated, which is why in functional language you will get more nesting in your data structure, but because you're operating at a higher order, you're often able to keep the pipeline flat is how you think of it.
**Scott**: [26:48] Very cool. You can go to PurelyFunctional.tv, which is Eric's website, and he's got video courses, he's got a guide that you can get on his mailing list. There's a whole section on job resources for both Haskell and Clojure.
[27:07] You can basically get on to the newsletter to start and check out some of his great video courses. He's got a whole system and the whole focus of your website.
[27:17] I really like this because rather than just saying, "Here's a bunch of videos, here's a bunch of stuff," your focus is, "If you wanna move your career, you wanna change your job from doing imperative programming to functional programming," you're basically setting people up for success with that being the goal.
**Eric**: [27:34] Yeah, and now you should understand the purely and the functional. It's like a play on words, because all I talk about is "Functional," and it's "Pure," and there you go.
**Scott**: [27:45] That's great. I'm going to put links to all of this in the show notes as well as some of the great free courses. He's got 13 hours of great free courses about clojure.tests and data modeling and all kinds of stuff.
[27:58] You can see his style of teaching, and if you check it out, maybe purchase some things. Thanks so much for chatting with me today.
**Eric**: [28:07] Thanks so much, Scott.
[28:08] [background music]
**Scott**: [28:09] This has been another episode of Hanselminutes, and we'll see you again next week.