Error when writing functions with multiple arity

I’m following this tutorial on authenticating a user when they log in/out, in phoenix. It requires that I create in my Guardian implementation module the authenticate function with different arities, corresponding to different scenarios. This is the part that is failing:

 def authenticate(%User{} = user, password) do
    authenticate(
      user,
      password,
      Argon2.verify_pass(password, user.encrypted_password)
    )
  end

  defp authenticate(nil, password) do
    authenticate(nil, password, Argon2.no_user_verify())
  end

  defp authenticate(user, _password, true) do
    {:ok, user}
  end

  defp authenticate(_account, _password, false) do
    {:error, :invalid_credentials}
  end

This fails with the following error:

** (CompileError) lib/myapp_web/authentication.ex:44: defp authenticate/2 already defined as def in lib/myapp_web/authentication.ex:36
    lib/myapp_web/authentication.ex:44: (module)

I am using elixir 1.14.1, Erlang 25, with phoenix 1.6, guardian 2.3, ueberauth 0.10.3

Could it be because the first clause uses def and defines a public function authenticate /2 while the next 2 clauses use defp and define a private function authenticate/2? You are basically trying to define 2 functions with the same and arity which is illegal.

5 Likes

I think the problem is the ‘defp’. Try this:

  def authenticate(%User{} = user, password) do
    ...

  def authenticate(nil, password) do
    ...
4 Likes

I found the explantion of the problem in this article:
Learn With Me: Elixir - Functions and Pattern Matching (#23) Chapter “Public and Private Clauses” by Kevin Peter

The article says:

Functions in the same module, with the same name and same arity may have multiple versions of the same function that differ in the pattern of parameters. These versions must all be either ‘def’ or ‘defp’ but cannot mix. Either all clauses of a function need to be public or they all need to be private.

2 Likes

Thank you @agreif @rvirding that helped

Why is that, what is the reason for that ( that we cannot mix) ?

Multiple function heads with the same arity are technically the same function. In fact they get rewritten to a case under the hood. So it wouldn’t make sense for a function to be both private and public.

1 Like

Just to be picky but saying that there are different versions is really weird and a bit bonkers. There is only 1 function with multiple clauses and not different versions because you can’t have multiple versions of a function. Having the multiple def does not really make it clearer.

2 Likes