I’m still unclear about which invariants need to be satisfied in order for the behaviour to be considered the same. The make function returns a card. If the card’s value is represented as a keyword, then (make :clubs :K) will return [:clubs :K]. If an integer representation is used, then (make :clubs :K) will return [:clubs 13]. The representation is exposed in a way that it typically isn’t in OO, but that’s OK so long as it is operated on via the functional abstractions.
But should (value [:clubs 13]) return 13 or :K? Are the values returned by the value function part of the behaviour we need to preserve? If, for this example, it must continue to return :K, then the advantage of the integer representation is lost (keywords are converted to integers, only to be converted back again immediately – though I suppose numeric-value could still exploit the integer representation if it ceases to use the value function). Alternatively, if it returns 13, then (make :clubs (value (make :clubs :K))) is invalid.
Or does the preservation of behaviour not extend to the parameters of the “constructor”, the make function? In that case, the constructor call would become (make :clubs 13), we get the benefits of the integer representation, and (make :clubs (value (make :clubs :13))) would be valid.
To put it another way: The values returned by make change to reflect the internal representation of a card, but that isn’t considered a change in behaviour because “card” is treated as a black box. Is the card’s value returned by the value function also considered a black box whose internal representation may change (from a keyword to an integer)?
Am I missing something painfully obvious, and making this more complicated than it needs to be?