Specs for overloaded function

As I couldn’t find a good example in the docs, Elixir and Ecto repository, I’m going to post the question here. I wasn’t able to find the error documented somewhere either.

I’m trying to write specs for a set of functions that serve one goal: calculating a cumulative moving average.

The code for generating the CMA looks like the following:

  def cumulative([]) do
    nil
  end

  def cumulative(list)
      when is_list(list) do
    cumulative(0, 0, list)
  end

  def cumulative({weight, avg}, list) 
      when is_integer(weight) and is_list(list) do
    cumulative(weight, avg, list)
  end

  def cumulative(0, _, []) do
    nil
  end

  def cumulative(weight, average, []) do
    {weight, average}
  end

  def cumulative(weight, average, [hd | tl], results \\ [])
      when is_integer(weight) and is_number(hd) do
    new_weight = weight + 1
    new_avg = (average * weight + hd) / new_weight

    case tl do
      [_ | _] ->
        cumulative(new_weight, new_avg, tl, results ++ [new_avg])
      [] ->
        {new_weight, results ++ [new_avg]}
    end
  end

After trying to add specs above the functions:

  @spec cumulative(list) :: {integer,  float}
        when list: [float] | [integer]
  @spec cumulative(tuple, list) :: {integer,  float}
        when tuple: {integer, integer | float},
             list: [float] | [integer]

I get the following error:

type variable tuple is only used once (is unbound)

I must be doing something wrong. How do I add specs correctly?

I think this is quite a misleading error message, because of tuple/0 beeing a predefined type, after I have switched it by t, it worked without an error message for me.

You should avoid list as well, it is a built-in type as well. In generall, try to avoid those basic types of erlang and elixir as names of own types or variables in a spec.

1 Like