Skip to content

New concept exercise city-office (docs and typespecs) #693

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Mar 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 2 additions & 25 deletions concepts/basics/about.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,34 +65,11 @@ string = "this is a string! 1, 2, 3!"
- Automatically imported.
- Its functions can be used without the `Kernel.` prefix.

## Documentation
## Code comments

- Elixir provides 3 ways to write [inline documentation][inline-documentation].

- Single line comments are preceded by `#`.
- Functions may be documented with `@doc` preceding the named function definition

```elixir
@doc """
Function Documentation
"""
def function(), do: true
```

- Modules may be documented with `@moduledoc` immediately following the module definition

```elixir
defmodule Example do
@moduledoc """
Module documentation
"""

#...
end
```
Comments can be used to leave notes for other developers reading the source code. Single line comments in Elixir are preceded by `#`.

[match]: https://elixirschool.com/en/lessons/basics/pattern-matching/
[inline-documentation]: https://elixirschool.com/en/lessons/basics/documentation/#inline-documentation
[operators]: https://elixir-lang.org/getting-started/basic-types.html#basic-arithmetic
[modules]: https://elixirschool.com/en/lessons/basics/modules/#modules
[functions]: https://elixirschool.com/en/lessons/basics/functions/#named-functions
Expand Down
8 changes: 2 additions & 6 deletions concepts/basics/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,9 @@ Most built-in data types have a corresponding module that offers functions for w

A notable module is the `Kernel` module. It provides the basic capabilities on top of which the rest of the standard library is built, like arithmetic operators, control-flow macros, and much more. Functions for the `Kernel` module are automatically imported, so you can use them without the `Kernel.` prefix.

## Documentation
## Code comments

Documentation is a priority in high-quality Elixir code bases, and there are 3 ways to write inline documentation:

- Comments can be used for inline documentation. Single line comments in Elixir are preceded by `#`.
- Function-level documentation uses the `@doc` annotation preceding named function definitions
- Module-level documentation uses the `@moduledoc` annotation following the module definition
Comments can be used to leave notes for other developers reading the source code. Single line comments in Elixir are preceded by `#`.

[functional-programming]: https://en.wikipedia.org/wiki/Functional_programming
[docs]: https://hexdocs.pm/elixir/Kernel.html#content
106 changes: 106 additions & 0 deletions concepts/docs/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# About

Elixir documentation:

- A first-class citizen.
- Written in [**Markdown**][markdown].
- Added by using special module attributes.
- Typically uses the heredoc syntax for multiline strings.

Module attributes used for documentation:

- `@moduledoc` - describes a module, appears on the first line of the module.
- `@doc` - describes a function, appears right above the function's definition and its typespec.
- `@typedoc`- describes a custom type, appears right above the type's definition.

```elixir
defmodule String do
@moduledoc """
Strings in Elixir are UTF-8 encoded binaries.
"""

@typedoc """
A UTF-8 encoded binary.
"""
@type t :: binary

@doc """
Converts all characters in the given string to uppercase according to `mode`.

## Examples

iex> String.upcase("abcd")
"ABCD"

iex> String.upcase("olá")
"OLÁ"
"""
def upcase(string, mode \\ :default)
end
```

## [Documentation vs. code comments][documentation-vs-comments]

Elixir treats documentation and code comments as two different concepts.

Documentation is an explicit contract between you and the users of your public API (which also includes your coworkers and your future self). Those users might or might not have access to the source code, so the documentation explains how to use your code.

Code comments are aimed at developers reading the source code. They are useful for leaving notes, explaining implementation details, marking opportunities for improvement, and so on.

Because documentation is meant to describe the public API of your code, trying to add a `@doc` attribute to a private function will result in a compilation warning. For explaining private functions, use code comments instead.

```
warning: defp do_check_length/2 is private, @doc attribute is always discarded for private functions/macros/types
lib/form.ex:33: Form.do_check_length/2
```

## Consuming documentation

There are many different ways to access the documentation of an Elixir project.

### `hexdocs.pm` and `ExDoc`

