Transcript from the "Function Modeling Practices" Lesson
>> Let's go through these examples. We'll circle back, it turns out, it's right here on the screen. This is the proper endo. I got a little confused before. The reason was, if, we'll circle back afterwards, but I'll show you why I got confused and why this works. So let's go down to our first exercise, we're gonna turn normal HTML into the JSX style HTML.
[00:00:25] We're gonna cheat on this one. It's not actually gonna work right but it's just for example purposes here. So, we have endo, we have a bunch of functions from string to string, terrific. We can rewrite this using endo. Did anybody have any questions or snags on this one before I dive in, I could address while we do it.
[00:00:47] All good? All right, so, well the first thing we do is just put these in endos, right? I can say endo of HTML for, we'll concat that with the endo of this one, and we'll concat that at the endo of this one, and so on. And what's happening here?
[00:01:12] This is one way, we'll redo it another way, but we haven't run it yet. So we have to run it on the HTML and see if that passes. It does, terrific. So it's just capturing function composition via monoids. Since the functions always return the same inputs type and same output type, we can effortlessly compose the types, never had a type error.
[00:01:35] So we also alternatively say, if we're gonna throw this on a list, right? List of, bam. And also it kind of doesn't really matter on the full map is. Kinda ahead of myself sort of, for map endo, and we'll give it the endo.empty and finally run it with the HTML.
[00:02:04] How's that? Didn't work, list is not defined. Did I prompt, didn't bring in list. Okay, that's fine. [LAUGH] We could do it a different way, we can just throw it in an array and reduce it. If we don't have full map at our disposal, we could just do a reduce, all right?
[00:02:21] An acc and an x. We'll put the x in the endo and there's a something reminds us that like a bloodhound gang line, put the x in the endo. [LAUGH] l concat that. So what's happening here?
>> You've given extra parentheses.
>> Opening one.
>> There it is.
[00:02:45] Bam, all right. And there we go, passes. So, again, this is all fold map took away, right? It's like a one line. I didn't have to import a library for that. I just do because really need to promote my libraries. Yeah, I'm just getting it. It's just what you're gonna see in other languages all the time.
[00:03:02] So I wanted to mimic that. That's it's really not about the library. It's about common Interfaces, so cool, done. And it turned this thing into this thing. [LAUGH] And if you notice the order doesn't really matter, does it? And that's kind of part of the contract you're kinda signing up for a little bit.
[00:03:23] It is composing them, but it's, since it's always the same type, I mean the order tends to not matter, okay. Let's go into predicates. Now, so this, we're gonna get into some cool stuff here. We're gonna contra map as we left off in the last example of reducers.
[00:03:45] So let's go ahead and write predicate, we'll take in a function, predicate function, tend to call these run. And follow the same kind of pattern we've been doing for expose run. And what we'll do here is just say, contra map. Since predicate is a, it takes a function that returns a Boolean, right?
[00:04:10] So I cannot map it. I can't turn the Boolean into another thing. It wouldn't be a predicate function anymore. That would just ruin the whole idea of modeling predicates. So what I'm gonna do is I'm gonna contra map, and give myself an opportunity, a little hook beforehand to hit our value coming in, x with the f, before it actually runs.
[00:04:38] So let's see what's happening here. So we have a contra map, and we need to concat too. That's fine. How do we concat two predicates? Concat, we take another one. And, so we have a predicate here. And we could, sorry, this is contra map, forgot to run. So something's wrong here.
[00:05:08] I will run the value of that. Okay, cool. Do you see how I missed that. We're gonna hit the f on the x and transform the value before we actually run it. All right, in concat here, what we're gonna do here is say, well, if I run this, so it'll give me a Boolean and we have another one, right?
[00:05:26] I don't know why that's passing [LAUGH] that's probably not good, but let me do this anyway. We're gonna say we have some other one with a run function and we can just concat that other run. And I think we have to decide right now whether or not we wanna return any or all within our predicate functions are free.
[00:05:49] We just wanna fix one. Why don't we just say and. This, did people see how that works? Basically, if I run my predicate function is gonna return a true or false. If I run the other one, it's gonna return a true or false. And now I have a predicate function that tells me they're both true or both false.
[00:06:08] I could decide to make it an or. Or I can actually say no, it returns me another monoid inside. And now I concat those two but we didn't really set this up to do that. So let's just fix it to and it passes. It was a little worrying that it passed this way.
[00:06:29] So I probably just wrote a bad example, but this is what it should look like. [LAUGH] But yeah, if you look at the example here, we're grabbing the length of x before it gets here. And then we're asking, if it starts with s. So we're transforming each of these into, all right, looks like, is that we're asking about the entire arrays length not each one's length.
[00:06:55] Is that why it was wrong? Well, in any case, that's predicate looks great. We can dig in further into why that passed. Yeah.
>> Could you just give like the high level definition of contra map again with the-
>> Yeah, so here's the idea, is that like, I have this predicate right?
[00:07:14] Here, look at this one, right? I've got this predicate that says, let's call it greater than four. And this predicate function takes a number, right. And I have this other predicate function that takes a string, const sStart. [LAUGH] So it starts with s, okay cool. So I have two predicate functions.
[00:07:45] This one takes a string, this one takes a number. And what I wanted to do is run through my results and filter all these strings that satisfy both predicates. But I don't want to pass a string to the thing that takes a number, right? So what I can do is contra map that it, we transform it into its length.
[00:08:09] But I don't want to do that in the definition. I can do that at the calling function when I'm combining the two to say like, you know what? I have a hook to be able to actually change this function and still pass it into the same filter. And then that gives me the ability to write one filter that just kind of combines all these predicates and transforms them before they get there.
[00:08:36] So that's the specific case, but the general case is that typically when you're combining these functions into one, they might take different arguments and it gives you a hook to kind of change it. And this is the use case, right? Like this is predefined in a library and I want to use that there.
[00:08:50] Did that answer the question or are you still have some-
>> No no, that's that's pretty clear.
>> Cool. Just if, we just so we're aware it's called a contravariant functor if you have contra map, contravariant, contra map. If we have contra map and map, it's called a profunctor.
[00:09:06] You can change the input and the output. It's kind of like a little connector thing. And so profunctors define both, contravariant functors does one and just functors or the other just map We have this little fun graphic. [LAUGH] If anybody wants to spend a night, pour yourself stiff drink, read through that.
[00:09:30] But that's there. It's in the fantasy land figures if you want to take a look there. Okay, back to our our example. So on exercise three, which is the last exercise. What we're gonna do here is say, rewrite using the matchesAny predicate, so it builds on the previous one.
[00:09:52] So if you didn't get the previous one, this one will be harder. The idea is that we have matchesAny, which is just a normal string match and then we have matching IP, the predicate version of that. We just wrapped matchesAny and put it in a predicate. So now predicate is a function which takes, when you call run, it takes that string.
[00:10:13] So you wanna run these with matchesAny IP. And since we wanna take advantage of concat and contra map, we can, kind of give us a hint as to, let's just give a quick idea of what's happening is. I'm calling the extension on the file here, and I'm calling contents on the file there.
[00:10:34] And combine them with and so let's leave that alone for a second and just write the same thing under it with matchesAny IP. So now that I have that, I can actually contra map extension, right? And then I can concat instead of calling and, with another predicate and this one takes contents so I can contra map that one x.contents.
[00:11:08] So I guess the file, file the contents and then we have to run it. Run it with the file. See if that passes. It does not, probably have a argument syntax error. There we go. And almost there, split of undefined on extension. So let's see what happened there.
[00:11:33] Extension takes the file, what does extension look like?. File.name.split, all right. Each file has a name. Anybody see any issues? We're contra mapping the extension concacting it with this one. So here I closed the concat and I'm conra mapping the contents. If I move this on the outside, now it should work.
[00:11:57] So let's take a look, allow me, just kind of break this down a little bit to that. So, are we seeing we have this function and then concatenate it with that function, and then we're running it. I made a mistake where I can concated it with that and then contra map the whole thing.
[00:12:39] And you end up with adapters and type transformations and a lot of stuff that you have to do to kind of plug things together. And yet a lot of pieces they just kind of, it's this additive flow where you keep adding new pieces to the system and then you kind of have to deal with them.
[00:12:55] With monoid, you have one piece and you just combine them into the same piece. And so you don't need adapters, you just keep combining and you get one thing back instead of this composite thing or new types that kind of latch things together. So, by normalizing our types into predicates and saying we're gonna get, provide a bunch of compasable predicates and then combine them.
[00:13:19] You're not introducing any new concepts in your system, you're just continually smashing it back into one thing. Okay, yeah, let's take a look back at my little issue here. We're just trying to do, let's say, I wanna provide endo with an empty string. I was very like, stuck on that, right?
[00:13:38] I was like, I, these are string functions and I need to do empty endo and I didn't have empty. So why wouldn't it work with an empty string? Well, it turns out that endo gets its argument later. Endo was a function that when you call run, that's when it gets its argument, right?
[00:13:56] And the identity function, since it's a function from the same type to the same type, here it is, again, identity function. It's coming in handy, right? And it says, okay, well, we'll just take what you started with and make, effectively make that the empty value. So it comes later.
[00:14:14] That's where I got little tripped up. So there, so endo empty is totally fine. It's holding a function not an empty string. So, and then if we look at all this, it works out just fine if I run node function modeling zero, we get this our Hello thing.
[00:14:32] I'm gonna talk about this in a second but yeah, totally works.