Debug Functional Compositions with a Logging Side Effect in JavaScript

Kyle Shevlin
InstructorKyle Shevlin

Share this video with your friends

Send Tweet
Published 3 years ago
Updated 3 years ago

Functional compositions are purposely opaque, with no obvious way to "visualize" the data as it transforms. This is great when our function works, as it's hard to add bugs, but challenging when our compositions aren't correct. This lesson teaches you how to debug functional compositions by writing and using a trace() function to log out our values as they transform. This allows us to continue using pointfree programming while also providing a view into our compositions.

Instructor: [00:00] Compositions of pure point free functions can often be opaque and difficult to debug. Let me illustrate that with an example. I'm going to create a function slugify that will take these book titles and turn them into URL slugs. I'm going to purposefully put a bug in the code. Now let's try logging out the result. We get a huge error.

[00:31] What we see is that string.toLowercase is not a function, which probably means that when we get to lower case, the value getting passed in isn't a string. What we need is a function that gives us a side-effect of being able to log out the current value.

[00:47] We can do this by creating a trace function. Trace receives a message as its first augment, and then the value getting passed to it, and now we're going to use the comma operator to log out our message and value, and return the value.

[01:02] Now we can place traces before and after functions in our composition in order to see the value transform step by step. We'll place a trace before we split. Remember with compositions we work from right to left, or bottom to up, hence why that one's before.

[01:21] We'll call this one afterSplit, and finally we'll put one after lower case. We'll save this and run it in the terminal again. We still got our error, but now we can see a little more information about what's taken place.

[01:36] Before split we have our array of book titles, and after split we actually have a two-dimensional array. What happened was that split took our strings. Split them at spaces, and made them arrays themselves, and our map.lowercase isn't working, because lowercase expects a string, and what it's getting is an array.

[01:56] What we can do is reverse the arguments that we have of map.split and map.lowercase. Now I'll rename my trace functions, I'll save it, and I'll run it again. All right, we didn't get an error, but I didn't get slugs at the bottom either. Let's look at our trace one more time.

[02:15] Before lowercase we have the array of titles like we would expect. After lowercase, we still have a one-dimensional array of lowercase strings. After the split, it looks like we now have a two-dimensional array each one was split at their space, and now we have each string individually.

[02:34] What happened when we called join on this array, is we actually put the hyphen between the last and the first values of each array. What we need to do, is we need to call a map.onJoin as well. We'll come back to our code, we'll add map here, we'll save this, and we'll run our code again.

[02:52] You can see we now have an array of slugs exactly like we expected. We can now clean up our traces knowing that our function works the way we expected. Save it, run it, nothing should have changed, we still have our array. Now you might notice we're calling map on all of these, so how could we make this better?

[03:10] We can make a composition of the functions and pass it once into map. We'll save that, and we'll run it in our terminal, and we get the same answer.

Haroen Viaene
Haroen Viaene
~ 3 years ago

In the last example, why is the root map wrapped in a compose?

Kyle Shevlin
Kyle Shevlininstructor
~ 3 years ago

Hey Haroen, you are correct that the final output could be refactored one step further by removing the outer compose since we're only passing one argument to compose. In truth, it just didn't occur to me at the time to take it that one step further. Fortunately, there's nothing mathematically invalid about having a redundant compose this way. Good catch.

Now, just to be clear to others, if they didn't understand what took place in that final step, because our composition was three maps over individual functions, we could compose those individual functions into a new function and pass that into a single map.

So what starts as:

const slugify = compose(
  map(join('-')),
  map(split(' ')),
  map(lowerCase)
)

Can be:

const slugify = map(compose(join('-'), split(' '), lowerCase))
Haroen Viaene
Haroen Viaene
~ 3 years ago

Thanks, that makes sense!

Agustin Quintanilla
Agustin Quintanilla
~ 3 years ago

Thanks so much Kyle, this was a nice and comprehensive course At this point, if you guys want to know a little more about functional programming I recomend you the free book 'Mostly adequate guide to FP (in javascript)' https://github.com/MostlyAdequate/mostly-adequate-guide

Hakan Karaduman
Hakan Karaduman
~ 3 years ago

Hi Kyle, very helpful course, can you explain what is the feature you used as (console.log(msg, x), x) . Why does this returns the x after comma?

Florian
Florian
~ 3 years ago

Hey Hakan, good question I was also wondering, check out this: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator

Kostiantyn Hryshyn
Kostiantyn Hryshyn
~ 3 years ago

Hi Kyle, very helpful course, can you explain what is the feature you used as (console.log(msg, x), x) . Why does this returns the x after comma?

Hi Hakan, we need "x" param after console.log to pass forward "x" value through the composition since the console.log just log the value and nothing to return

Hakan Karaduman
Hakan Karaduman
~ 3 years ago

Hey Hakan, good question I was also wondering, check out this: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator

Hi Hakan, we need "x" param after console.log to pass forward "x" value through the composition since the console.log just log the value and nothing to return

Thanks Kostiantyn and Florian, didn't know about this comma operator.

martin sundvall
martin sundvall
~ 3 years ago

Thanks Kyle! Really a good explanation of how to use functional programming. I have read a lot about it, and this goes from theory into practice. It would be great to see more, like examples of implementation in a web app.

Simran
Simran
~ 2 years ago

Thoroughly enjoyed the course. Every example was simple, very clean and easy to understand. Comma operator and point-free programming were two interesting things that I learned apart from the core JS functional concepts! Loved it.