Skip to content

Commit f1d4d5b

Browse files
author
José Valim
committed
Add pages about typespecs and docs, closes #4079
1 parent e6a31af commit f1d4d5b

File tree

5 files changed

+276
-184
lines changed

5 files changed

+276
-184
lines changed

Makefile

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -128,14 +128,14 @@ clean_exbeam:
128128

129129
LOGO_PATH = $(shell test -f ../docs/logo.png && echo "--logo ../docs/logo.png")
130130
SOURCE_REF = $(shell head="$$(git rev-parse HEAD)" tag="$$(git tag --points-at $$head | tail -1)" ; echo "$${tag:-$$head}\c")
131-
COMPILE_DOCS = bin/elixir ../ex_doc/bin/ex_doc "$(1)" "$(VERSION)" "lib/$(2)/ebin" -m "$(3)" -u "https://github.com/elixir-lang/elixir" --source-ref "$(call SOURCE_REF)" $(call LOGO_PATH) -o doc/$(2) -p http://elixir-lang.org/docs.html
131+
COMPILE_DOCS = bin/elixir ../ex_doc/bin/ex_doc "$(1)" "$(VERSION)" "lib/$(2)/ebin" -m "$(3)" -u "https://github.com/elixir-lang/elixir" --source-ref "$(call SOURCE_REF)" $(call LOGO_PATH) -o doc/$(2) -p http://elixir-lang.org/docs.html $(4)
132132

133133
docs: compile ../ex_doc/bin/ex_doc docs_elixir docs_eex docs_mix docs_iex docs_ex_unit docs_logger
134134

135135
docs_elixir: compile ../ex_doc/bin/ex_doc
136136
@ echo "==> ex_doc (elixir)"
137137
$(Q) rm -rf doc/elixir
138-
$(call COMPILE_DOCS,Elixir,elixir,Kernel)
138+
$(call COMPILE_DOCS,Elixir,elixir,Kernel,-e "lib/elixir/pages/Typespecs.md" -e "lib/elixir/pages/Writing Documentation.md")
139139

