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.

1 Like

Thank you @agreif @rvirding that helped