Does "primitive obsession" as a poor design practice apply to development in Python? I have seen a lot of examples and discussion in the context of statically typed languages (like Java, C#), but there are also dynamically typed languages like Python. Do solutions to primitive obsession look different in Python without having explicit types?
- 5This is a really interesting question. What is the point of a forum if you close every question? It's OK for people to ask questions even if you don't understand why they are asking.JimmyJames– JimmyJames2022-08-17 21:25:58 +00:00Commented Aug 17, 2022 at 21:25
- 2@JimmyJames: This is not a forum though. It's a Q&A site. A forum implies open discussion, which StackExchange is not set up to do, e.g. there no real back-and-forth opportunity here. Other than an errant comment for clarification, it's a straight "ask question, give answer" flow. This comes with some rigidity as to how questions should be formed. If you want open discussion, and I agree that this has value too, it's better directed at a community with a format more suited to it (e.g. actual forums, a discord group, a reddit-like nested comment format, ...)Flater– Flater2022-08-18 09:44:52 +00:00Commented Aug 18, 2022 at 9:44
- 3@Flater Sorry, my mistake. What's the point of a Q&A site if you close every question?JimmyJames– JimmyJames2022-08-18 14:19:48 +00:00Commented Aug 18, 2022 at 14:19
- 2@JimmyJames the need to close questions comes from a need to have a site about a specific something. Something we're known for and can be trusted to be about. Failing to do that actually hurts the sites popularity. However, Ruthlessly closing rather than editing out problematic issues (thank you Doc) or commenting leaves few people willing to even ask questions. Too much in either direction kills the site.candied_orange– candied_orange2022-08-18 14:25:00 +00:00Commented Aug 18, 2022 at 14:25
- 2@PaulBendevis the close reason given is asking for the question to be more focused. The idea behind that is meant to narrow the question to the point that one good answer can squeeze out others. Otherwise the question receives an endless list of equally correct answers that are rated not on their quality as much as on when they were posted. With that in mind I note that almost all of your sentences end in question marks. Narrow that down and I think your reception will improve.candied_orange– candied_orange2022-08-18 14:31:35 +00:00Commented Aug 18, 2022 at 14:31
2 Answers
The simple answer here is 'yes': primitive obsession is still an issue in dynamically typed languages. The longer answer is that the issue is somewhat different in a dynamically typed language than a statically-typed one. I'm going to use Python as an example.
So the first bit of nuance is that in Python and similar languages, there aren't really 'primitives' in the first place. There are built-in types but they are objects. I only think we can mostly ignore this, but not completely. For most intents and purposes, they are used just like primitives are in other languages.
The other nuance is that in a dynamically-typed language, dependencies are not really defined by the type of an object, but rather by the operations it supports. For (somewhat of a silly) example, if I have a function that will add all the items in a sequence together, there's nothing stopping me from passing in a list of custom objects that support the __add__ method. This effectively means it's harder to create a primitive obsessed API but as Flater noted in comments on another answer, it doesn't prevent you from improperly using 'primitives' in a sub-optimal way.
The most common primitive obsession error (IMO) that I see often is the use of primitive numeric types to represent money. Using a decimal type is typically better than float, but it's still inappropriate. Money has special rules and passing it around as a bare number will often result in bugs and hamper the evolution of an application. For example, I once was talking to someone about an application they were involved in that used integer types for money. Then later, integer type they used was too small. They went through and changed all the integers representing money to doubles. I consider that two mistakes. So first off, consider trying to find every instance of an integer being used in a financial application and then determine what it's trying to represent. That's a major PITA in itself. Instead of using a primitive for money, a better solution is to define a Money type. It can be stupid simple initially: don't over-design things. But once you've done that, if you need to add a capability such as a wider range or support conversions, you can often fix that in one spot. And if you need to find all the places it's used, it's far easier to locate. Can you make a primitive obsession mistake like using base numeric types for money in a dynamically-typed language? Absolutely.
On the other hand, in my experience, dynamically typed languages make reusing existing types easier and I therefore don't feel the need to create as many custom types as I would in a statically-typed language. But you should still be considering whether a custom type is warranted and largely for the same reasons that you would in a statically-typed language.
- 3+1 for being willing to wade into the weakly typed issue. For the record, typing based only on what you use is called Duck Typing. And your money assessment was, well, on the money.candied_orange– candied_orange2022-08-18 16:42:04 +00:00Commented Aug 18, 2022 at 16:42
- @candied_orange You are correct that I am not using correct strong/weak static/dynamic or using the terminology precisely. I'll try to clean that up.JimmyJames– JimmyJames2022-08-18 17:17:50 +00:00Commented Aug 18, 2022 at 17:17
Primitive obsession is about failing to design a dedicated type that would abstract a concept and instead trying to force an existing type into the role. If the language in question has customizable types then the smell applies.
The whole static vs dynamic thing is just about where you declare the type. Has nothing to do with primitive obsession.
- 1I just think this is might be a little deeper question that it appears. Consider truthiness and the issues that can come from that. I'm not saying the answer is 'no' (I think it applies) but there's some nuance here.JimmyJames– JimmyJames2022-08-17 21:34:07 +00:00Commented Aug 17, 2022 at 21:34
- 7I think this answer is very succinct and accurate. No nuance here. If you pass two ints and a bool around your program instead of a class that encapsulates them, it's primitive obsession. Full Stop. Whether the language has explicit typing or static typing has no bearing on itmarstato– marstato2022-08-18 01:49:31 +00:00Commented Aug 18, 2022 at 1:49
- 1@JimmyJames The issue isn't with what types are expected, the issue is with what types are being passed in (by the consumer) to represent the needed data. Yes, in strongly typed languages the two are generally equivalent, but not for weakly typed languages. Primitive obsession comes down to "Hmmm, I want to pass [this information] along, which existing type fits best?" => The issue is that the thinker is omitting the concept of creating their own type that can perfectly describe the information they're trying to pass, which is the superior option as it leads to better abstraction.Flater– Flater2022-08-18 09:51:09 +00:00Commented Aug 18, 2022 at 9:51
- 2@JimmyJames: It changes the ways in which someone can use the wrong type for the job, but the principle as to why it's wrong remains unchanged. Besides, even in strongly typed OOP languages you could do exactly the same as in a weakly typed one (by using an overly vague base type such as
object), so there's no real distinction other than strongly typed languages having more tools available to the designer to disincentivize primitive obsession.Flater– Flater2022-08-18 17:14:55 +00:00Commented Aug 18, 2022 at 17:14 - 1@JimmyJames right. And as Flater points out in Java you don't have to (Integer is an Object). In either case it's the receiver who complains that
adddoesn't exist if it doesn't. Thus it's the receiver who decides what type is acceptable regardless of language static, dynamic, looseness, weakness, or ducks. The caller only gets to decide what concrete type to send. Not if it works.candied_orange– candied_orange2022-08-18 21:58:39 +00:00Commented Aug 18, 2022 at 21:58