4 ms·
> For example, a map should normally return an Optional<ValueType> when you try to retrieve a key's association. However, there may be special cases where you k
by kerkeslager 7y ago
> For example, a map should normally return an Optional<ValueType> when you try to retrieve a key's association. However, there may be special cases where you know that a key is present (maybe it is a constant map, maybe you just set the value of that key etc).
Both of these cases are still pretty big code smells.
1. Just don't use constant maps. Instead of doing this:
const myConfig = new Map<string, int> {
"height": 72,
"weight": 160,
};
[...]
var height = myConfig["height"].matchOptional(
ifNull: () => 0,
ifValue: i => i,
);
...use a constant structure (with an anonymous type):
const myConfig = struct {
height: 72,
weight: 160
};
var weight = myConfig.weight;
You can verify whether height or weight are null at compile time this way[1].
2. If you just set the value of the key, instead of doing this:
dictionary[word] = getDefinition();
let definition = dictionary[word].matchOptional(
ifNull: () => "",
ifValue: d => d,
);
...do this:
let definition = getDefinition();
dictionary[word] = definition;
I'm aware that I'm playing fast and loose with the syntax of our pseudo-language, but note that avoiding the optional will is terser and simpler than using the optional and eating the null case--this is true in most cases in most strongly/statically-typed languages. Not only do you learn to lean on the type system in a strongly/statically-typed language, but if the syntax is well-designed, it makes it easier to lean on the type system than to not lean on the type system.
[1] You may say, but what if I'm loading from a file? The common pattern is to load a config from a file as a map, and then load it into a struct, setting defaults, like so:
const defaultHeight = 72;
const defaultWeight = 160;
JsonObject configJson = json.loadFile("config.json");
const config = struct {
height: configJson["height"].matchOptional(
ifNull: defaultHeight,
ifValue: v => v.asInt(notInt: v => throw Exception("Invalid height \"{}\" in config.".format(v)))
),
weight: configJson["weight"].matchOptional(
ifNull: defaultWeight,
ifValue: v => v.asInt(notInt: v => throw Exception("Invalid weight \"{}\" in config.".format(v)))
};
You eventually hit cases with user data where you can't handle it (hence the throwing exceptions) but this pattern allows you to fail early, and with descriptive error messages.