Remix.run Logo
moth-fuzz 12 hours ago

I'm not a fan of the recent trend in software development, started by the OOP craze but in the modern day largely driven by Rust advocates, of noun-based programming, where type hierarchies are the primary interface between the programmer and the code, rather than the data or the instructions. It's just so... dogmatic. Inexpressive. It ultimately feels to me like a barrier between intention and reality, another abstraction. The type system is the program, rather than the program being the program. But speaking of dogma, the author's insistence that not abiding by this noun-based programming model is a form of 'lying' is quite the accusatory stretch of language... but I digress at the notion that I might just be a hit dog hollering.

lmm 2 hours ago | parent | next [-]

"Bad programmers worry about the code. Good programmers worry about data structures and their relationships."

> 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.

kccqzy 10 hours ago | parent | prev | next [-]

The kind of noun-based programming you don’t like is great for large teams and large code bases where there is an inherent communication barrier based on the number of people involved. (N choose 2 = N*(N-1)/2 so it grows quadratically.) Type hierarchies need to be the primary interface between the programmers and the code because it communicates invariants on the data more precisely than words. It is dogmatic, because that’s the only way it could work for large teams.

When you are the only programmer, this matters way less. Just do whatever based on your personal taste.

llmslave2 9 hours ago | parent [-]

That sounds eerily similar to the "OOP is for large teams" defence which is simply not true.

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?

kccqzy 5 hours ago | parent [-]

> That sounds eerily similar to the "OOP is for large teams" defence

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.

llmslave2 9 hours ago | parent | prev [-]

Agreed. It's often accompanied by the dogma "make invalid states unrepresentable" which sounds good until you start trying to encode into the type system foo.bar being 1-42 unless foo.baz is above 10, where now foo.bar can be -42-1 instead, but if foo.omfg is prefixed with "wtf" then foo.baz needs to be above 20 for its modifiers to kick in.

Yeah good luck doing that in the type system in a way that is maintainable, open to modification, an scales with complexity.

kccqzy 5 hours ago | parent [-]

You have misunderstood what it means to make invalid states unrepresentable.

    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}