Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crash conforming keyword list containing function value #93

Open
garthk opened this issue Aug 27, 2020 · 0 comments
Open

Crash conforming keyword list containing function value #93

garthk opened this issue Aug 27, 2020 · 0 comments

Comments

@garthk
Copy link
Contributor

garthk commented Aug 27, 2020

import Norm

key_opt_spec = {:keys, coll_of(one_of(~w"a b c"a))}
fun_opt_spec = {:fun, spec(is_function(1))}
opts_spec = coll_of(one_of([key_opt_spec, fun_opt_spec]))

conform!([{:fun, fn _ -> "auto" end}], opts_spec)

crashes with:

** (Protocol.UndefinedError) protocol Enumerable not implemented for #Function<44.97283095/1 in :erl_eval.expr/5> of type Function, only anonymous functions of arity 2 are enumerable. This protocol is implemented for the following type(s): Ecto.Adapters.SQL.Stream, Postgrex.Stream, DBConnection.Stream, DBConnection.PrepareStream, Timex.Interval, Matrex, StreamData, HashSet, Range, Map, Function, List, Stream, Date.Range, HashDict, GenEvent.Stream, MapSet, File.Stream, IO.Stream
    (elixir 1.10.4) lib/enum.ex:3731: Enumerable.Function.reduce/3
    (elixir 1.10.4) lib/enum.ex:605: Enum.count/1
    (norm 0.12.0) lib/norm/core/collection.ex:57: Norm.Conformer.Conformable.Norm.Core.Collection.check_counts/3
    (norm 0.12.0) lib/norm/core/collection.ex:18: Norm.Conformer.Conformable.Norm.Core.Collection.conform/3
    (elixir 1.10.4) lib/enum.ex:1396: Enum."-map/2-lists^map/1-0-"/2
    (elixir 1.10.4) lib/enum.ex:1396: Enum."-map/2-lists^map/1-0-"/2
    (norm 0.12.0) lib/norm/conformer.ex:99: Norm.Conformer.Conformable.Tuple.conform/3
    (elixir 1.10.4) lib/enum.ex:1396: Enum."-map/2-lists^map/1-0-"/2
    (norm 0.12.0) lib/norm/core/any_of.ex:18: Norm.Conformer.Conformable.Norm.Core.AnyOf.conform/3
    (elixir 1.10.4) lib/enum.ex:1396: Enum."-map/2-lists^map/1-0-"/2
    (norm 0.12.0) lib/norm/core/collection.ex:22: Norm.Conformer.Conformable.Norm.Core.Collection.conform/3
    (norm 0.12.0) lib/norm.ex:62: Norm.conform!/2

Enumerable.impl_for/1 for (fn _ -> "auto" end) returns Enumerable.Function, which is good enough for your check_enumerable/3 in norm/lib/norm/core/collection.ex, but when you try to actually enumerate it it'll blow up in your face:

    defp check_enumerable(input, path, _opts) do
      if Enumerable.impl_for(input) == nil do
        {:error, [Conformer.error(path, input, "not enumerable")]}
      else
        :ok
      end
    end
defimpl Enumerable, for: Function do
  def count(_function), do: {:error, __MODULE__}
  def member?(_function, _value), do: {:error, __MODULE__}
  def slice(_function), do: {:error, __MODULE__}

  def reduce(function, acc, fun) when is_function(function, 2), do: function.(acc, fun)

  def reduce(function, _acc, _fun) do
    raise Protocol.UndefinedError,
      protocol: @protocol,
      value: function,
      description: "only anonymous functions of arity 2 are enumerable"
  end
end

Further up the stack, we're trying to enumerate the function because in lib/norm/conformer.ex:99 you're trying to do this:

Conformable.conform(spec, elem(input, i), path ++ [I])

… which, if you expand the arguments, is:

Norm.Conformer.Conformable.conform(
  #Norm.CollOf<#Norm.OneOf<[:a, :b, :c]>>,
  #Function<44.97283095/1 in :erl_eval.expr/5>,
  [0, 1]
)

I lack the time to figure out why you're trying to conform the :fun to the spec of the :key. I'm going to have to just rip the @contract off for now, as it's preventing routine maintenance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant