I know I’m reviving an oollllllllld topic here, but this was the highest-ranked Google result that wasn’t documentation I’d already read through.
By my reckoning, it makes sense to wish typespecs could use string literals, because API integrations don’t trade in atoms. And, structured as they are, it is still much more ergonomic to keep the keys they send over the wire in the string formats they were received in, than it is to write bespoke modules with struct definitions that enumerate all of those same keys and provide functions to convert maps parsed from JSON into those structs and then work with the data being received. Yet, trust is still rickety enough with APIs that it is also highly undesirable, and indeed a potential source of atom declaration leaks, to take JSON received from an API and parse it with, e.g., Jason.decode data, keys: :atoms
, like some kind of cowboy.*
And I know that @ericmj’s reply about atoms being “built for this use case” was to @Aetherus’s particular one-string-or-another problem, but I struggle to see the same utility to atoms in my use case, where I want to document the anticipated shape of data, which is optimistically well-structured, but subject to additions, revisions, and removals, as seen fit by its third party developer – whom I am unlikely to be able to persuade into reconsidering any such changes – of that integration. I’m only contemplating the typespec to give myself and my language servr hinting as to what keys I can expect to be able to work with while handling that data, so that I have quick access to that information when I would otherwise have to rummage through references to it in other parts of my code & through my curl
history entries of when I’d received it.
Beyond that, it strikes me that, in a language where function signatures can be crafted to raise exceptions when their arguments fall outside of strictly pattern-matched, any such circumstance where a finite set of inputs constitute the only success-path arguments to a function (or where said inputs exhibit a specialized workflow in contrast to generic, but still valid, arguments) are worth representing as such inside of the corresponding type definitions and documentation. To wit, the type definition for a hypothetical IP4.address
or Color.rgba
function would benefit immensely from its signature being expressible as {0..255, 0..255, 0..255, 0..255} :: Ip4.Address.t | Color.Swatch.t
than from it only being possible to sat that the function receives arguments of {non_neg_integer, non_neg_integer, non_neg_integer, non_neg_integer}
… and forgive my saying, but I sincerely doubt atoms would carry more utility in that situation.
*with utmost respect to the incomparable Erlang HTTP server, cowboy