When you are the only programmer, this matters way less. Just do whatever based on your personal taste.
On the contrary, this noun-based programming explodes with complexity on large teams. Yes, interfaces are obviously important, but when every single thing is its own type and you try to solve problems with the type system leading to a combinatoric explosion of types and their interactions, what do you think happens when you scale the team up?
False. They are only similar to you. Haskell is a pure functional programming language and it is very much noun-based. Type classes like functors and monads are nouns that describe the structure of many types. Modern Haskell best practices involve way more types than other languages. Very few people operate on JSON for example, instead almost everyone will parse that JSON into a domain-specific type. The “parse don’t validate” idea is based on the idea that data that has been checked and data that has not been checked should have different types.
Rust also is decidedly not OOP: it does not even have inheritance. Yet it also has way more types than usual. Most languages would be satisfied with something like a Hashable interface, but Rust further decouples the calculation of hash values into traversing a type's fields and updating the internal state of the hash function. This results in both Hash and Hasher types. This is a wonderful design decision that helps programmers despite an increase in the number of nouns.
> a combinatoric explosion of types and their interactions
Absolutely not my experience at all. There is nothing combinatoric here. Most types do not interact with many other types. The structure is more like a tree than a complete graph.
> It's just so... dogmatic. Inexpressive. It ultimately feels to me like a barrier between intention and reality, another abstraction.
On the contrary, it's a much more effective way to express intention when you have a language that can implement it. Programmers in C-family languages waste most of their time working around the absence of sum types, they just don't realise that that's what they're doing. Yes it is an abstraction, all programming is abstraction.
Yeah good luck doing that in the type system in a way that is maintainable, open to modification, an scales with complexity.
data UnvalidatedFoo = UnvalidatedFoo
{ unvalidatedOmfg :: String,
unvalidatedBar, unvalidatedBaz :: Int
}
data ValidatedFoo = ValidatedFoo
{ validatedOmfg :: String,
validatedBar, validatedBaz :: Int
}
validate :: UnvalidatedFoo -> Maybe ValidatedFoo
validate UnvalidatedFoo {..} = do
when ("wtf" `isPrefixOf` unvalidatedOmfg) $ do
guard (unvalidatedBaz > 20)
if unvalidatedBaz > 10
then guard (unvalidatedBar >= 1 && unvalidatedBar <= 42)
else guard (unvalidatedBar >= -42 && unvalidatedBar <= 1)
pure ValidatedFoo {validatedOmfg = unvalidatedOmfg, validatedBaz = unvalidatedBaz, validatedBar = unvalidatedBar}