Capture Side Effects in a Task

    Brian LonsdorfBrian Lonsdorf

    We examine the data structure Task, see some constructors, familiar methods, and finally how it captures side effects through laziness.



    Become a Member to view code

    You must be a Member to view code

    Access all courses and lessons, track your progress, gain confidence and expertise.

    Become a Member
    and unlock code for this lesson


    00:00 Let's have a look at task. Now, we're going to use this data.task on NPM here from Folktale, but you can use whatever implementation you like. It might slightly vary. Here we are. We have a task, and this has an of method.

    00:10 We can throw anything in there. I could say, "Hello," or the number two, or true. Let's stick with just the number one to be simple here. It acts just like a box. We have a task with the number one in it there. Now, to actually witness this number one, we have to call a function called fork.

    00:24 This is takes an error case -- I'll console.log(error), the error, e -- and the actual success case, too -- console.log(success) with the x here. Now let's run this.

    00:40 I have success of one. If I wanted to hit the error case, I can make a rejected task with the rejected method here. We run that, and we have an error of one.

    00:50 Now, something to interest to note is if we map over this, just like the other containery types here, we could add one to x. On the rejected case, it ignores it altogether. It just returns error one, just like I left on the either type. Now, if we have of, this is a successful task, and it will actually add one here in the map.

    01:06 We could also chain over it. If we were to return another task, we call chain if we're returning a task within the task here. This will give us the success of three. Again, if we return the rejected version, it will just ignore both the map and the chain, and short circuit, and go right down to the error.

    01:22 This, at this point, is not that interesting, but there's a different constructor. This will allow us to capture side effects.

    01:28 Now, let's go ahead and make ourselves a function, called launchMissiles. That will take nothing, and simply it's going to just console.log(launchMissiles()). Now, the interesting this is, this will just run immediately.

    01:42 Now imagine, we're just faking this with the console.log, but if we're actually launching the missiles, we want to be able to capture this in a task so that it will be lazy, and we can compose with it. Let's use the task constructor here that takes a rejection function and a result function.

    01:55 These will correspond to the two functions that I've given to fork. What we can do here is go ahead and launch the missiles, and then call a result with, let's just say, missile. That will be our return value for this function.

    02:09 Here we have launchMissiles. That will be passed into this map. This is the result here. We don't want to add one to it anymore. How about an exclamation point here? Then we can run this, and it will, indeed, launch the missiles. It gives us a success here with our result.

    02:26 Now, if we don't fork it all here, it will just not run. That is very good, because if our application was just this, then the caller of our application has to fork it. They're in charge of all of the side effects, and the problems that come with that.

    02:40 Also, they can perhaps extend the computation before it runs. Let's add a second exclamation point here. Now, when they fork it here, it will have two exclamation points. That's very good, because it means that, if this was some library code, or somewhere else, that they can keep extending things, and composing along, and our whole application remains pure.