That happens. Surely, if you know about this in advance, you can use a type along the lines of
data Field = Field Int | Null | Empty
And if you don’t have this kind of knowledge, well... That’s just a problem waiting for the right time to surface, whether you are using Haskell, Clojure or whatever else.
> 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.
I would’t say Haskell forces you to live in the bubble. Haskell forces you to think, in advance, about the relations between fields and types, sure. It doesn’t force you to use only simple, bubble-y types, though; the types can be something general, or some abomination (like the one above). I’m not aware of a use case where I wouldn’t be able to say “this will always be something”.
The only major distinction is, I would say, the place in code where you deal with the types. Clojure: In the functions all over the place (-), and for some fields, never (+). Haskell: Always in the topmost layer of your app (+), but you have to deal with all of them (-).
That’s the basic tradeoff between those two languages. Which pros and cons are more important depends heavily on your use case.
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
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.
Is agree.
Of course, if you can't describe the values in a business domain in types, I'd argue you aren't fully aware of it's reality. And when you've done that modelling, you aren't only aware of the reality, but you've encoded it so that the knowledge is preserved not only for operational use in the program, but also for anyone who reads the program.
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.