Elixir: Odd typespec behaviour

Created on 30 Jun 2020  路  8Comments  路  Source: elixir-lang/elixir

Environment

  • Elixir & Erlang/OTP versions (elixir --version): Erlang/OTP 23 [erts-11.0] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]

Elixir 1.10.3 (compiled with Erlang/OTP 21)

  • Operating system: Linux Manjaro x86-64

Current behavior

Given the following module:

defmodule Example do
  @type tuple(_param) :: any()
  @type example :: tuple(3)
end

when checking the type example:

iex> t Example.example
@type example() :: {3}

Expected behavior

iex> t Example.example
@type example() :: any()

or possibly

iex> t Example.example
@type example() :: tuple(3)
Elixir Bug

All 8 comments

or possibly

iex> t Example.example
@type example() :: tuple(3)

Correct. This is what it should return tuple(3). Look at the example below

For the record: these are the types that are failing.
The code is shared here: https://gist.github.com/eksperimental/55f1e207ab5878a668668546f57a3f90
It lists all the types defined in the Typespecs page, and 4 of them are giving unexpected results.

defmodule TypespecExample do
    @type map(key) :: [key]
    @type tuple(key) :: [key]
    @type list(key, key) :: [key]
    @type nonempty_list(key, key) :: [key]

    @type example_map() :: map(3)
    @type example_tuple() :: tuple(3)
    @type example_list(key, key) :: list(3)
    @type example_nonempty_list(key, key) :: nonempty_list(3)
end
iex(4)> t Elixir.TypespecExample.example_map/0
** (FunctionClauseError) no function clause matching in anonymous fn/1 in Code.Typespec.typespec_to_quoted/1    

    The following arguments were given to anonymous fn/1 in Code.Typespec.typespec_to_quoted/1:

        # 1
        {:integer, 0, 3}

    (elixir 1.10.3) lib/code/typespec.ex:261: anonymous fn/1 in Code.Typespec.typespec_to_quoted/1
    (elixir 1.10.3) lib/enum.ex:1396: Enum."-map/2-lists^map/1-0-"/2
    (elixir 1.10.3) lib/code/typespec.ex:261: Code.Typespec.typespec_to_quoted/1
    (elixir 1.10.3) lib/code/typespec.ex:76: Code.Typespec.type_to_quoted/1
    (iex 1.10.3) lib/iex/introspection.ex:731: IEx.Introspection.format_type/1
    (iex 1.10.3) lib/iex/introspection.ex:719: IEx.Introspection.type_doc/4
    (iex 1.10.3) lib/iex/introspection.ex:703: IEx.Introspection.t/1

iex(7)> t Elixir.TypespecExample.example_tuple/0
@type example_tuple() :: {3}

iex(13)> t Elixir.TypespecExample.example_list/2
@type example_list(key, key) :: [3]

iex(14)> t Elixir.TypespecExample.example_nonempty_list/2
@type example_nonempty_list(key, key) :: [3, ...]


Wow, thank you for such a thorough investigation!

A further observation: Wouldn't types like @type example_list(key, key) :: list(3) be expected to raise a "type variable key is unused" CompileError?
Maybe there is an additional problem related to type-variable hygiene, or maybe it is another symptom of the same problem.

For completion's sake, I have tried it with remote types as well.

defmodule Other do
  @type tuple(_int) :: any()
end

defmodule Example do
  @type example :: Other.tuple(3)
end

In this case, the definition of Example.example remains Other.tuple(3) as expected.

This was already fixed on master thanks to 70da7098817c3fb8c5ff87142a829c7518372329. I have pushed more tests.

@josevalim list and nonempty_list are still not working as expected.

iex(15)> t Elixir.TypespecExample.example_list/2
@type example_list(key, key) :: [3]

iex(16)> t Elixir.TypespecExample.example_nonempty_list/2
@type example_nonempty_list(key, key) :: [3, ...]

You are calling list(3) and list(3) is the same as [3]. The same for nonempty_list.

You are calling list(3) and list(3) is the same as [3]. The same for nonempty_list.
correct.
All is working as expected.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jakubpawlowicz picture jakubpawlowicz  路  32Comments

josevalim picture josevalim  路  27Comments

josevalim picture josevalim  路  33Comments

josevalim picture josevalim  路  36Comments

pragdave picture pragdave  路  28Comments