4 ms·
I'm not entirely in agreement. Use of object-oriented programming paradigms here would merely distribute the logic that is necessary to achieve the desired ma
by sidlls 10y ago
I'm not entirely in agreement.
Use of object-oriented programming paradigms here would merely distribute the logic that is necessary to achieve the desired mapping over multiple points in the code.
The example function presented is only marginally too complicated. I'd split it in two: one to obtain the book list given the same arguments as the example function, and one taking the result as its only argument to build the mapping.
I find myself shying away from rigorous adherence to encapsulation more and more these days. I prefer small functions that operate on data explicitly.
Edit: and I'm a bit confused how the example has anything to do with "JSON-driven development", other than the coincidence that a hash/dictionary is the core data structure being manipulated here. This example function could exist and be (mostly) reasonable had JSON never existed. I'd expect to see an argument that the JSON serialization schemes that abound are problematic, given the title.
- mattnewton 10y agoWith the added benefit that you can reuse these functions for new data shapes more often than I expected when switching to this style.
- sidlls 10y agoAnd better testability.
- milesvp 10y agoThis. I've been programming this way for over a decade. Long before JSON was a thing. I find I rarely need anythng more than a list or a dict for most of the data manipulation I do. Being on the web has only strengthened my tendency for this, since everything ends up being stringly typed anyways. Nearly every function/API I write is: get some data from somewhere (hopefully serialized), manipulate the data, return data (very possibly serialized). Nearly every time I've seen coworkers try to improve things with classes, it complicates the code, and often adds little encapsulation given how much we do is reliant on external data sources. Every once in a while I think how nice it would be to be able to use typed data and smart setters to avoid much of the bounds checking I have to do, but I find there's never enough code between the boundaries of serialization to make it worth the added complexity that this introduces (also my problem domain involves mostly copy so most things are basically strings, ints, or datetimes anyways).
- jowiar 10y agoLists are lists, and I have no issues there. It's dicts as objects (often nested) where things to get hairy. I often see folks end up relying on internal implementation details of other libraries, or other data sources, and things can subtly break (or explode in a ball of fire). The more systems I've built, the more I've wanted to have very well-defined seams between "inside" and "outside" -- well-defined interfaces with external APIs, libraries, databases, systems that may be maintained at a different speed, etc. Having an explicit translation/serialization/etc. step forces you to do this. It's not the only way, but pretty much every codebase I've interacted with that doesn't do this gets careless, and it's can get really messy when things get any longer than a "script".
- sheepmullet 10y ago> I often see folks end up relying on internal implementation details of other libraries, or other data sources, and things can subtly break I'm not sure how you can get around this as a consumer of a service? How do you know what is an internal implementation detail? Why are they exposing implementation details? As a producer of a service there are lots of techniques. E.g. - Only expose data and provide a spec for that data. - Provide "helper" classes in target languages for consumers to use - Publish an API spec + gaurantees - Always maintain backwards compatibility
- jowiar 10y agoAs a consumer, you're not going to get around it. But what you can do is quarantine it to a specific area within your application. I find dependencies to be a very natural place to divide up an application -- at the very least, to consider "What would have to change if I ripped this thing out entirely?". "What are the methods I would need to implement?", etc, and writing a translation layer implementing that interface. As a hypothetical, let's say you needed to implement a binary persistence layer in your application. Rather than interacting with S3 in 10 different places, you define a "BlobStore" interface with the methods that you need, code against that interface, then implement an S3BlobStore that handles the calls to Amazon using whatever library necessary.
- mikekchar 10y agoI'm also confused about this example. I've often seen similar code in C using structs. What's really missing for me is some context about why he wants these data structures and what he's going to do with them. Essentially he's doing a join on 2 tables and you are left with the thought, "Why do you need to do that join?" I think what he's really trying to get at is that he dislikes the style of programming espoused by one of the child posters: Make everything a dict/hash and write filters than manipulate those dicts/hashes. I think the reason he dislikes it is for exactly the reason I'm confused about his example: you can lose track of why you need the types in the first place. One thing you often see in Javascript (and I presume Python, although I don't have much experience in that ecosystem) is the idea that types don't matter. You have an object (essentially a hash) and you can transform it any way you want. If it is slightly more convenient to access your data in a different way, then transform, transform, transform. Now all your functions have different signatures: "No, in this function we use the store inventory, which is exactly the same as a book list, but grouped by store". And then you have 25 different functions all doing slightly different versions of the same thing to keep track of all the weird mutations of types along the way. Again, this isn't new stuff. We've been writing crappy code like this for decades. One of the nice things about languages like C++ is that it's such a PITA to define arbitrary data structures that you avoid doing it, but you still see variations of that theme even there. As for OO or not OO, I think it's a red herring. If I have functions: make_foo(bar, baz), print_foo(foo), manipulate_foo(foo), or if I have a class called Foo with a constructor(bar, baz) and 2 methods called print() and manipulate(), it's exactly the same thing. Even if you write the equivalent code functionally, mostly all you are doing is moving the context (bar and baz) out of the heap and putting it on the stack (yeah... I know... lack of mutability is a pretty important bit too ;-) ). This is almost as long as the original rant, but I'll jam one more thing in. Serialization, I think, has little to do with the problem except that people don't know how to separate their concerns at layer boundaries. The main bad idea that perpetuates is that I should have the same data structure in my database as is in my business logic as is in my UI views as is in my UI wigits as is in my communication protocols. Back in my day, we even thought that it was a good idea to serialize entire objects (with executable code!) from one end to the other, so I guess it's getting slightly better ;-) To sum up: you can't ignore types even when it is easy to morph types in your language. At your layer boundaries you also need to transform your data from one set of types to the other set of types (and you should never expect that a 1:1 mapping is automatically going to be a good idea). Within your layers you should never mutate your types and you should write functions with clear signatures. OO helps you do this. Non-mutating state is also a really good idea and functional helps you do this.
- afroisalreadyin 10y agoI think I failed to represent the scale in the example. That's a heavily modified function from a code base I'm working on, and the inventory, book, cell etc. dictionaries and lists containing them are all over the place, with similar looping logic (e.g. find item with given label) and combinations are all over the place. Adding the objects to the above function would of course complicate it in the sense that it would get longer, but it would improve the actual example considerably. I will try to come up with better sample code that represents my worries better.
- Goladus 10y agoStill, if there's way too much looping logic strewn around the code, that's probably because the code lacks good abstractions for the most routine data access tasks in this code. If the code used more objects, each data type would be packaged up with its own methods for looping over the data. For code that doesn't wrap every bit of data in a specialized type, you may find that a few small utility abstractions will clean up your code substantially. The tradeoff is that you must import these functions in every module that needs to use them, since they aren't passed to your code with the object.