I'd say inheritance is NOT a good use-case for Person / Employee
A person can play many roles, at the same time. Composition is better in this case. A person has many roles
Inheritance is better for types of legal parties though (see The Party Model). A person is a legal party. An company is an organisation is a legal party
I'd say that inheritance, with method overriding, is the worst anti-pattern of mainstream OOP, which contains a number of otherwise sound ideas, like encapsulation.
I like the Go's approach to that (taken from Oberon), and the Rust's system of traits.
In one large codebase I saw there was a rule to make classes either abstract (without implementation of key parts), or final. Everything else is done via interfaces and composition.
> I'd say inheritance is NOT a good use-case for Person / Employee
Good call. If I wanted Person distinct from Employee, how I'd probably start, in an analysis model, is have Employment as an association/relationship between Person and Company.
In my meta-model, associations like Employment can have attributes, and that might be where things like "employee number" and "salary" come in.
And, taking that a little further, we can represent the history of how things like salary change over time.
(Meanwhile, a project that went with only an Employee record/object might not have saved any coding time initially, and they're quickly piling on expensive, hard-to-maintain kludges, just to meet evolving business needs. Even the first time an employee got a raise, they might've already been hitting incorrectness cases, just trying to get two parts of the system to use the right salary for the right time.)
As someone quite in love with functional programming (background in Haskell and Scala), one thing that seems quite obviously missing here and in my Javascript experience so far is the use of typeclasses as a form of ad-hoc polymorphism.
Is there a nice (i.e. not too much boilerplate) way of using them in the land of Javascript/Typescript?
This is definitely the best option in the TS ecosystem but it's not meaningfully implementing typeclasses from the perspective of the end user. There's no single `fmap` for example, instead you'll be using dedicated `Array.map`, `Option.map`, etc.
As far as i remember there's functions that take explicit typeclass dictionaries (that you can compose via contramap). So I guess they sort of do typeclasses, but without implicits, which is pretty painful IME. Example: https://gcanti.github.io/fp-ts/modules/Functor.ts.html#map
As someone new to JS, a friend of mine insists that I always use classes and inheritance, and that functional style / composition is bad. However, most of the newer libraries I read through all seem pretty functional, whereas the older ones are full of classes. Personally, I find myself writing everything with factory functions and composition because it feels natural to the way I think about code.
Am I wrong, and should I be listening to my friend? He has much more experience than me.
I've been getting paid to write JS/TS for about 10 years. I think I've used the `class` keyword less than 5 times in the last 5 years and feel better off without it. I've found that a more functional style leads to better separation of state/data from any logic you have to write, allowing for a better testing experience and an easier time refactoring. Plus it means I have to think way less about what `this` means.
At the end of the day, features of languages are just tools, and sometimes they fit the problem you're trying to solve. I think it's also important to decide what tools to use in the context of what experience you and your team already have.
Also, it's hard to have small .js files when you use classes, and I love small files. I think Go made a great design decision to allow functions on structs to be implemented in separate files.
I once heard someone tell me that good OOP ends up looking very functional. I've taken that to heart, and I believe the code I write is better as a result.
I do use the `class` keyword in my js, but I'm also on a team of C++/Ruby programmers who are forced to write js, so the more I can do to make it more familiar, the better. As you said, it's just a tool. Some people like nails and some people like screws; there are objective advantages to each, but either one will hold a house together.
> I once heard someone tell me that good OOP ends up looking very functional.
Immutability and thinking about if a class should really be stateful are the major wins.
I was recently looking over some code that essentially just did transformations, but all the inputs and outputs were member variables, so it took some effort to track what creates what and what depends on what. The members also had empty defaults, so if you mix up the order you call the functions in, the code still appears to work. It wasn't "wrong;" it was hard to follow and error-prone.
Using 'classes' doesn't imply OO. They're a perfectly good way to organize data, at very least for data objects in TS.
It's fine to want to go functionally heavy, but if you're not using classes - which are the foundation of 'types' - then why bother with 'type'script? (Unless you mean to say 'interface'? Which is obviously not 'class' but implies the same usage of OO-founded keywords)
In TypeScript classes are in no way foundational. At the type level they are roughly implemented as a combination of two types: the instance type and the "newable" constructor function type.
Pure functions, polymorphism, inheritance, composition, factory functions, classes... All of those are tools. They have benefits and drawbacks that are circumstantial (they depend on the language, the problem, the time pressure to ship, the skill level of the team, and many more other factors). Extremely successful codebases have been written using almost any combination of those tools. What those codebases have in common is that the people who wrote them understood their tools. So if I were you I would strive for understanding.
In that spirit, it's good that you question what experienced people tell you. What about asking your friend to explain why, at a fundamental level, their recommendation is good? "Always use classes and inheritance" as a hard-and-fast rule is not very helpful.
This is a great answer. Life is about tradeoffs. Asked to tell the best way to do something, a good engineer pretty much always will answer "Well, it depends..." and list pros and cons of a tool.
I’m mostly the opposite: classes in JavaScript feel like a kludge invented to pretend JavaScript’s version of OOP is what people were familiar with in languages like Python, Java and C++.
If I’m going to write OOP JS, I generally prefer to use object literals and Object.create. However, I’ve mostly switched to the “Unix philosophy” style that libraries like Ramda promote: lots of sharp tools that are composed to build your programs.
I'm generally on your side. Functional style and composition are better styles than inheritance since inheritance is too rigid for most things. Inheritance often results in substantially more refactors required, and possibly even worse when at some point it's impossible to refactor.
With that said, in real-world projects, the bigger problem is to find the "greatest common divider" - which are the most common skillsets people are familiar with and settle with that. For example, in many full-stack projects, people are more familiar with the backend language which is mostly OO, and it might be a good idea to settle with OO style in the front-end as well. If the functional style is desired in the future, make sure to educate everybody, have a plan, set the direction so everybody can head toward it.
Some Scala projects are struggling with this issue. It results in mixed codebases, where OO guys cannot understand half of the codebase, and the functional guys are angry at OO guys who wouldn't learn, while keep introducing new novelty every month. Operating a team is more than technology, a key part is to keep everybody aligned, and make sure everybody was well taught with the knowledge they need.
> a friend of mine insists that I always use classes and inheritance, and that functional style / composition is bad
If that is really what your friend is saying, I would not listen to him. The world (including the world of programming) is bigger than "always use / do X".
Yes, your friend is wrong. Like langager, I've been getting paid to write JS/TS for some years now (okay not 10!). Maybe because I started off in Clojure/Script, I still tend toward a functional style - and even React these days is very functional.
I certainly am not opposed to using classes. If the problem begs to be thought of as a noun, then I will reach for a class to represent it. Especially if we have a collection of nouns, which are related.
But in general I prefer to code in verbs, piping data through a series of functions and then mapping over them at last to produce components, for instance.
Your friend is opinionated. I work on a large TS project which has classes at its core. While it does make sense to use Classes at some locations, it's not always an obvious choice. Writing and understanding Unit tests is painful in some places with classes. I keep business logic in classes and move the utility functions somewhere else(where I ensure functional paradigm). Writing Units Tests for such code is a breeze.
Tangentially, is there something implicit about class hierarchy and game development? Most engines and libraries are built on deeply-nested, hard to follow, inheritance models. I read about entity-component systems, but there's seemingly no big, functionally-oriented game engines. Why? Legacy? Typical languages (C++, C#, etc.)?
In my personal experience, the class syntax specifically gets used very infrequently. Proper prototypal inheritance was more common “back in the day” but my own preference and I _think_ what a lot of others are leaning towards these days is composition.
The only valid use of class is to create nominal types in TS. Take a hint from the react devs, who even switched from compositional classes to functions (never inheritance though).
There is a lot of wrong in this article: inability to articulate what polymorphism is, misunderstanding of prototypes, bad JS practices (`var`, `.__proto__`), falsehoods on programming languages including JS itself, confusion around ECS, unfinished examples... I suspect the author is in the early stages of their learning journey and Dunning-Kruger is in full effect.
You might be right but your last sentence is the reason why people are apprehensive about sharing their work and writing more articles. There's no need for it.
There is no judgment in what I said. DK is a fact of life and we will all experience it at some point. I know I have, and quite badly.
I wish when I was younger someone had told me "look, you don't really know what you're talking about, here's how things really work". If no one tells you, how long does it take for you to realize and how much time do you waste thinking you know what you don't know? Again, the author is at the start of their learning journey. I'm not saying they're a bad person or a hopeless case.
And finally, people should not be apprehensive about sharing their work in general, but I think they should be when writing articles that purport to teach others.
Okay, "You don't know what you're talking about." :) There is a clear judgement on your part.
For me my own recent DK-moment was I'm learning a foreign language and in a shop asked for a simple item with a simple phrase, sure I had it right. They had no idea what I meant and I got angry because I thought you were just trolling me. Relating situation to a friend who let me know my accent was just wrong, so hard to understand. What got me is sometimes my accent good, sometimes it's not, day by day. But I was so sure, because my level is quite low. Haha
For you, with your judgement, you don't know whether it's true or not they are in DK, additionally you yourself may be mistaken in your JS criticism, it could be you that has DK, and they are correct.
Also, "You don't know what you're talking about.", OK, so, " this is how it works": generally when you criticize someone as doing X, and then backpaddle on pushback to say you do the same, you should recount a specific instance where you did X "and quite badly" because that shows you're genuine, and willing to be vulnerable, as you have tried to make the other person vulnerable by invoking the criticism you, by that omission, seem reluctant to invoke upon yourself. :) ;p xx
For me, a recent one where I didn't do that was a friend was telling me about their job offer progress, and salary negotiation. They wanted Y, but didn't say, and company was offering less. I told them how they should just ask for what they want, and try to get it. Friend pushed back, and got upset. Fast forward to same friend, next job offer, this time I shared a story how I hadn't pushed for my preferred salary in a previous job, then told how I felt not enjoying the job and feeling I was not getting fair reward each work day. Much better conversational outcome, friend more receptive to advice this time. Friend ended up with high salary they were happy with.
Better yet lead with the story of your mistake, tho that's hard to do. Either way, sharing your own makes you seem more credible in trying to bring someone up not put them down, shows you speak from experience, and engages someone emotionally into feeling for you rather than just being defensive :) ;p xx
I disagree with some of what you say, but I think your last point is correct.
>you yourself may be mistaken in your JS criticism
It should be easy to verify I'm not. `__proto__` is indeed bad practice [0] and I'm pretty sure criticism of `var` vs. `let` and `const` is a Google search away. Same idea for the other problems I listed, but the details are besides the point.
>There is a clear judgement on your part
I'm not sure what makes you think there is. It was important to first point out that this contribution to HN is a rather poor one on technical grounds. People might have taken away bad practices from the article. Then, I felt it would also be interesting to share my suspicion about the author's state of mind. This is after all what commentators of all kinds do, from movie critics to sportscasters.
>sharing your own [...] shows you speak from experience, and engages someone emotionally into feeling for you rather than just being defensive
This rings true and I could be a better communicator if I applied this advice. So here goes: this [1] is my most upvoted SO answer that I wrote years ago. The code I suggest is rather bad, as I'm sure any experienced C++ dev would agree, and I find the style pompous. It makes me cringe a little, but that answer also demonstrates an interest in technical topics and an eagerness to share, just like the author's article.
Cool, yeah I wasn't saying that your JS was good or bad, just the possibility of it being so. I'm with you, I think you're good at JS, but then I don't really know. And neither, in truth, do you. We're both judging.
I think you need to remain open to the possibility, not just of DK but of we all have something to learn. I think I'm pretty good at JS and have popular projects and good test scores, but I also fail some JS questions (the other day in the FB PE screen I couldn't work out that the regex /^sep\n$/g wouldn't actually match. I was confused and thought the newline was needed as well as the $. Haha. Just one example.) I try to give my self a rating of 7. Keeping open to the possibility of being wrong, is more important than being right or wrong. I can't really explain it right now (maybe one reason is it's super late here, another might be I just don't know how to explain it right now) but I feel that humility is important, more so the more you know and the better your skill. Somehow I feel more humility make your skill better, especially the more skill you have.
Thanks for your humble and vulnerable example, I appreciate it! It gives you great credibility. If you were more open to the possibility of things you don't know, and not so absolutist about your JS knowledge, I would have felt you were even more credible.
Finally, you have a blindspot. You can't see that you judgement of the JS skill was actually a judgement. Because you're so sure you're right about it. Ties in with what I said before about humble and open to wrong. Of course it's a judgement, it's a "you think" it's like this, not a "it is like this". There are other perspectives that are valid. I think if you see that, then it will make wielding this big sword you have an easier and more pleasant experience! :P ;) xx
The problem is JS - it's been a fuzzy bit of confusion since inception, it was never remotely intended to do what it's doing, and nary a tiny fraction of JS developers could authoritatively explain the differences between prototype __proto__ etc. - I keep a chart handy as a reminder and much like C++ stick to a set of rules to avoid unknowns.
There's a reasonable chance that with a much more succinct and clean '20/20 hindsight' the articles like this would be either clear, or frankly unnecessary.
I write all my scripts top-down so I need not to think about complex stuff and also divide on which method is the best because there's always another programmer who has a different opinion.
I've never developed for the web out of fear of talking employers into recruiting me as a UI/UX designer. I've also been pretty thrown off over Web Development being known as a massive mess.
I've worked on desktop applications in C# so far. Is web development anymore pleasant or is it all just muckwork?
I develop front-end, mostly React, and I find it to be a lot of fun. The large community has resulted in some amazing tooling, and the unopinionatedness of React means I can write code in the way I like.
> I've never developed for the web out of fear of talking employers into recruiting me as a UI/UX designer.
I do think that as developers we need to help with design in some limited ways. Often designers will miss things. How is this going to look at different screen widths? Is this accessible? Does this function the way users expect websites to function? I give quite a bit of feedback to designers I work with. But nobody who ever saw me design something from the ground up would try to get me to do it full-time.
The very title is kinda scary, in the context that everything was kind of hacked onto JS. While it's probably important to understand, the 'answer' I think, without being ham-fisted, is probably to use Typescript in most cases.
A person can play many roles, at the same time. Composition is better in this case. A person has many roles
Inheritance is better for types of legal parties though (see The Party Model). A person is a legal party. An company is an organisation is a legal party