mostly-adequate-guide icon indicating copy to clipboard operation
mostly-adequate-guide copied to clipboard

Chapter 3 The Case for Purity

Open davidsells opened this issue 6 years ago • 5 comments

I don't believe that the assertion below is correct

_Since our data is immutable, we can simply replace the teams with their actual value

const punch = (a, t) => ('red' === 'green' ? t : decrementHP(t)); We see that it is false in this case so we can remove the entire if branch

const punch = (a, t) => decrementHP(t);_

davidsells avatar Jul 12 '18 22:07 davidsells

Could you elaborate a bit :) ?

What feels wrong to you? The substitution? The code simplification? The condition evaluation?

KtorZ avatar Jul 16 '18 06:07 KtorZ

I also don't understand the given in C3.

const { Map } = require('immutable');

// Aliases: p = player, a = attacker, t = target const jobe = Map({ name: 'Jobe', hp: 20, team: 'red' }); const michael = Map({ name: 'Michael', hp: 20, team: 'green' }); const punch = (a, t) => (isSameTeam(a, t) ? t : decrementHP(t)); ... First we'll inline the function isSameTeam.

const punch = (a, t) => (a.get('team') === t.get('team') ? t : decrementHP(t)); Since our data is immutable, we can simply replace the teams with their actual value

const punch = (a, t) => ('red' === 'green' ? t : decrementHP(t));

Why is it that the 'team' can be hardcoded as 'red' and 'green'? I am not sure on the relevance of the immutability of the data. What if, for example, we were to call punch(michael, michael) with the former function definition vs. the refactored function definition? Or if a new member alex was introduced on the same team as michael and punch(michael, alex) was called?

These cases should not cause HP to decrement but would when refactored. The original definition says that if the two members are on the same team, no damage is incurred. The latter does not so the substitution seems incorrect.

emily-li avatar Dec 23 '18 23:12 emily-li

Okay. I think with @emily-li 's remark, I now understand @davidsells' one.

To be clear here, this isn't about refactoring equivalent part of the code with another. Seemingly, team isn't hardcoded and a.get('team') === t.get('team') isn't equivalent to 'red' === 'green' for all possible executions of our program. But it is for one (at least), and that's the one we're looking at here.

Equational reasonning is a way to debug and understand the execution of a program, given some inputs. It tells you that a function (or more generally speaking, any binding between an identifier and its corresponding value) can be replaced by its definition. This only holds in purely functional paradigms with immutable data. The immutability aspect is of the utmost importance here because, if anyone is free to change the definition of a value after it has been defined, it means that, looking at the code doesn't tell you much about the value of what you're looking at. To know such value, one will have to potentially replay a few execution scenarios and maintain some states in one's head to keep track of the evolution of such values.

So here, given the inputs job and michael as they are being defined, we can proceed by inlining function definitions and resolving a few equations to understand what would happen in such execution.

This example is a dummy one, and perhaps, the value of FP vs OOP isn't much emphasized. But in more complex scenarios, this becomes a very powerful tool where it is possible to analyze a whole subset of a system by just looking at how the data flow through the system.

KtorZ avatar Dec 24 '18 09:12 KtorZ

Thanks, @KtorZ . I think I understand what you are saying as that due to the fact we are using immutable data, we can use referential transparency. Within the punching code block, referential transparency gives us the capacity to logically reason about the code, and equational reasoning gives us the final

const punch = (a, t) => t.set('hp', t.get('hp') - 1);

For me, when reading this section, I think I confused the concept of equational reasoning for understanding and equational reasoning for refactoring, and assumed that the example was for the latter.

emily-li avatar Dec 24 '18 12:12 emily-li

To be clear here, this isn't about refactoring equivalent part of the code with another. Seemingly, team isn't hardcoded and a.get('team') === t.get('team') isn't equivalent to 'red' === 'green' for all possible executions of our program. But it is for one (at least), and that's the one we're looking at here.

Although those are equivalent for some situation, but they are NOT equivalent. And we should avoid such confusing code. I suggest something like this: punch(jobe, michael) // ('red' === 'green' ? t : decrementHP(t))

hugh-lx avatar Aug 29 '21 09:08 hugh-lx