Is there an idiomatic way in Elixir to write a case guard to pattern match on the value of a key inside a Keyword List?
The keyword list has an arbitrary number of keywords, so matching agains the entire list is not an option.
The best we can come up with is a nested case statement to match against Keyword.get, is there a cleaner way?
# Contrived example for discussion purposes.
def run_something(args) do
# calculate_something returns:
# {:ok, response}
# {error, [message: string, code: atom, ...] }
case calculate_something(args) do
{:ok, response} ->
response
# How do you perform such a pattern match?
{:error, reason} when Keyword.get(reason, code:) == :timeout ->
# Maybe try again?
{:error, reason} when Keyword.get(reason, code:) == :auth_failed ->
# Maybe try to re-authenticate?
{:error, reason } ->
# Unknown reason, abort.
end
end
Keyword list keys can be duplicated, and they can be at an arbitrary position as well, so the answer to your direct question is “no”. You can pattern match e.g. [{:desired_key, _value} | _rest] but that will only work if :desired_key is the key of the first element in a keyword list and in no other conditions.
I don’t view your code as boilerplate-y or clunky but if there is a chance that those error clauses can explode you can indeed just make a helper function i.e.
case stuff do
{:ok, value} -> value
{:error, anything} -> act_on_error(anything)
end
…and then use the Keyword module functions inside the helper function to manipulate the embedded error value further.
Note too that you can’t invoke arbitrary functions in a guard. Guards need a constant time execution guarantee and therefore only a small set of functions are permitted.
If you really need structured results, consider using a map rather than a keyword list. Map keys can be used in guard clauses.
We’re calling into a third-party library, [Joken](https://hexdocs.pm/joken/Joken.html#t:error_reason/0), that returns the keyword list.
Our use case is decoding JWT tokens. If the token fails to validate, then there are certain conditions we can handle. (Example: the token has expired and we can refresh it.) Depending on the token we’re dealing with, we might handle things a bit differently, so we don’t have a single “decode token and handle all failures” codepath, yet.
In which case I would have calculate_something(args) return a {:ok, code, result} or {:error, code, result} tuple so you can directly match on the code.
Now that is a syntax we would not have considered as new Elixir developers. I guess this works because a keyword list is just a list of 2-item tuples. TIL, thanks!
Yep! I’m repeating myself from another recent post but it’s just a list with some very specific contents and nothing more! All the functions in List, Enum, and Stream work exactly as they would on any other list.
If we can’t pattern match on keyword list, we are going to replace it with maps? why many 3rd party libs especially http dependent ones still return headers in keyword list.
but ast of map also looks similar to Keyword list
quote do: %{a: 5}
{:%{}, [], [a: 5]}
whats the d/f?
is it the map is what it says that is kV store, with o1 access or its internally list only?
Because the order of HTTP headers can matter if some have the same name. This encompasses two important qualities of keyword lists! You can probably think of other examples of where order matters. As for duplicate keys, these are used in Elixir’s import: import String, only: [split: 1, split: 2] as well as Ecto: from q in query, where: q.a == 1, where q.b = 1. Maps have efficient look up but they don’t maintain their order and of course cannot have duplicate keys.