I’m not sure this is the right section to ask this question, because it’s mostly stylistic, but I’d like it if more experienced elixirists (alchemists? which one is it) could give their personal opinion.
I have a store (just a map, actually) that stores user credentials. Credentials are just strings (String.t).
I have some functions that take credentials as arguments or return credentials.
Should I define something like
@type credential :: String.t
This makes type signatures clearer, but obscures the fact that credentials are strings.
If this were Haskell, I’d define a newtype or even a datatype for credentials, and it would protect me from dumb errors, but I worry that in elixir defining a new type might obscure things without any practical benefits for someone who calls the function.
While this might be true, I would argue that it’s good for documentation. A function that operates on credential should be declared as such and not for arbitrary strings (even when you have no ability to actually restrict it via type only, compared to, for example, Haskell’s type constructors).
The obscurity of it is valid concern, but can be solved if the editor can somehow expand type definitions (Atom + atom-elixir at least can be told to Show Elixir Docs, which includes type defs).
When you decide later on, that credentials are so much more than just a plain String.t, you can easily change a single @type credential to whatever you came up with now, no need to walk your complete codebase and decide per case if you need to change this String.t to the new enhenced type or if it is just a string.
A very good example might be String.t, String.codepoint, and String.grapheme itself:
I’m a newb as well but my inclination is to put off declaring types until the thrashing has settled. During the early stages of a project or feature or whatever, it just makes for more accounting. Once the dust has settled, and other humans might have to start consuming documentation is when I’d add them. At least that’s my approach right now.
In something like Haskell the types are part of the program. In Elixir typespecs are linter help and documentation. (At least that’s my impression)
That may actually mean that you have already decided not to declare types at all - I understand that your are trying to cut down on the cognitive overload right now - but the earlier you start “thinking about/in types” the more consistent your function signatures will be.
For example, when I was working through “React Up And Running”, Flow wasn’t introduced until Chapter 7 where most of the sample application was already firmly established - introducing type checking at that point can only be described as “infuriatingly disruptive” - the experience would have been a lot smoother if type checking was introduced from the start.
I disagree with that. I mean, I understand the logic behind the statement but I’ve yet to see that born out in my experience. Often the admonition to contemplate type signatures before writing functions ends up being an exercise in premature design. Much like writing documentation for a function call before writing the function call itself.
Everybody is different, for sure. I often find myself doing a lot of exploratory coding before I settle into something sane. It’s not so much the type checking that puts a spanner in the works there, but the accounting involved in updating the declarations. That’s just my experience with this language so far in the last several months, so I may find myself coming around to another way of looking at things.
I personally use @type when I want to use @typedoc. If I have a String.t that isn’t immediately obvious what the purpose is or needs a special description, then it gets a @type.
Meaning that if the credential type was something like username@password then I would have a @type and an @typedoc for that type. In this case I don’t find the name credential descriptive enough to figure out what it is, so it gets a @type and @typedoc.
However, if I have something like username type. I would probably just do: