3 ms·
I think the post doesn't give a fair comparison as in the if/else case there's no need to use "else" blocks if you're throwing an error or returning. In this ca
by harg 5y ago
I think the post doesn't give a fair comparison as in the if/else case there's no need to use "else" blocks if you're throwing an error or returning. In this case I think simple "if" statements are cleaner and certainly more "normal".
E.g.
if (!user) {
throw new Error("User must be defined.");
}
if (!user.firstName) {
throw new Error("User's first name must be defined");
}
return user;
- _greim_ 5y agoI'm definitely in the minority here, but I'd rather see the `else` block, since it's more explicit at-a-glance, and the logic has more symmetry with all outcomes on the same level. One reason I like RustLang is it treats this as an ergonomic issue by appending `?` for early returns, without block-nesting. So nice.
- CloselyChunky 5y agoWhen validation gets complex (e.g. there are many criteria to check), I like to build a list/stream/array (what ever the language offers) of tuples of predicates (functions from the object that gets validated to boolean) and strings (or functions from the object to string so I can have context in my error messages). Then iterate over the tuples, if a predicate fails, return the associated error message and throw an error/display the message to the user. In the end it looks something like this: var validators = Stream.of( Map.entry(user -> user != null, "User must be defined"), Map.entry(user -> user.firstName != null, "Missing first name")) validators.filter(e -> e.getKey().apply(userToBeValidated)).map(Map.Entry::getValue).getFirst() (This example uses Map.entry for tuples as Java lacks native support for tuples) This limits branching and you have all validation criteria neatly organized in the same location.
- harg 5y agoSure, if you're validating some data there're loads of better ways to do it than a bunch of conditionals. I was purely commenting on the switch(true) pattern vs some "if"s. That approach looks nice though. On that subject, JS has some nice libraries including io-ts[1] which has a functional approach using Eithers to encapsulate errors/success. [1]: https://github.com/gcanti/io-ts https://github.com/gcanti/io-ts
- CloselyChunky 5y agoSure, if you have an either/result type, the whole thing becomes a fold, where each validator is a function from user to Either<Error, User> and then validators.reduce(Either.right(user), (acc, next) -> acc.flatMapRight(next)) This way you'll end up with either the validated user, or the first error that occurred and all the other validators were skipped.
- fl0wenol 5y agoAgreed, +1 This pattern goes beyond JS and is very helpful for supporting tests; you've already factored out your validators. There's always going to be stragglers but you can define and push in an extra check on the spot (i.e. using lambda funcs at least in JS).
- wvenable 5y agoIf you are using a language with yield you can make this simpler (and more flexible) by using a generator.