Ho appena iniziato a familiarizzare con la programmazione funzionale, principalmente tramite F #, e c'è un particolare idioma funzionale che non comprendo pienamente i vantaggi di. L'ho visto descritto in alcuni punti, ma mi riferirò alla versione in questo articolo .
L'articolo descrive un carrello della spesa con diversi requisiti, che in pratica lo pone in uno dei tre stati: Empty
, Active
(occupato da uno o più elementi ma non ancora pagato) o PaidFor
. Si sostiene che usando F #, è facile garantire la correttezza perché il compilatore ti impedirà di eseguire operazioni illegali sugli stati del carrello. Ad esempio:
let addItemToCart cart item =
match cart with
| Empty state -> state.Add item
| Active state -> state.Add item
| PaidFor state ->
printfn "ERROR: The cart is paid for"
cart
Solo gli stati Empty
e Active
hanno metodi associati a Add
, quindi se provassimo a scrivere:
| PaidFor state -> state.Add item
o simili, otterremmo un errore del compilatore.
Per confronto, un approccio OO potrebbe (in C #) apparire come:
interface ICart
{
ICart AddItem(CartItem item);
}
class EmptyCart : ICart
{
public ICart AddItem(CartItem item)
{
return new ActiveCart(item);
}
}
class ActiveCart : ICart
{
public ICart AddItem(CartItem item)
{
return new ActiveCart(_items.Add(item));
}
}
class PaidForCart : ICart
{
public ICart AddItem(CartItem item)
{
throw new InvalidOperationException("Who cares about Liskov anyway?");
}
}
Quello che sto cercando di capire è quale vantaggio ottiene un cliente dall'avere addItemToCart
piuttosto che ICart.AddItem
. In entrambi i casi, non esiste un controllo in fase di compilazione che impedisca al client di utilizzare quella funzione con un carrello a pagamento. In ogni caso, il cliente non ha il controllo su ciò che accade nel caso di errore.
La versione funzionale potrebbe essere modificata per dare qualche controllo al client su ciò che accade nel caso di errore (un callback, o qualche tipo di wrapper sul risultato), ma l'orientato agli oggetti potrebbe essere modificato per farlo altrettanto facilmente (magari con un metodo TryAddItem
, che sarebbe un po 'più idiomatico OO).
Quindi cosa mi manca?
Modifica
È stato detto che nei commenti che nella versione C #, chiunque poteva creare un'implementazione ICart
. Per risolvere questo problema: una versione alternativa sarebbe sostituire ICart
con una classe Cart
astratta e renderne interni i costruttori. Questo non è l'ideale e in alcune situazioni potrebbe non essere fattibile, ma penso che nel caso generale funzioni abbastanza bene.