4 ms·
I don't know how you can claim that static typing & pattern matching don't help with maintainability. In languages with exhaustiveness checking and good pattern
by leshow 7y ago
I don't know how you can claim that static typing & pattern matching don't help with maintainability. In languages with exhaustiveness checking and good pattern matching you can often make a change in one spot and literally just follow the compiler errors to implement a new feature. They help massively in refactoring, and that's important for maintanability.
- mumblemumble 7y agoI find they don't help with maintainability in most my business applications because they're solving the wrong problem. It's sort of like when someone spends a lot of time carefully optimizing a piece of code that doesn't have anything to do with the application's actual performance bottlenecks, just because that's the part that's more fun to optimize. The part of the business applications I work on that's a problem is dealing with the outside world. The data is messy. It's inconsistent. The protocols I'm using to communicate are invariably something horrendously loosey-goosey like JSON or XML. Stuff like that. And so, an inordinate amount of time I spend doing business applications in static languages ends up being spent on taking the messy, messy outside world and trying to create a clean, well-typed, rigorous façade for it so that I can operate on it inside my blissfully statically typed fantasy world. And all that static typing never seems to save me in practice, because the software quality problems I run into almost never crop up in the bits that I can operate on in a Haskell-friendly way. It's invariably in some mismatch between the outside world and my domain model that I failed to deal with accurately, which means that it's in my mapping code. Worse, oftentimes it's because of my mapping code, because my statically typed domain model ends up accidentally placing requirements on the input that aren't strictly needed by the business logic; I just unwittingly introduced them in the course of my efforts to get the types to line up.
- yakshaving_jgt 7y agoIn practice, this means you would rather nils permeate throughout your system rather than being caught at a system boundary, i.e., where you parse and validate that loosey-goosey outside world data.
- mumblemumble 7y agoPerhaps. I think it's worth considering. In most the systems I work in, null permeates both the input and the output, and can often even have its own semantic value. i.e., "there is a value for this key, and that value is null" might actually be semantically distinct from "there is no value for this key". . . it's gross, but it happens, and whether or not it happens is often outside my control. And I am beginning to suspect that it's actually safer, and even simpler, to fully live in and be fully aware of the reality of the business domain I'm working in, than it is to try and live in a bubble and pretend that life isn't complicated. And I'm also beginning to think that it might be wise to mind my own business. . . which includes not worrying about whether or not a value is null unless and until I find that I need to care whether or not it is null. Rejecting input because a value wasn't set when I had no intention of even looking at it is just such a grave violation of Postel's law. If I find that I'm only doing it because I need to satisfy the type checker. . . seems like a foolish consistency to me. Perhaps if I could live in an alternate reality where things like JSON and MongoDB hadn't happened, and we instead decided that clean and consistent data is every bit as important when sitting on magnetic disks or traveling through fiber optic cables as it is when bouncing around in silicon. Oh, that would be wonderful. I dream to live in that world. But that doesn't seem to be the reality I occupy.
- yakshaving_jgt 7y agoFrom the example you gave, it seems like we're talking past each other. The systems I write also have to deal with JSON with nullable fields, and with fields I ignore while parsing. Aeson for instance gives you complete control over how strict or lenient you wish to be when trying to parse data. The idea I was trying to convey was that if you care about marshalling data into types a la Haskell, then you can code less defensively when writing code for the data you actually care about. You do that defensive validation in just one place, as opposed to sprinkling nil checks all throughout your system. If I'm understanding your system correctly, it sounds like you have unreliable input, and you want the same output, only with some of the fields updated if they're there. Haskell readily lets you do this too. The lens-aeson library is perfect for this. Lots of examples for that here: https://www.snoyman.com/blog/2017/05/playing-with-lens-aeson https://www.snoyman.com/blog/2017/05/playing-with-lens-aeson Also, I am not sure how you differentiate between a null value and no value, but whatever mechanism you're using you could also use to model those two different types of null as actual types in Haskell. > Oh, that would be wonderful. I dream to live in that world. But that doesn't seem to be the reality I occupy. It's hard[er] to discern tone through the medium of text, but it sounds like you're suggesting Haskellers live in some fantasy world where all the data is perfect and everything is pure. I don't really understand that perspective. I make 100% of my income from writing online business software in Haskell. I employ other programmers to write Haskell for me too. We live in the same world you live in, but we might just approach it differently.
- UK-Al05 7y agoHaving some kind of shell which cleans up the messy outside world before moving into the core is a good thing.
- lucasdicioccio 7y agoI see roughly the type of issue you face. For the case where there's some good value/effort ratio to invent internal representations, Haskell's being "strong" on parsing tasks make it a good fit. When this ratio is less clear: I found lenses, albeit a bit foreign to typical Haskell code, are a really good addition (for me it's akin to a new language, like `jq` for arbitrary in-memory structures) for extract/read tasks. Anecdotal evidence: I recently had to turbo-build a tool to generate statistics over five-digit numbers of very complex business objects (including dates, strings, IDs, boolean as whatever string, ill-named columns) scattered in a number of systems (some web apis plus some CSV extracts from you don't really-know where). Using 'raw structures' like Aeson's JSON AST with lenses was more than good enough. Lenses have typically solved the "dynamic / cumbersone shape". Then I had to create a CSV extratc with like 50 boolean fields, reaching to intermediate techniques like type-level symbols allowed to really cheaply ensure I was not mixing two lines when adding a new column. I could even reach to Haxl from Facebook to write a layer of "querying" over my many heterogeneous source to automatically make concurrent and cached fetches for it. The main difficulty in this setup is to keep the RAM usage under control because of two things. On the one hand, AST representations are costly. On the other hand, concurrency and caching means more work done in the same memory space. Overall, got the data on time at relatively low effort (really low compared to previous attempt - to a point that some people with mileage at my company thought it would be impossible to build timely). Pretty good experience, would recommend to a friend.