What is the pattern called where you override the values of an existing record but keep the values you didn't override?
I suppose that you can view this in more than one way. As jaco0646 suggests, inheritance, or, perhaps, more generally, subtyping could be one way of looking at it.
You can also think of it as a mask, as in a bitmask:
10010101 10100101
OR 11110000 11110000
= 11110101 11110101
You can think of this as a binary operation on binary arrays, as in x || y
, where the left-most value x
is the default value, and y
contains your overrides.
If you generalise this idea, you can think of it as a binary operation on a type of data (e.g. configuration data) that can be partwise overridden on multiple levels. Something like x <> y <> z
, where x
is the most general set of data (the defaults) and the further to the right you get, the more specific the overrides get.
If you institute the (sensible) rule that this operation is associative, you have a semigroup. One semigroup is called last, which always picks the last (i.e. rightmost) value. That gets you halfway there, but you only want to pick the last value if it's there. You can combine it, then, with 'optionality', in the following called Just
and Nothing
(AKA Maybe
). Here are some simple examples in Haskell:
Prelude Data.Semigroup> Just (Last 42) <> Just (Last 1337)
Just (Last {getLast = 1337})
Prelude Data.Semigroup> Just (Last 42) <> Nothing
Just (Last {getLast = 42})
Prelude Data.Semigroup> Just (Last 42) <> Nothing <> Just (Last 2112)
Just (Last {getLast = 2112})
If the default value is a 'neutral' value, you instead have a monoid. Both semigroups and monoids compose.
If you consider something like overridable configuration values, a configuration set is typically a set of different values. As an example, let's consider a timeout value in milliseconds (an integer) and a default name (a string).
Thus, you can consider this configuration as a tuple. (You can create an explicit type with field names for each value, but that would be isomorphic to a tuple.)
Here's an example of a default value:
Prelude Data.Semigroup> (Just (Last 1000), Just (Last "Foo"))
(Just (Last {getLast = 1000}),Just (Last {getLast = "Foo"}))
If you want to override only the name, you can do that:
Prelude Data.Semigroup> (Just (Last 1000), Just (Last "Foo")) <> (Nothing, Just (Last "Bar"))
(Just (Last {getLast = 1000}),Just (Last {getLast = "Bar"}))
A later step can also override the timeout:
Prelude Data.Semigroup> (Just (Last 1000), Just (Last "Foo")) <> (Nothing, Just (Last "Bar"))
<> (Just (Last 2000), Nothing)
(Just (Last {getLast = 2000}),Just (Last {getLast = "Bar"}))
So, in essence, this 'pattern' is just a composition of semigroups, with the last semigroup as the inner semigroup, maybe/option in the middle, and composed with an appropriate tuple as the outermost semigroup.
Software abstractions (especially frameworks) that provide sensible defaults are often referred to as opinionated. This is in contrast to abstractions that require a client implementation for every variable. The latter was originally thought to offer more freedom; but it turns out that freedom is a lot of work and most clients make the same choices in the end. Opinionated abstractions offer the freedom not to choose.
You might also simply call it inheritance.