[`hex.pm`][hex-pm] is a package repository for Elixir and Erlang. Every package published to `hex.pm` will get its documentation automatically published to [`hexdocs.pm`][hexdocs-pm]. There, you can find the documentation for all your favorite Elixir libraries, as well as [Elixir's official documentation][official-documentation] itself.

You can generate a documentation website for your project that looks exactly like Elixir's official documentation without having to publish a package to `hex.pm`. The tool that does it is called [`ExDoc`][ex-doc]. `ExDoc` will produce HTML files that you can browse from your local filesystem.

Make sure to follow the [official recommendations for writing documentation][writing-documentation-recommendations] to ensure best results when using `ExDoc`.

### The `h` IEx helper

You can access the documentation of the standard library, as well as any library you have installed and your Elixir project, directly from your computer.

If you have Elixir installed on your computer, you can use it in [the interactive mode][getting-started-iex] by running the `iex` command (or `iex -S mix` if you want to load the source of your current mix project).

In `iex`, you can type [`h`][iex-h], followed by a space and a module name or a function name, to read its documentation.

```elixir
iex()> h String.upcase/1
# def upcase(string, mode \\ :default)
#
# Converts all characters in the given string to uppercase according to mode.
# (...)
```

By pressing the tab key after providing a partial module or function name, you can leverage the autocomplete option to discover available modules and functions.

### Modern IDEs

Many modern IDEs that support Elixir can parse and display documentation and typespecs in useful pop-ups, for example [Visual Studio Code][vsc-documentation] or [Intellij with the Elixir plugin][intellij-elixir-documentation].

## Internal modules and function

If a module or a function is intended for internal usage only, you can mark it with `@moduledoc false` or `@doc false`. Those modules and functions will not be included in the generated documentation. Note that that doesn't make them private. They can still be invoked and/or imported. Check the [official documentation about hiding internal modules and functions][hiding-internal-modules-and-functions] to learn about potential solutions to this problem.

[markdown]: https://docs.github.com/en/github/writing-on-github/basic-writing-and-formatting-syntax
[official-documentation]: https://hexdocs.pm/elixir/
[ex-doc]: https://hexdocs.pm/ex_doc/readme.html
[hex-pm]: https://hex.pm/
[hexdocs-pm]: https://hexdocs.pm/
[writing-documentation-recommendations]: https://hexdocs.pm/elixir/writing-documentation.html#recommendations
[intellij-elixir-documentation]: https://github.com/KronicDeth/intellij-elixir#quick-documentation
[vsc-documentation]: https://thinkingelixir.com/elixir-in-vs-code/#Documentation_displayed_on_hover
[iex-h]: https://hexdocs.pm/iex/IEx.Helpers.html#h/1
[getting-started-iex]: https://elixir-lang.org/getting-started/introduction.html#interactive-mode
[hiding-internal-modules-and-functions]: https://hexdocs.pm/elixir/writing-documentation.html#hiding-internal-modules-and-functions
[documentation-vs-comments]: https://hexdocs.pm/elixir/writing-documentation.html#documentation-code-comments
30 changes: 30 additions & 0 deletions concepts/docs/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Introduction

Documentation in Elixir is a first-class citizen.

The two module attributes that commonly used to document your code - `@moduledoc` for documenting a module and `@doc` for documenting a function that follows the attribute. The `@moduledoc` attribute usually appears on the first line of the module, and the `@doc` attribute usually appears right before a function definition, or the function's typespec if it has one. The documentation is commonly written in a multiline string using the heredoc syntax.

Elixir documentation is written in [**Markdown**][markdown].

```elixir
defmodule String do
@moduledoc """
Strings in Elixir are UTF-8 encoded binaries.
"""

@doc """
Converts all characters in the given string to uppercase according to `mode`.

## Examples

iex> String.upcase("abcd")
"ABCD"

iex> String.upcase("olá")
"OLÁ"
"""
def upcase(string, mode \\ :default)
end
```

[markdown]: https://docs.github.com/en/github/writing-on-github/basic-writing-and-formatting-syntax
10 changes: 10 additions & 0 deletions concepts/docs/links.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[
{
"url": "https://hexdocs.pm/elixir/writing-documentation.html",
"description": "Documentation - Writing Documentation"
},
{
"url": "https://elixirschool.com/en/lessons/basics/documentation/",
"description": "Elixir School - Documentation"
}
]
89 changes: 89 additions & 0 deletions concepts/typespecs/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Introduction

Elixir is a dynamically typed language, which means it doesn't provide compile-time type checks. Still, type specifications can be useful because they:

- Serve as documentation.
- Can be used by static analysis tools like [Dialyzer][dialyzer] to find possible bugs.

A type specification can be added to a function using the `@spec` module attribute right before the function definition.

```elixir
@spec longer_than?(String.t(), non_neg_integer()) :: boolean()
def longer_than?(string, length), do: String.length(string) > length
```

## Types

Expressions allowed in a typespec:

- Basic types, for example:
- `boolean()`
- `integer()`, `non_neg_integer()`, `pos_integer()`, `float()`
- `list()`
- `map()`
- `any()`

- A union of types:
- e.g. `integer() | list(integer())`

- Parameterized types:
- e.g. `list(integer())` - a list of integers
- e.g. `%{age: integer()}` - map with an integer value under the key `:age`

- Remote types (defined in some module), for example:
- [`String.t()`][string-t]
- [`Enum.t()`][enum-t]
- [`Range.t()`][range-t]

- Literals, for example:
- `%{}` - an empty map
- `[]` - an empty list (but `[any()]` is a non-empty list)
- e.g. `:ok` - an atom literal

- Built-in specialized types, for example:
- `char()` - an integer from the range `0..0x10FFFF`
- `charlist()` - a list of chars
- `keyword()` - a list of two element tuples, where the first element is an atom

- Custom types

A full list of all types can be found in the ["Typespecs" section in the official documentation][types].

## Naming arguments

Arguments in the typespec could also be named which is useful for distinguishing multiple arguments of the same type.

```elixir
@spec to_hex({hue :: integer, saturation :: integer, lightness :: integer}) :: String.t()
```

## Custom types

Custom types can be defined in using one of the three module attributes:

- `@type` - defines a public type
- `@typep` - defines a private type
- `@opaque` - defines a public type whose structure is private

```elixir
@type color :: {hue :: integer, saturation :: integer, lightness :: integer}

@spec to_hex(color()) :: String.t()
```

## `String.t()` vs `binary()` vs `string()`

[`String.t()`][string-t] is the correct type to use for Elixir strings, which are UTF-8 encoded binaries. Technically, `String.t()` is defined as a `binary()`, and those two types are equivalent to analysis tools, but `String.t()` is the more intention-revealing choice for documenting functions that work with Elixir strings.

On the other hand, `string()` is a different type. It's an Erlang string, in Elixir known as a charlist. The `string()` type should be avoided in typespecs and `charlist()` should be used instead.

## Dialyzer

[Dialyzer][dialyzer] is a static analysis tool that can detect problems such as type errors in Erlang and Elixir code. The easiest way to use Dialyzer in an Elixir project is with [Dialyxir][dialyxir].

[types]: https://hexdocs.pm/elixir/typespecs.html#types-and-their-syntax
[string-t]: https://hexdocs.pm/elixir/String.html#t:t/0
[enum-t]: https://hexdocs.pm/elixir/Enum.html#t:t/0
[range-t]: https://hexdocs.pm/elixir/Range.html#t:t/0
[dialyzer]: http://erlang.org/doc/man/dialyzer.html
[dialyxir]: https://hexdocs.pm/dialyxir/readme.html
50 changes: 50 additions & 0 deletions concepts/typespecs/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Introduction

Elixir is a dynamically typed language, which means it doesn't provide compile-time type checks. Still, type specifications can be used as a form of documentation.

A type specification can be added to a function using the `@spec` module attribute right before the function definition. `@spec` is followed by the function name and a list of all of its arguments' types, in parentheses, separated by commas. The type of the return value is separated from the function's arguments with a double colon `::`.

```elixir
@spec longer_than?(String.t(), non_neg_integer()) :: boolean()
def longer_than?(string, length), do: String.length(string) > length
```

## Types

Most commonly used types include:

- booleans: `boolean()`
- strings: `String.t()`
- numbers: `integer()`, `non_neg_integer()`, `pos_integer()`, `float()`
- lists: `list()`
- a value of any type: `any()`

Some types can also be parameterized, for example `list(integer)` is a list of integers.

Literal values can also be used as types.

A union of types can be written using the pipe `|`. For example, `integer() | :error` means either an integer of the atom literal `:error`.

A full list of all types can be found in the ["Typespecs" section in the official documentation][types].

## Naming arguments

Arguments in the typespec could also be named which is useful for distinguishing multiple arguments of the same type. The argument name, followed by a double colon, goes before the argument's type.

```elixir
@spec to_hex({hue :: integer, saturation :: integer, lightness :: integer}) :: String.t()
```

## Custom types

Typespecs aren't limited to just the built-in types. Custom types can be defined using the `@type` module attribute. A custom type definition starts with the type's name, followed by a double colon and then the type itself.

```elixir
@type color :: {hue :: integer, saturation :: integer, lightness :: integer}

@spec to_hex(color()) :: String.t()
```

A custom type can be used from the same module where it's defined, or from another module.

[types]: https://hexdocs.pm/elixir/typespecs.html#types-and-their-syntax
10 changes: 10 additions & 0 deletions concepts/typespecs/links.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[
{
"url": "https://hexdocs.pm/elixir/typespecs.html",
"description": "Documentation - Typespecs"
},
{
"url": "https://elixirschool.com/en/lessons/advanced/typespec/",
"description": "Elixir School - Typespecs"
}
]
Loading