Do you ever wish Elixir had more types?
Share some of the ones that you frequently use.
If the outcome is good I will compile them into a library.
Here are some:
@typedoc """
A non-positive integer.
That is, any integer `<= 0`.
"""
@type non_pos_integer :: 0 | neg_integer()
@typedoc """
A keyword list with `key` specified.
For example: `keyword(version :: atom(), map())`
"""
@type keyword(key, value) :: list({key, value})
@typedoc """
A non-empty keyword list.
"""
@type nonempty_keyword(value) :: nonempty_list({atom(), value})
@typedoc """
A non-empty keyword list with `key` specified.
For example: `nonempty_keyword(version :: atom(), map())`
"""
@type nonempty_keyword(key, value) :: nonempty_list({key, value})
8 Likes
We can probably get syntactic sugar for string-keyed and atom-keyed maps? Technically these:
%{String.t() => term()}
%{atom() => term()}
…should be enough, but I do wonder whether having builtin syntax won’t help further adoption. Something like e.g. string_map()
and atom_map()
.
Maybe some imitation of sum types as well? So instead of this:
@type pixel_value :: :red | :blue | :green
…we can have this:
@type pixel_value :: Enum(red | blue | green)
That would be just some syntactic sugar.
But I don’t think the community will agree on alternative syntaxes for things you already can express. I am mostly thinking out loud here.
1 Like
Hi @dimitrarvp , thanks for chiming in. Note that the intention of the post is not to make a proposal to the language. Just dream and list types that you find useful in your day-to-day development.
2 Likes
In that case I’ll stand by by my suggestion of having convenient and accepted aliases for string-keyed and atom-keyed maps. While you can easily express them as shown above, IMO it will pay off to have something more intuitively named.
1 Like
Yes, I was also writing some aliases for the empty types.
There is a divergence between pattern matching and type specs, for example, the type %{}
… is that a synonym for map()
or for an empty map?
some for <<>>
, so i created
@type empty_bitstring :: <<>>
@type empty_binary :: <<>>
@type empty_map :: %{}
1 Like
This is my wishlist:
String literals:
@type my_string::"foo"
List literals:
@type my_list::[:foo, :bar]
Min-length tuples
@type my_tuple_at_least_two :: {any, any, ...}
5 Likes
I am surprised that literals are not accepted!
Well, I have put up this little library,
The usage is quite simple.
defmodule Foo do
use ExtendedTypes, all?: true
@spec sample :: nonempty_keyword()
def sample(), do: [a: 1]
end
defmodule Bar do
use ExtendedTypes, only: [string_map: 0, atom_map: 0]
@spec my_map(atom) :: atom_map()
@spec my_map(String.t) :: string_map()
def my_map(key) when is_atom(key), do: %{a: 1}
def my_map(key) when is_binary(key), do: %{"a" => 1}
end
If you run mix docs
you can see all the supported types under the ExtededTypes.Types
module.
Please let me know if you think it could be improved in any way.
Thank you.
2 Likes
ityonemo:
String literals:
@type my_string::"foo"
Where would a type like this be useful?
dorgan
January 12, 2022, 3:54pm
10
Liveview handle_event
callback is the first to come to mind
4 Likes
Anything json related too
1 Like
Lists are strictly [list_type | final_type] and there’s no additional granularity available.
There’s also some tricky “bugs”? in the spec, like nonempty improper lists are usually tagged as any for the final type, which technically includes the empty list, which is “not correct”
1 Like
ityonemo:
There’s also some tricky “bugs”? in the spec, like nonempty improper lists are usually tagged as any for the final type, which technically includes the empty list, which is “not correct”
That is precisely what I am trying to tackle with the library i just published,
Look at the types t:all/0
and t:all_but_empty_list/0
(commented out)
https://github.com/eksperimental/extended_types/blob/53a86539296796ad7843ba4f8de641b2b93ec52c/lib/extended_types/types.ex#L116-L159
I am thinking of creating a function/macro that can generate custom types where certain types are excluded from any()
.
Edit: It is covered in these lines:
@type any_but_list :: ...
https://github.com/eksperimental/extended_types/blob/07594b8acf45b78112e08ad56c6ad867e8f0b675/lib/extended_types/types.ex#L146-L156
@type improper_list :: nonempty_improper_list(any, any_but_list)
https://github.com/eksperimental/extended_types/blob/07594b8acf45b78112e08ad56c6ad867e8f0b675/lib/extended_types/types.ex#L192-L197
I think it will be a good addition to the Typespecs page, to list all these caveats