Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

It's not easy but I'll try:

    counter = new Counter

    currentValue = counter.value
    newValue = currentValue + 5
    counter.set(newValue)
In most programming languages, each of those lines is executed sequentially. Therefore we are used to it.

If this is just a script then it's simple and pure functional programming (PFP) as no benefits here. The reason is that the order of calls is always the same (it's the same as the order of lines)

Things change when the order of calls is not static anymore but becomes dynamic. Think about a webserver. Or a any system that receives calls from the outside - or has something "running" like a cron job.

In that case, you can't just look at the lines of code to understand how the program operates. You know have to simulate not only the state, but also the access/change to the state (including external state).

Here PFP comes in, making those things explicit and therefore decoupling it from the order of lines of code.

In the example of a simple script, this is just annoying because we now have to be explicit even though we now everything should be ordered as the lines of code:

    counter = new Counter

    currentValue = counter.value
    newValue = currentValue + 5 // does not compile, because currentValue now is an "effect"
    counter.set(newValue)
currentValue is now an action/effect that might be run at some point or maybe not. Therefore we have to rewrite it:

    counter = new Counter

    currentValue = counter.value
    newValue = currentValue.onceItHappenedModify(value -> value + 5)
    // newValue is now also an effect
    updateCounter = newValue.onceItHappenedExecuteOneMore(value -> counter.set(value))
    updateCounter.execute()
   
In the end we have to execute the "updateCounter" effect because until this point it is just a datastructure. A blueprint for an execution if you want so. However, in PFP we don't actually execute it - that's the whole clue! We just pass the blueprint around and it gets bigger and bigger. Until the point where we return it as datastructure to the main method. And then, the programming languages runtime executes it!

If you find that complicated, you are right. That's why PFP only works in language that support this concept and make it ergonomic. I often use languages that don't (e.g. typescript) and in there, I don't use this technique because it has more drawbacks than benefits.

Anyways, it becomes more interesting once things happen in parallel/concurrently and from different points in the application. The reason is that when you work with those blueprints, you are forced to explicitly combine/merge effects.

You can, for instance, do this:

    fireRockets = fireRocketsEffect()
    activateLasers = activateLasersEffect()
Nothing has happened so far. We only created two blueprints. In other languages, things would be running already, but not here. We now explicitly have to decide how to run those:

    fireRockets.onceItHappenedExecuteOneMore(activateLasers)
or

    activateLasers.onceItHappenedExecuteOneMore(fireRockets)
or

    activateLasers.executeAtTheSameTimeAs(fireRockets)

And so on. As you can imagine, you quickly end up with combinators for e.g. running a list of effects either in parallel or sequential or in parallel but max X at the same time and so on.

I hope that explanation makes sense. I found it hard to grasp without actually building something myself.



Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: