099: Funcadelic with Taras Mankovski
Taras Mankovski: tarasm
CHARLES: Hello everybody and welcome to The Frontside Podcast Episode 99. My name is Charles Lowell, developer here at The Frontside and your podcast host-in-training. And with me today is Mr. Taras Mankovski. Welcome.
TARAS: Thank you, Charles. It’s a pleasure to be here.
TARAS: Yeah, sounds good. It’s going to be fun.
TARAS: Oh, I think yeah, I think it’s probably more six months ago. I think it’s probably what, two months, I think probably December maybe?
CHARLES: Okay. But it’s hard now to imagine working without this tool on my workbench. It’s been probably the biggest game-changer for me in the last, I don’t know, definitely in the last several years.
TARAS: Yeah, it’s pretty impressive how little, how small of a library can have such a big impact in what we do day-to-day. Because it definitely makes me think differently about how I can solve problems that I solve on a daily basis when I work with React. So, it’s been pretty interesting. I think for me, having worked with this library, I think what I’m getting is an understanding of how things work in a way, and a perspective on how React works, in a way that I don’t think was available to [inaudible] Funcadelic. The funny thing is it’s not a React library, right? It’s not designed for React. It’s just that…
CHARLES: I don’t even think that – it helps you think about React, but I don’t even think it’s the way that the React developers think about React, right?
CHARLES: Right. And I think that’s actually kind of one of the reasons I think a library like Funcadelic is so important and fills kind of a missing piece in the ecosystem, is because it really is predicated on the idea that programmers use these concepts all the time. They really are, they’re foundational. But we only kind of see them out of the corner of our eye, in our peripheral vision, as being like a formal concept, like mapping. And giving a name to that. You know what I mean? Like you do the mapping, but you’re not thinking about: how do I generalize over it? And I think that that for me, certainly in my journey with functional programming, I thought that it was mostly about functions. Not to say that it isn’t, but that was kind of the end of the story. It’s like, keep your functions pure so that the outputs are only dependent on the inputs. And away you go. And understand closures and higher-order functions, functions that return functions or take functions, and that’s it. But I really feel that that’s only half the story.
TARAS: You know? And it wasn’t until I started reading the readme – I’m like, “There’s so many answered questions that I didn’t even know these questions were asked.” You know?
I think what it is, is what’s concrete to one person is abstract to another. And it’s like, if you’re working with those things, you’re working with those sets of analogies, and then you’re working with those abstractions every day, then they’re concrete to you. In a sense that once it clicks in your mind and your mind kind of accepts it and rationalizes over it, then it moves from being, “Yes, it’s an abstraction. But it’s a concrete abstraction,” in the sense that you can have a conversation with somebody and use that abstraction as an example, as a counterpoint, and a method triangulate and reveal other abstractions. But if you’re talking to somebody for whom those abstractions haven’t clicked yet, then it’s just, it’s opaque. And it’s not helpful. And so, I think that one of the things that I realized is like we are using these abstractions, we just don’t have names for them. And so, I wanted to give them names and put them in the hands. And the names are weird, but they are really useful. And so yeah, maybe we could talk about some of those right now. Because I think that maybe now’s a good time to actually introduce some of those abstractions.
So for example, if you don’t know what a functor is, it’s worthless to talk about a monad, in my opinion. So, that was critical piece of information for me. Because that is like missing in every monad tutorial you ever read. At least, I must have read a thousand monad tutorials. And they kind of glossed over functor or didn't mention it. Whatever. Maybe they did but I wasn’t looking. And that needs to be put front and center, that there is a natural sequence to these things. It’s like, some of these abstractions are built on other abstractions and you have to – you can’t skip to monad. You have to start with functor. Again, I realize that’s probably gibberish gobbledy-goop to a lot of people. So really, this is what Funcadelic is about, what this conversation is about, is just saying – talking through it in real-world examples to make those abstractions concrete, so it doesn’t feel strange anymore.
TARAS: Yeah. It’s definitely giving names to things. I think it’s really helpful. One thing I really like about Funcadelic is that you’re not giving names that you made up. These are names that existed for 50 years. They’re historic. And so, when you talk to somebody who is familiar with functional programming and if you say ‘functor’, all of a sudden, I feel much smarter. And we’re actually referencing the same thing, because we are – because alternatively, you can say something like, “Something that is mappable,” right? Like a functor is essentially describing something that you can map over.
CHARLES: Right. That’s all it is.
TARAS: Right. But you know, having a name for it, it allows you to just describe it exactly as it is.
CHARLES: So yeah, there are tons of things that you can map over, right? Most of the time, we think about arrays as something that we can map over.
TARAS: One thing I found really interesting in starting to use Funcadelic is that when you start thinking about things as they are like abilities – like you know with an array you can map over an array. It’s something we’ve all been doing for a while – but then something that you end up doing a lot of. When you get familiar with an array, being able to use object map, mapping an object, becomes something you want to do at some point. Most of the time, what happens is that you’re like, “Oh, I don’t actually – well, I have to write this thing. How do I write this thing?” and then by the time you do this, you’re like, “I’m just going to go to Lodash and I’ll just get the map thing that will map an object.” At the end of the day, it doesn’t quite necessarily feel right because a lot of these libraries – like, Lodash has map but it feels like there is always some kind of a compromise with how these things are implemented. It’s not consistent.
TARAS: And I don’t remember exactly what the problem with Lodash map was. I know for a fact there are, like there’s different ways that you can map things. There are different functions available for mapping different things in Lodash.
CHARLES: There’s ‘map to’ and ‘map in’ and blah, blah, blah, blah, blah.
TARAS: Yeah. All those different variations. But I think it’s been really interesting. We’ve been using Funcadelic on a project we’ve been working on, on microstates. And just being able to use one function map that is going to map an array or is going to map an object and it’s going to do it the same, use the same function, and it’s going to do the same thing.
So, I think that that’s one of the reasons that I prefer Rambda, for example, over Lodash, is because it has a form of polymorphism. Most of the things like maps and lenses and applicatives and stuff, almost everything works on both objects and arrays. That’s actually kind of nice. So, Rambda has a basic polymorphism. But I think one of the other things that is really empowering about Funcadelic is that it allows you to make the map function work on any data structure that you happen to come up with. Anything that you want. You can make it mappable and map will work on it.
TARAS: Yeah. I think for people, it’s probably quite abstract, what that actually looks like. I think one thing that’s interesting is that – so for listeners, the way that would look is you have a class, you have an ES6 class, which gives name to a certain piece of data that you have in your application. And then what you can do with Funcadelic, you can then say, “This is how you would implement – if you were to map this kind of an object, if you had an instance of this object, if you wanted to map that object, you can specify: what is the function you would use?” So, even though you would use the same – you would import map from Funcadelic and you would use that map to map whatever that object is and whatever type it is, but there’s a way for you to say, “For instances of this type, I would like to use this specific implementation of a map to map that object,” but use one function to do it. So, it’s going to use one function that you call. But you can specify, under the hood it can specify how the actual, what is actually used to map that instance.
CHARLES: Right. And then that’s nice, because then you can – anything that uses a mappable object, there’s a couple of reasons that that’s nice. Any time you can have some sort of higher-order mechanism that just requires that something be mappable, that it be a functor. And then it’s really nice because then you can have higher-order operations that they just need something that’s mappable. But you don’t have to use that one shot of actually having a map function as a property of the object. You can actually, you can kind of define wrapper classes or whatever, that then introduce a unique way that this object can be mapped. So, you can have the same structure and map it three different ways. Whereas you’re kind of constrained by that using normal OO inheritance because you have to have a map property on your object.
TARAS: Yeah. There’s something else actually, when you start thinking about this. For me personally, I think the first step when we start working with objects, having mappable objects, it was the first thing that was really helpful. But then I think really quickly, right after that, I think my second thing that I started using and I think is probably my favorite feature now, is append. I think it’s actually – yeah, so append is an implementation of a semigroup but I think it’s simpler. A simplest metaphor would be something like object assign. So, object assign is an implementation of a semigroup, except that object assign mutates the object that you pass into it. Right?
TARAS: It doesn’t create a new object for you.
CHARLES: About like, array smoosh? I think this is nice – smooshing, right? But you can smoosh things together. And with addition, I can smoosh two integers together or two numbers together and get a third number, right? Or with objects, I can take two objects and smoosh them together by merging their keys. Or I can take two strings and I can smoosh them together and I can end up with a string that’s concatenated. Or I can take two arrays and smoosh them together and I’ve got now an array that’s been concatenated. So, what’s interesting is these are all very, very different types that I’ve just described. And yet, there is some fundamental operation about them.
You might even, I don’t know a hardcore FP nerd would have to probably correct me because this is just totally conjecture, although I know it’s probably a solved problem – is maybe you could say we append two functions together. And that returns a function which is composed, right? That might be a way that you could say – what would it look like? Is function a semigroup? Can we append two functions together? And maybe you end up with like a pipe or a function composition or something like that. But I think that highlights, when you have these universal interfaces, because literally I feel like most of the stuff that we have been working with, there’s literally five, there’s like five interfaces. And everything is one of these five interfaces.
And it kind of flips you on your head, because the classic programming wisdom that I have certainly have espoused for at least the last 10 years is that you don’t want to race to find abstractions. That’s dangerous. Because you can get locked into the wrong abstraction, right? Wait. Let the abstraction emerge. And I’m a lot less bullish on that concept now that I’ve discovered these things, because that wisdom is cultivated in a world where there are [billions] of abstractions, if you’re giving unique names to everything and the combinations between them. But if you are coming up in a world where there’s five basic abstractions, then it actually pays off to ask the question, “Is this thing a functor? Is it a semigroup? Is it a monad? What would that look like?” It’s a nice thought experiment that doesn’t require that much investment.
You can think about it for a couple of minutes. And usually, you can come up and say, “Yes, yes. It totally is,” and I can start using it. And now I’ve introduced this really powerful abstraction. Or you say like, “No, no it’s not.” And then that information is just as valuable. And so, it’s very low-cost to experiment with abstractions. And so, I kind of think of it as – I know I’m on a little bit of a rant here but this has kind of been a major revelation for me – is that when you have very few abstractions which you compose in myriad ways versus having a whole bunch of abstractions that can’t be composed very much, the cost for experimenting with abstraction and making the wrong abstraction is several orders of magnitude lower. And so, you don’t have to be as cautious. And you can actually use trying on abstractions as a tool, rather than a very, very high risky undertaking.
And just to kind of close that thought out, I think that – I don’t know if anybody else but me remembers the world before we decided we were going to make all of our web services RESTful – like when people first started building all these web services, we were just going crazy with the endpoints. And there was no rhyme or reason. Kind of weird arbitrary levels of nesting. Sometimes, you’d throw in an ID as a query param, sometimes you’d throw it in as part of the path. And then I definitely remember, it was probably around, I don’t know, 2010 for me where I listened to a podcast where James Edward Gray was talking about S3 and ‘Was it a RESTful interface?’ and the O’Reilly book. And it really clicked for me. And realizing that if you constrain yourself to thinking about your API at least as these fundamental operation of manipulating resources, and you were constrained to four verbs and everything, you want to have ID-based URLs and resources and as flat as possible, those constraints actually are very enabling for consumers of your API and for actually authoring an API. And I think it’s the same principle at work here. Anyway, so I’ll end that rant. [Laughs]
TARAS: Yeah. I think it’s, I think people could probably, for those who haven’t been in programming as long as Charles has been, it’s probably easier – Charles I think people could probably relate to what’s happening with components now, I’d imagine. Because having components essentially look the same across every framework, they all have props and they all render, return some DOM, or some variation of that. But it’s kind of the same thing. You take some data and then you return something that is going to become DOM. And I think having that as a rule for what a component is, you can then make really complicated applications using these fundamental building blocks. And then you don’t have to – there’s not really much thinking on, “Well, how am I? What interface is this component going to have?” Okay, well you know it’s going to accept props. And you know it’s going to render some DOM when you actually render it, right?
TARAS: That simplicity I think is really helpful. And I think it’s one of the things that – I think one of the things about Funcadelic that I really like is there’s kind of a really small set of rules that are really helpful. And these rules are actually, they make it predictable. Because one thing that I find really challenging with using Lodash or using Rambda is that because there are so many functions, it’s difficult to know what is actually going to happen when I do something. So, a good example would be like if you use omit from Lodash, Lodash omit will then, it will actually – one thing you can do is you can materialize your getters. So, if you have getters in your objects, those getters can become values on the new object that’s created. So, that’s one thing that could happen. Or if you use omit, your symbol, if you have values…
CHARLES: What does omit do, by the way? I’m actually…
TARAS: Omit is a pretty popular way to exclude functions. Basically like a filter equivalent for an object. It’s usually used to remove some props that are coming into a component. But it can do some – it can actually change the type of the object. One thing you know for sure is if you use something like omit or if you use assign, if you have an instance of something, guaranteed, working with that object in a mutable way is going to cause some really strange things. I think with omit, if you were to have an instance, it would definitely not give you an instance of the same type, like an object of the same type. It will give you just a regular [inaudible]. And there is no real way – you could create an instance. I don’t know what assign would do. I’m guessing that it would just take an instance and would put things on it.
But it’s really not – I think this kind of ambiguity doesn’t work very well when you’re trying to build something, when you really just need to know exactly how your tool behaves. I think that’s one thing that with Funcadelic, because it’s such a small API and because you know for a fact that the library is designed to be immutable and it’s designed to preserve type information, then you know that if you use one of the operations, you will get most likely the same kind of object. And you’re not – well, you will the same kind of object and it’s not going to mutate that object. And so, there are some of these guarantees that are actually really helpful and [inaudible] is liberating, I think.
TARAS: Especially when you’re trying to do more challenging things, not trivial, just copy some properties from one object to another. But when you’re actually doing more sophisticated things, in those use cases, having these kind of rules is extremely powerful.
CHARLES: Yeah. And I’ve definitely resisted and I think will continue to resist expanding the API surface area that much. Because it is, I think there’s only five or six functions in there. But what you get for those functions is extremely powerful and extremely predictable. I think it might help to give a concrete example. Like when you were talking about object assignment.
Like if you have a car class in your application and you want to do an immutable object assign, well the kind of stereotypical way to do that or the typical way to do that is you assign. You create an empty object and then you assign one car to it and then you assign the next car to it. And now you’ve merged these two car things, right? But then the problem is, you’re going to get an object out of that, not a car. It’s just going to be a vanilla object. Now, it’s going to have all the properties of both, but it’s not going to be a car. And that could be a problem if you’ve got any custom methods on the prototype, any computed properties on the prototype. It’s going to be a problem. Whereas Funcadelic, you can append two cars together and you’re going to know that it’s going to have both of the properties. You’re going to know that it’s going to be of type car. And you don’t have to worry about running the constructor or anything like that. It’s just going to have – the properties are going to be carried over properly.
If you’re using a library like Lodash or Rambda or something that doesn't account for type, because in order to append two things or map something, the implementation actually lives with the type, not with the function. The function is polymorphic, but the implementation lives with the type. You can then actually, you can always return the proper type. Because it’s ultimately – like if your map operation or your filter operation or your what have you operation doesn’t take type into account, then there’s no way to actually preserve type. But because we delegate in Funcadelic, it’s core to the concept. And so, it’s actually a very trivial thing to do, which is why you get that repeatability.
TARAS: One of my favorite things about append and semigroup implementation for object is that you can overload getters on an existing instance. So, what you get is you get a new instance with the getter that you passed to append applied to that instance. And this is kind of trippy but it’s actually really powerful because – so, let’s say you have an instance of an object and that instance has some getters on it. And those getters use some properties of this object to compute their value. And so, when you want – if I need to create a copy of that object in such a way that the getters still continue to work properly but I need to override how one specific getter works for one specific instance, one specific scenario, one specific use case in my code, then I can just use append. So, the first argument is the original instance, second argument is an object that has the getters that I want that I want to overload the getters on the original object. And then append will squish those things together, smoosh those things together, and give me a new instance that has the getters that I passed in, in the second argument. And all the same things that the first object had. And that object will work. This new object will be a fully-functional object just like the original object that I still have a copy of, that I can use.
But this is really interesting because one thing that I’m finding with having these kind of tools in my toolset is that I’ve had features that I needed to implement on a project. And the people that I work with are really technical. So, they know where problems are going to be. And so, the would write requirements for how something – for a feature that needs to be implemented. And knowing the problems, they’re like, “Oh, by the way. You’re going to have a problem in this area when implementing this kind of specific functionality.” And for me, I’m like, having this toolset, I’m like, “I don’t see it as a problem at all.” It’s so easy for me. Because I know that if I need to implement – so if I have something that has expected behavior, but I need to create something that behaves very similarly but slightly different in one particular use case, I can always just copy that object and then overload its behavior. And it’s still going to be a fully-functional object. And I think that alone is just not something that you can do usually. It’s not something that’s available.
CHARLES: Yeah. It’s a technique that I think was discovered. Maybe it’s not original to us. But it’s just, the tools enabled it in the sense that it’s like having a flashlight in a dark room. It’s like, that technique was always there. It’s just when it becomes so concise that it’s so easy to just append one more computed property to a thing, then you just wouldn’t have thought to do it otherwise.
And by starting to bring these ideas, moving the wealth of knowledge from the source into the realm where we actually need to use it now, we can actually start to take advantage – leverage all these insights that actually enables all of this. It’s not like – so, the idea of how to write React and think in a reactive way or think in a functional way, those things are not just owned by the React core team or they’re not owned by an elite group of developers who really understand how functional programming works. It’s just available to everyone. And all you need to do is just learn some of these concepts that glue all these ideas together that are fundamental pieces of how functional programming works.
TARAS: Yeah. I think they’re really – for most people, if they were to read the readme and then – well, I think one of the problems, it kind of works with the language metaphor, is that you need an immersion, right? I think one of the reasons why Funcadelic really stuck and functors and semigroups and [filterable] and all of these things really stuck for me, and I’m thinking about how using monads and monadic operations or applicatives and all that stuff – the reason why it all stuck for me is because I’ve been able to talk to you about it. And I think for people, finding a network that will allow them to practice immersively, to think about functional programming, not just occasionally – I mean, you could do it occasionally as well, it just takes much longer. But once you really, once you have a few conversations where you try to dissect a problem in a functional way and you think about what these pieces are made of, it becomes very natural, very quickly.
CHARLES: Yeah. I think that’s actually a really great point. And the thing is, you can immerse yourself incrementally. So you can just say, “You know what? I’m just going to start using append.” Anywhere that I would concat two strings or I concat two arrays or I would do object assign – screw that. You can even just say, “Instead of using object.assign, I’m going to use append,” and start there. Or to say, “I’m just going to start with mapping.” I think also the thing that’s nice about it too, is the buy-in can be incremental. But you’re right. You do need immersion. You do need practice. You need to actually use, you need to use the functions and you need to be able to use them one at a time, so that your mind can close over them. So then, you can kind of move onto the next one.
CHARLES: So, that might be one way, is to say – because you know, I don’t think I really understood. I was already using append and map ubiquitously before I really understand applicative/apply. So, you don’t have to grok it all at once. You can definitely bite it in chunks. And the best way to do that is to start with one concept and really just attack it mercilessly. And then also understand that there’s a natural sequence there.
TARAS: I would add a little bit of a caveat to that. I think there’s a thing about using – doing something for learning purposes and there’s another thing about shipping things. What’s interesting with Funcadelic and what’s interesting about a lot of these ideas from functional programming is that I think they give you benefits that you might have not previously thought. Like for example, if you’re going to concat two strings together, doing it with append is probably the most robust way of doing it, relative to just being able to use [inaudible].
CHARLES: Yeah, that’s true. You wouldn’t want to use a string [inaudible], wouldn't you?
TARAS: Yeah. But there are areas when using – there are times when things are just not possible otherwise. Like for example, if you wanted to treat an instance of a class in an immutable way, this is simply not possible in any way. So, if you’re going to say, “I’m going to work with a bunch of these instances of ES6. And I want to keep them as instances, because they have certain behaviors that I want to have. I want to have a getter, or I want to have a method on it. And I want to keep these things fully full instances, not broken, not be turned into objects. I want them to be normal instances but I want to work with them immutably. When I need to make a change, I want to get a new object and not modify that object.” So, if you set yourself that goal and you say, “This is what I’m going to do,” then you really are not left with very many options. You only really have, you have to use append from Funcadelic. And because alternatively, you’re going to implement something yourself. You might as well just use append. And [I think] that’s a good place.
I think if you’re starting to, if you need to make something lazy, if you need to delay an execution of something – so, instead of pushing that execution, instead of using object assign and then computing everything ahead of time, you can use append. And you can create a getter and you can delay the computation of that value until the point when the user actually reaches for that value. If you want to start doing that kind of stuff, you really are not left with very many options. And append is the way to do it. But that’s the thing, is when you start to set these kinds of standards for yourself, you level up in a way that is very significant. I think it’s like a natural progression of learning. You start off learning and anything goes, as long as you can make this website work, it’s like, “I’m happy.” And then over time you get better and better at it. And then when you get good at building applications, your next step might be like, “What if I was stricter about it? What if I could actually – what would that open up for me? What would that allow me to do?” I personally think about it that way.
CHARLES: Yeah. I think that’s a good way to think about it. You mentioned having a network for discussing these concepts and trying to internalize them. Let me first and foremost offer myself as somebody. If this is a hill that you are interested in climbing, and I think it is a very worthwhile hill to climb because of the perspective that you will gain from its summit, please reach out to me. You can contact me at cowboyd on Twitter or firstname.lastname@example.org. I’d be happy to discuss these kinds of things, because I think that these tools are just incredibly powerful and will improve you. So, if folks want to get in touch with you, Taras, where would that be?
TARAS: I’m email@example.com and tarasm on Twitter.
CHARLES: Alright. Well, thank you everybody for listening. And as always, if you want to get in contact with us at Frontside, you can just email us at firstname.lastname@example.org or give us a shout on Twitter at @TheFrontside. Thanks everybody. We’ll see you all next time for episode 100.