140140
docs_eex: compile ../ex_doc/bin/ex_doc
141141
@ echo "==> ex_doc (eex)"
@@ -186,16 +186,6 @@ publish_docs: docs
186186
rm -rf ../docs/$(DOCS)/*/
187187
cp -R doc/* ../docs/$(DOCS)
188188

189-
# This task requires aws-cli to be installed and set up for access to s3.hex.pm
190-
# See: http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-set-up.html
191-
192-
publish_mix: compile
193-
cd lib/mix && MIX_ENV=prod mix escript.build
194-
aws s3 cp lib/mix/mix s3://s3.hex.pm/builds/mix/v$(VERSION)/mix --acl public-read
195-
aws s3 cp lib/mix/mix s3://s3.hex.pm/builds/mix/mix --acl public-read
196-
rm lib/mix/mix
197-
rm -rf lib/mix/_build
198-
199189
#==> Tests tasks
200190

201191
test: test_erlang test_elixir

lib/elixir/lib/kernel/typespec.ex

Lines changed: 1 addition & 170 deletions
Original file line numberDiff line numberDiff line change
@@ -1,174 +1,5 @@
11
defmodule Kernel.Typespec do
2-
@moduledoc ~S"""
3-
Provides macros and functions for working with typespecs.
4-
5-
Elixir comes with a notation for declaring types and specifications. Elixir is
6-
dynamically typed, and as such, typespecs are never used by the compiler to
7-
optimize or modify code. Still, using typespecs is useful as documentation and
8-
tools such as [Dialyzer](http://www.erlang.org/doc/man/dialyzer.html) can
9-
analyze code with typespecs to find bugs.
10-
11-
The attributes `@type`, `@opaque`, `@typep`, `@spec`, `@callback` and
12-
`@macrocallback` available in modules are handled by the equivalent macros
13-
defined by this module. See sub-sections "Defining a type" and "Defining a
14-
specification" below.
15-
16-
## Types and their syntax
17-
18-
The type syntax provided by Elixir is fairly similar to [the one in
19-
Erlang](http://www.erlang.org/doc/reference_manual/typespec.html).
20-
21-
Most of the built-in types provided in Erlang (for example, `pid()`) are
22-
expressed the same way: `pid()` or simply `pid`. Parameterized types are also
23-
supported (`list(integer)`) and so are remote types (`Enum.t`).
24-
25-
Integers and atom literals are allowed as types (ex. `1`, `:atom` or
26-
`false`). All other types are built of unions of predefined types. Certain
27-
shorthands are allowed, such as `[...]`, `<<>>` and `{...}`.
28-
29-
### Basic types
30-
31-
type :: any() # the top type, the set of all terms
32-
| none() # the bottom type, contains no terms
33-
| pid()
34-
| port()
35-
| reference()
36-
| tuple()
37-
| atom()
38-
| integer()
39-
| non_neg_integer() # 0, 1, 2, 3, ...
40-
| pos_integer() # 1, 2, 3, ...
41-
| neg_integer() # ..., -3, -2, -1
42-
| float()
43-
| map()
44-
| struct()
45-
| list(type)
46-
| nonempty_list(type)
47-
| improper_list(type1, type2)
48-
| maybe_improper_list(type1, type2)
49-
| Literals # Described in section "Literals"
50-
| Builtin # Described in section "Builtin-types"
51-
| Remotes # Described in section "Remotes"
52-
53-
### Literals
54-
55-
The following literals are also supported in typespecs:
56-
57-
type :: :atom ## Atoms
58-
| 1 ## Integers
59-
| 1..10 ## Integers from 1 to 10
60-
| 1.0 ## Floats
61-
62-
| <<>> ## Bitstrings
63-
| <<_::size>> # size is 0 or a positive integer
64-
| <<_::_ * unit>> # unit is an integer from 1 to 256
65-
| <<_::size * unit>>
66-
67-
| [type] ## Lists
68-
| [] # empty list
69-
| [...] # shorthand for nonempty_list(any())
70-
| [type, ...] # shorthand for nonempty_list(type)
71-
| [key: type] # keyword lists
72-
73-
| (... -> type) ## Functions
74-
| (... -> type) # any arity, returns type
75-
| (() -> type) # 0-arity, returns type
76-
| (type1, type2 -> type) # 2-arity, returns type
77-
78-
| %{} ## Maps
79-
| %{key: type} # map with key :key with value of type
80-
| %{type1 => type2} # map with keys of type1 with values of type2
81-
| %SomeStruct{}
82-
| %SomeStruct{key: type}
83-
84-
| {} ## Tuples
85-
| {:ok, type} # two element tuple with an atom and any type
86-
87-
### Built-in types
88-
89-
These types are also provided by Elixir as shortcuts on top of the
90-
basic and literal types.
91-
92-
Built-in type | Defined as
93-
:---------------------- | :---------
94-
`term()` | `any()`
95-
`binary()` | `<<_::_ * 8>>`
96-
`bitstring()` | `<<_::_ * 1>>`
97-
`boolean()` | `false` \| `true`
98-
`byte()` | `0..255`
99-
`char()` | `0..0x10ffff`
100-
`number()` | `integer()` \| `float()`
101-
`char_list()` | `[char()]`
102-
`list()` | `[any()]`
103-
`maybe_improper_list()` | `maybe_improper_list(any(), any())`
104-
`nonempty_list()` | `nonempty_list(any())`
105-
`iodata()` | `iolist()` \| `binary()`
106-
`iolist()` | `maybe_improper_list(byte()` \| `binary()` \| `iolist(), binary()` \| `[])`
107-
`module()` | `atom()` \| `tuple()`
108-
`arity()` | `0..255`
109-
`mfa()` | `{atom(), atom(), arity()}`
110-
`node()` | `atom()`
111-
`timeout()` | `:infinity` \| `non_neg_integer()`
112-
`no_return()` | `none()`
113-
`fun()` | `(... -> any)`
114-
`struct()` | `%{__struct__: atom()}`
115-
`as_boolean(t)` | `t`
116-
117-
### Remote types
118-
119-
Any module is also able to define its own type and the modules in
120-
Elixir are no exception. For example, a string is `String.t`, a
121-
range is `Range.t`, any enumerable can be `Enum.t` and so on.
122-
123-
## Defining a type
124-
125-
@type type_name :: type
126-
@typep type_name :: type
127-
@opaque type_name :: type
128-
129-
A type defined with `@typep` is private. An opaque type, defined with
130-
`@opaque` is a type where the internal structure of the type will not be
131-
visible, but the type is still public.
132-
133-
Types can be parameterized by defining variables as parameters, these variables
134-
can then be used to define the type.
135-
136-
@type dict(key, value) :: [{key, value}]
137-
138-
## Defining a specification
139-
140-
@spec function_name(type1, type2) :: return_type
141-
@callback function_name(type1, type2) :: return_type
142-
@macrocallback macro_name(type1, type2) :: Macro.t
143-
144-
Callbacks are used to define the callbacks functions of behaviours (see
145-
`Behaviour`).
146-
147-
Guards can be used to restrict type variables given as arguments to the
148-
function.
149-
150-
@spec function(arg) :: [arg] when arg: atom
151-
152-
Type variables with no restriction can also be defined.
153-
154-
@spec function(arg) :: [arg] when arg: var
155-
156-
Specifications can be overloaded just like ordinary functions.
157-
158-
@spec function(integer) :: atom
159-
@spec function(atom) :: integer
160-
161-
## Notes
162-
163-
Elixir discourages the use of type `string` as it might be confused with
164-
binaries which are referred to as "strings" in Elixir (as opposed to character
165-
lists). In order to use the type that is called `string` in Erlang, one has to
166-
use the `char_list` type which is a synonym for `string`. If you use `string`,
167-
you'll get a warning from the compiler.
168-
169-
If you want to refer to the "string" type (the one operated on by functions in
170-
the `String` module), use `String.t` type instead.
171-
"""
2+
@moduledoc false
1723

1734
@doc """
1745
Defines a type.

lib/elixir/pages/Typespecs.md

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
# Typespecs
2+
3+
Elixir comes with a notation for declaring types and specifications. Elixir is
4+
dynamically typed, and as such, typespecs are never used by the compiler to
5+
optimize or modify code. Still, using typespecs is useful as documentation and
6+
tools such as [Dialyzer](http://www.erlang.org/doc/man/dialyzer.html) can
7+
analyze code with typespecs to find bugs.
8+
9+
The attributes `@type`, `@opaque`, `@typep`, `@spec`, `@callback` and
10+
`@macrocallback` available in modules are handled by the equivalent macros
11+
defined by this module. See sub-sections "Defining a type" and "Defining a
12+
specification" below.
13+
14+
## Types and their syntax
15+
16+
The type syntax provided by Elixir is fairly similar to [the one in
17+
Erlang](http://www.erlang.org/doc/reference_manual/typespec.html).
18+
19+
Most of the built-in types provided in Erlang (for example, `pid()`) are
20+
expressed the same way: `pid()` or simply `pid`. Parameterized types are also
21+
supported (`list(integer)`) and so are remote types (`Enum.t`).
22+
23+
Integers and atom literals are allowed as types (ex. `1`, `:atom` or
24+
`false`). All other types are built of unions of predefined types. Certain
25+
shorthands are allowed, such as `[...]`, `<<>>` and `{...}`.
26+
27+
### Basic types
28+
29+
type :: any() # the top type, the set of all terms
30+
| none() # the bottom type, contains no terms
31+
| pid()
32+
| port()
33+
| reference()
34+
| tuple()
35+
| atom()
36+
| integer()
37+
| non_neg_integer() # 0, 1, 2, 3, ...
38+
| pos_integer() # 1, 2, 3, ...
39+
| neg_integer() # ..., -3, -2, -1
40+
| float()
41+
| map()
42+
| struct()
43+
| list(type)
44+
| nonempty_list(type)
45+
| improper_list(type1, type2)
46+
| maybe_improper_list(type1, type2)
47+
| Literals # Described in section "Literals"
48+
| Builtin # Described in section "Builtin-types"
49+
| Remotes # Described in section "Remotes"
50+
51+
### Literals
52+
53+
The following literals are also supported in typespecs:
54+
55+
type :: :atom ## Atoms
56+
| 1 ## Integers
57+
| 1..10 ## Integers from 1 to 10
58+
| 1.0 ## Floats
59+
60+
| <<>> ## Bitstrings
61+
| <<_::size>> # size is 0 or a positive integer
62+
| <<_::_ * unit>> # unit is an integer from 1 to 256
63+
| <<_::size * unit>>
64+
65+
| [type] ## Lists
66+
| [] # empty list
67+
| [...] # shorthand for nonempty_list(any())
68+
| [type, ...] # shorthand for nonempty_list(type)
69+
| [key: type] # keyword lists
70+
71+
| (... -> type) ## Functions
72+
| (... -> type) # any arity, returns type
73+
| (() -> type) # 0-arity, returns type
74+
| (type1, type2 -> type) # 2-arity, returns type
75+
76+
| %{} ## Maps
77+
| %{key: type} # map with key :key with value of type
78+
| %{type1 => type2} # map with keys of type1 with values of type2
79+
| %SomeStruct{}
80+
| %SomeStruct{key: type}
81+
82+
| {} ## Tuples
83+
| {:ok, type} # two element tuple with an atom and any type
84+
85+
### Built-in types
86+
87+
These types are also provided by Elixir as shortcuts on top of the
88+
basic and literal types.
89+
90+
Built-in type | Defined as
91+
:---------------------- | :---------
92+
`term()` | `any()`
93+
`binary()` | `<<_::_ * 8>>`
94+
`bitstring()` | `<<_::_ * 1>>`
95+
`boolean()` | `false` \| `true`
96+
`byte()` | `0..255`
97+
`char()` | `0..0x10ffff`
98+
`number()` | `integer()` \| `float()`
99+
`char_list()` | `[char()]`
100+
`list()` | `[any()]`
101+
`maybe_improper_list()` | `maybe_improper_list(any(), any())`
102+
`nonempty_list()` | `nonempty_list(any())`
103+
`iodata()` | `iolist()` \| `binary()`
104+
`iolist()` | `maybe_improper_list(byte()` \| `binary()` \| `iolist(), binary()` \| `[])`
105+
`module()` | `atom()` \| `tuple()`
106+
`arity()` | `0..255`
107+
`mfa()` | `{atom(), atom(), arity()}`
108+
`node()` | `atom()`
109+
`timeout()` | `:infinity` \| `non_neg_integer()`
110+
`no_return()` | `none()`
111+
`fun()` | `(... -> any)`
112+
`struct()` | `%{__struct__: atom()}`
113+
`as_boolean(t)` | `t`
114+
115+
### Remote types
116+
117+
Any module is also able to define its own type and the modules in
118+
Elixir are no exception. For example, a string is `String.t`, a
119+
range is `Range.t`, any enumerable can be `Enum.t` and so on.
120+
121+
## Defining a type
122+
123+
@type type_name :: type
124+
@typep type_name :: type
125+
@opaque type_name :: type
126+
127+
A type defined with `@typep` is private. An opaque type, defined with
128+
`@opaque` is a type where the internal structure of the type will not be
129+
visible, but the type is still public.
130+
131+
Types can be parameterized by defining variables as parameters, these variables
132+
can then be used to define the type.
133+
134+
@type dict(key, value) :: [{key, value}]
135+
136+
## Defining a specification
137+
138+
@spec function_name(type1, type2) :: return_type
139+
@callback function_name(type1, type2) :: return_type
140+
@macrocallback macro_name(type1, type2) :: Macro.t
141+
142+
Callbacks are used to define the callbacks functions of behaviours (see
143+
`Behaviour`).
144+
145+
Guards can be used to restrict type variables given as arguments to the
146+
function.
147+
148+
@spec function(arg) :: [arg] when arg: atom
149+
150+
Type variables with no restriction can also be defined.
151+
152+
@spec function(arg) :: [arg] when arg: var
153+
154+
Specifications can be overloaded just like ordinary functions.
155+
156+
@spec function(integer) :: atom
157+
@spec function(atom) :: integer
158+
159+
## Notes
160+
161+
Elixir discourages the use of type `string` as it might be confused with
162+
binaries which are referred to as "strings" in Elixir (as opposed to character
163+
lists). In order to use the type that is called `string` in Erlang, one has to
164+
use the `char_list` type which is a synonym for `string`. If you use `string`,
165+
you'll get a warning from the compiler.
166+
167+
If you want to refer to the "string" type (the one operated on by functions in
168+
the `String` module), use `String.t` type instead.

0 commit comments

Comments
 (0)