I don't think most features should be backported as it increases maintenance burden (on features themselves but also more minor stuff like @doc since). I definitely agree about the usefulness though. I tend to vendor-in the new functionality and at compile-time pick between the vendored and upstream implementation. For Keyword.validate/2 it would be:
# TODO: use Keyword.validate when we require Elixir 1.13 if Version.compare(System.version(), ">= 1.13.0") do defp keyword_validate(keyword, values) do Keyword.validate(keyword, values) end else defp keyword_validate(keyword, values) when is_list(keyword) and is_list(values) do validate(keyword, values, [], [], []) end defp validate([{key, _} = pair | keyword], values1, values2, acc, bad_keys) when is_atom(key) do case find_key!(key, values1, values2) do {values1, values2} -> validate(keyword, values1, values2, [pair | acc], bad_keys) :error -> case find_key!(key, values2, values1) do {values1, values2} -> validate(keyword, values1, values2, [pair | acc], bad_keys) :error -> validate(keyword, values1, values2, acc, [key | bad_keys]) end end end defp validate([], values1, values2, acc, []) do {:ok, move_pairs!(values1, move_pairs!(values2, acc))} end defp validate([], _values1, _values2, _acc, bad_keys) do {:error, bad_keys} end defp validate([pair | _], _values1, _values2, _acc, []) do raise ArgumentError, "expected a keyword list as first argument, got invalid entry: #{inspect(pair)}" end defp find_key!(key, [key | rest], acc), do: {rest, acc} defp find_key!(key, [{key, _} | rest], acc), do: {rest, acc} defp find_key!(key, [head | tail], acc), do: find_key!(key, tail, [head | acc]) defp find_key!(_key, [], _acc), do: :error defp move_pairs!([key | rest], acc) when is_atom(key), do: move_pairs!(rest, acc) defp move_pairs!([{key, _} = pair | rest], acc) when is_atom(key), do: move_pairs!(rest, [pair | acc]) defp move_pairs!([], acc), do: acc defp move_pairs!([other | _], _) do raise ArgumentError, "expected the second argument to be a list of atoms or tuples, got: #{inspect(other)}" end defp keyword_validate!(keyword, values) do case keyword_validate(keyword, values) do {:ok, kw} -> kw {:error, invalid_keys} -> keys = for value <- values, do: if(is_atom(value), do: value, else: elem(value, 0)) raise ArgumentError, "unknown keys #{inspect(invalid_keys)} in #{inspect(keyword)}, the allowed keys are: #{inspect(keys)}" end end end On December 14, 2021, "chrobot.io" <ste...@chrobot.io> wrote: > Hi, I'd like to follow up on this since Keyword.validate has landed in > 1.13. Is there a will to use the validation in common libraries? > > Just recently I've been hit by misspelling "redact: true" with > "redacted: true" when defining a field in Ecto. Sounds like a perfect > use case for Keyword.validate. Ideally this would explode, but that's > a breaking change, so emitting a warning would probably make more > sense. > > The other thing is that most libraries tend to stay on the oldest > Elixir version possible, so they can't just call Keyword.validate > (unless I'm missing something). In our case we're running on Elixir > 1.10, but we backport small bits because of their usefulness (so for > example we have Future.Keyword.validate and Future.tap). Is something > like that a viable way to do this in libraries? I guess the other > option is conditional compilation, but it sounds like a code that's > going to stay there for a very long time (what would be the incentive > to bump the minimum version as high as 1.13?). > > Looking forward to your thoughts on this. > > Best, > Stefan > > wtorek, 27 lipca 2021 o 10:02:38 UTC+2 polva...@gmail.com napisał(a): > > The PR has been sent! Link: <https://github.com/elixir- > > lang/elixir/pull/11149> > > > > Em segunda-feira, 26 de julho de 2021 às 15:07:18 UTC-3, José Valim > > escreveu: > > > In this case, I would call Keyword.validate! And then call > > > Map.new. I don’t see a benefit in the generic API as it won’t be > > > faster and not necessarily clearer either. So I would stick with > > > my last proposal. :) > > > > > > On Mon, Jul 26, 2021 at 20:01 Paulo Valente <polva...@gmail.com> > > > wrote: > > > > I was considering making it only accept keywords, and return > > > > keywords as well. > > > > > > > > However, I can see benefits in accepting Enumerables. What do > > > > you think about using another verb such as cast or coerce, > > > > instead of validate, for this typing > > > > (e.g. @spec Keyword.cast!(Enumerable.t(), keyword) :: keyword | > > > > no_return and @spec Map.cast!(Enumerable.t(), keyword) :: map | > > > > no_return)? > > > > > > > > Should I also implement it for Map as well, or maybe add it in a > > > > second PR? > > > > > > > > Thanks for the idea on Enumerables! > > > > Em segunda-feira, 26 de julho de 2021 às 12:47:31 UTC-3, Wojtek > > > > Mach escreveu: > > > > > Keyword.validate will accept just a keyword, right? Did you > > > > > consider making it accept any enumerable of pairs, just like > > > > > Keyword.new does? Same for Map. > > > > > > > > > > I think one particular scenario is something like this: > > > > > > > > > > def foo(opts) when is_list(opts) do > > > > > config = Map.validate!(opts, …) > > > > > config.name <http://config.name> # the assertive map.key > > > > > is really convenient here > > > > > > > > > > what do you think? The only concern, besides feature creep :), > > > > > is perhaps validate is not the best name given it accepts > > > > > broader set of inputs. But then again, Keyword.keyword and in > > > > > particular Map.map are maybe a bit awkward. > > > > > > > > > > On July 26, 2021, josevalim <jose....@gmail.com> wrote: > > > > > > I debated this with the Elixir team and we agreed on the > > > > > > following API: > > > > > > Keyword.validate! :: keyword | no_return > > > > > > Keyword.validate :: {:ok, keyword} | {:error, unknown_keys, > > > > > > missing_keys} > > > > > > The same functionality would have to be defined for Map. > > > > > > The API for Keyword.validate! and Map.validate! would be the > > > > > > exact same as the one found in Nx.Defn.Kernel.keyword!. > > > > > > Could you please send a PR? Or open up an issue if you can't > > > > > > submit it at the moment. > > > > > > > > > > > Thanks!On Monday, July 12, 2021 at 12:46:38 PM UTC+2 > > > > > > polva...@gmail.com wrote: > > > > > > > > > > > > I would propose to add this to the Kernel module, to > > > > > > mirror the struct! helper. But adding to Keyword also works. > > > > > > Thoughts? > > > > > > > > > > > > Makes sense! I suggested Keyword mostly because there's the > > > > > > module already, but since we have struct! in Kernel, it > > > > > > might be better to have keyword! there too. Usage would also > > > > > > be nicer this way. > > > > > > > > > > > > > Should the bang version really raise for redundant > > > > > > options? > > > > > > > > > > > > As José said, it would help clean out stray options being > > > > > > passed. I can think of a few cases in which I thought one > > > > > > option was being used, but had either a typo or wrong name > > > > > > altogether (think "end" vs "end_datetime" sort of > > > > > > situation). > > > > > > However, I can see a less strict version being useful as > > > > > > well. Perhaps there could be a keyword!/3 which accepted > > > > > > options like: keyword!(opts, [extra_keys: true], key1: > > > > > > :default1, key2: :default2) > > > > > > > > > > > > > > > > > > > > > > > -- > > > > > > You received this message because you are subscribed to the > > > > > > Google Groups "elixir-lang-core" group. > > > > > > To unsubscribe from this group and stop receiving emails > > > > > > from it, send an email to elixir-lang- > > > > > > co...@googlegroups.com. > > > > > > > > > > > To view this discussion on the web visit > > > > > > https://groups.google.com/d/msgid/elixir-lang-core/9b83a803- > > > > > > c2c1-49e7-a531-56791607bcaan%40googlegroups.com > > > > > > <https://groups.google.com/d/msgid/elixir-lang- > > > > > > core/9b83a803-c2c1-49e7-a531- > > > > > > 56791607bcaan%40googlegroups.com?utm_medium=email&utm_source=footer>. > > > > > > > > > > > > > > > > > > > > > -- > > > > You received this message because you are subscribed to the > > > > Google Groups "elixir-lang-core" group. > > > > To unsubscribe from this group and stop receiving emails from > > > > it, send an email to elixir-lang-co...@googlegroups.com. > > > > > > > To view this discussion on the web visit > > > > https://groups.google.com/d/msgid/elixir-lang-core/a96fb19a- > > > > 8148-41dc-8b45-0e2b9fe755b8n%40googlegroups.com > > > > <https://groups.google.com/d/msgid/elixir-lang-core/a96fb19a- > > > > 8148-41dc-8b45- > > > > 0e2b9fe755b8n%40googlegroups.com?utm_medium=email&utm_source=footer>. > > > > > > > > -- > You received this message because you are subscribed to the Google > Groups "elixir-lang-core" group. > To unsubscribe from this group and stop receiving emails from it, > send an email to elixir-lang-core+unsubscr...@googlegroups.com > <mailto:elixir-lang-core+unsubscr...@googlegroups.com>. > To view this discussion on the web visit > https://groups.google.com/d/msgid/elixir-lang-core/43f206e4-6b22-4622- > b8d9-1ade01556039n%40googlegroups.com > <https://groups.google.com/d/msgid/elixir-lang-core/43f206e4-6b22- > 4622-b8d9- > 1ade01556039n%40googlegroups.com?utm_medium=email&utm_source=footer>. -- You received this message because you are subscribed to the Google Groups "elixir-lang-core" group. To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-core+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/b8beea46f50e401bf00961fff49a76f68c360bb3%40hey.com.