hellerim (22) [Avatar] Offline
With OOP, I would not object against a dependency between classes Invoice and Customer. In particular, adjusting a customer's balance on assigning an invoice to him is absolutely natural. Setting the invoice's state to "issued" in the same action flags it so assignment cannot be done twice - this action should be considered atomic/transactional. Also, I would not be surprised about an altered balance. This is simply OO design, so if I where clueless this means that I did not fully understand the design. Usually, a class is designed to support not only one use case but several - in contrast to methods (and functions).

At this point, your FP-inexperienced reader has not adapted her mindset to FP, so you still need to convince her. In the sequel, she will feel pain in refactoring the example. The number of classes increases. As a programmer, she needs to care of many more things because the OO principle of encapsulation or information hiding is broken. And all this only to adhere to some obscure FP principles! I think you should do much more to convince her of the usefulness of these principles.

Why make copies of a customer? Just to have them immutable? Of course, yes, but the OO mindset would be reluctant to follow you. Feeling pain to leave the way you're accustomed to is not a good condition to develop openness to different ways of thinking.

I'm not sure if your refactoring of objects towards FP structures is really that easy to follow. At least I think the example is inappropriate because the classes you chose are in practice subject to persistence. In contrast, in FP the data is transformed from one generation into the next. So what's the point in having several generations of one customer?

I don't want to say that FP cannot deal with persistence. However, in my mind a customer remains the same independently of his actual balance. You'd better have descriptions of real entities. Then you may argue that if a description changes then actually you've got a new version of the description.

To make the point: In OOP, you use data to represent entities, in FP you're dealing with data which describes them.

Probably using something people don't associate persistence with would do the job much better. What about processing messages?

Message was edited by:

Message was edited by:
modeler4 (1) [Avatar] Offline
Re: Chapter 2, p.26ff: Invoice example more than confusing
I'm a newbie to FP, the line that caught my eye was,
"Feeling pain to leave the way you're accustomed to is not a good condition to develop openness to different ways of thinking."
To me, I would what to know, What are the practical reasons for me to spend time learning FP,
i.e., what can FP do that OO can't??
I need to decide, is this FP stuff worth my time, are there programs that would fall on their face if written in OO, versus FP? What kind of programs are they?
If you don't answer this as soon as possible, the book will get put aside for a long time.
But I like the style of writing.
hellerim (22) [Avatar] Offline
Re: Chapter 2, p.26ff: Invoice example more than confusing
OK, let's go through the example, starting from an OO perspective. We have a customer with a balance, and an invoice with an amount of money to be issued. An invoice does not come into life by itself but expresses that the customer owes you the money for some reason at some point in time. The balance of the customer expresses the total of the money he owes you minus the money you owe him, again at that point in time. The invoice contributes to the balance as long as it has the state "issued" and not, e.g. "paid" or "revoked". Anyway, an invoice without a customer does not make sense.

If you add the invoice to the customer's collection of invoices, you can access it via this collection. So the reference to the customer is implicitly given by the context. Otherwise, you'd need an explicit reference to the customer as part of the invoice.

Issuing an invoice can be perfectly modeled by making it a transaction which a) sets the state of the invoice to "issued" and b) adjusts the balance of the customer accordingly.

Now, you might argue that the balance as a field introduces redundancy into this model since, at any point in time, you could sum up the customer's invoices having status "issued" and subtract all what you're owing him at this moment. In OOP, this is would not be a transparent decision:

You might hide the way you get the customer's balance by means of a getBalance() method which is implemented either by reading the balance field or else by calculating the value dynamically. Nethertheless, when issuing the invoice, in the first case you needed to update the balance field while in the second no action was required. Similarly, when changing the status, in the first case you'd need to adjust the balance again.

Both options are legitimate, and usually, you would base your decision on some non-functional criteria. E.g. it might be too costly to retrieve all the customer's invoices with state "issued" plus the amount you owe him at a certain point in time. In this case, it would be more practical use a balance field in the customer object. Your gain in performance then is to be weighted against the expenses for maintaining redundancy, the risk of failure to do so, and so on.

Having these observations in place, I'd like to see how an FP approach could influence that decision. Does it bring advantages?

Anyway, from the domain view issuing an invoice has an intended side effect: It increases the customer's balance in favor of me, the issuer. This is independent of any programming language. After having issued the invoice I want the customer's balance to reflect this fact. Actually, the invoice is something which documents the "side effect", so this side effect is just the reason why I would issue an invoice - the side effect is the desired essential, not the invoice being issued (which comes more or less close to a log entry). Why then construct a function which avoids it? Moving the responsibility to the caller urges him to execute both actions explicitly: He's left now with two responsibilities.

Then, we come close to a question posed in one of the later exercises: If an invoice is similar to a logging entry, is issuing it still essential?

As the example evolves, we see a customer object in its initial state and then a different customer object which is altered after creation although its said to be immutable (its balance is set to the new amount - page 42, step 2). So what is the point with having these two customer objects which describe the same real entity with two different states? Which one of the two objects am I interested in?

I hope you see that I don't argue at all against FP. Instead I'm arguing against an example which creates confusion instead of clearifying ideas. Playing an advocatus diaboli, I do this from an OOP point of view. No idea what FP is good for, so please convince mesmilie

The weakness of the customer/invoice example becomes evident when suddenly another example falls from heaven - the shopping cart example. I really would prefer having an example which is appropriate to demonstrate all the principles equally and why they are advantageous. In the example, first I'd like to see a clear description of the realitiy it is modelling and which problem the code is intended to solve, second I'd like to see high quality OO code if OOP is to be a starting point. And then I'd like to see why an FP approach solves the problem more adequately as the OO approach.