Skip to content

Commit

Permalink
Book harder
Browse files Browse the repository at this point in the history
  • Loading branch information
lpil committed Feb 10, 2019
1 parent 72df65a commit cd16487
Show file tree
Hide file tree
Showing 36 changed files with 1,409 additions and 403 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ gleam/target/release/gleam: $(shell find gleam/src -type f)
cd gleam && cargo build --release

docs/index.html: $(shell find book/src -type f)
rm -r docs
cd book && mdbook build --dest-dir ../docs/
9 changes: 7 additions & 2 deletions book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@

- [Hello, Gleam!](./README.md)
- [Language tour](./tour/README.md)
- [String & Atom](./tour/string-and-atom.md)
- [String](./tour/string.md)
- [Bool](./tour/bool.md)
- [Int & Float](./tour/int-and-float.md)
- [Let bindings](./tour/let.md)
- [Case](./tour/case.md)
- [Tuple](./tour/tuple.md)
- [List](./tour/list.md)
- [Let](./tour/let.md)
- [Function](./tour/function.md)
- [Record](./tour/record.md)
- [Enum](./tour/enum.md)
- [External function](./tour/external-function.md)
- [External type](./tour/external-type.md)
- [Module](./tour/module.md)
16 changes: 10 additions & 6 deletions book/src/tour/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
# Comparison to Erlang and Elixir
# Language Tour

Being another functional language on the BEAM (the Erlang virual machine)
Gleam is very similar to both Erlang and Elixir, albeit with a static type
system.
In this chapter we explore the fundamentals of the Gleam language, namely its
syntax, core data structures, flow control features, and static type system.

This chapter aims to give Erlang and Elixir users enough knowledge to start
writing reading and writing Gleam code.
After completion the reader should know enough to start reading and writing
Gleam code, assuming they have some prior programming experience.

In some section we touch on the runtime representation of various features.
This is useful for programmers with Erlang or Elixir experience who wish to
use Gleam alongside these languages. If you are using Gleam alone this
information can be safely ignored.
2 changes: 1 addition & 1 deletion book/src/tour/bool.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ hand side if they don't have to.
`||` evaluates the right hand side if the left hand side is `False`.


## Runtime representation
## Erlang interop

While written in the code using a capital letter, they are represented at
runtime with the atoms `true` and `false`, making them compatible with Elixir
Expand Down
1 change: 1 addition & 0 deletions book/src/tour/case.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Case
21 changes: 14 additions & 7 deletions book/src/tour/enum.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# Enums

Enums in Gleam are a way of modeling data that can be one of a few different
variants.
variants. They must be declared before use, and the names of variants must be
unique for the given module.

We've seen an enum already in this chapter- `Bool`. In Gleam Bool is defined
like this:
We've seen an enum already in this chapter- `Bool`.

Bool is defined like this:

```rust,noplaypen
// A Bool is a value that is either `True` or `False`
Expand All @@ -13,7 +15,9 @@ enum Bool =
| False
```

Enum variants can also contain other values.

Enum variants can also contain other values, and these values can be extracted
using a let binding.

```rust,noplaypen
enum User =
Expand All @@ -26,8 +30,11 @@ let rick = LoggedIn("Rick")
let visitor = Guest
```

When given an enum we need to pattern match on it to determine the variant and
use any contained values.

## Destructuring

When given an enum we can pattern match on it to determine which variant it is
and to assign names to any contained values.

```rust,noplaypen
fn get_name(user) {
Expand All @@ -52,7 +59,7 @@ p // => 50
```


## Runtime representation
## Erlang interop

At runtime enum variants with no contained values become atoms. The atoms are
written in `snake_case` rather than `CamelCase` so `LoggedIn` becomes
Expand Down
1 change: 1 addition & 0 deletions book/src/tour/external-function.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# External function
1 change: 1 addition & 0 deletions book/src/tour/external-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# External type
1 change: 1 addition & 0 deletions book/src/tour/function.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Function
33 changes: 5 additions & 28 deletions book/src/tour/let.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,14 @@
# Variables
# Let bindings

A value can be given a name using `let`. Names can be reused by later let
bindings, but the values contained are _immutable_, meaning the values
themselves cannot be changed.

```rust,noplaypen
// Gleam
let x = 1
let x = 2
```
```
# Elixir
x = 1
x = 2
```
```
% Erlang
X = 1.
X = 2. % Runtime error! Erlang doesn't allow rebinding
```

Pattern matching can be used to extract contained values from data structures
when defining variables with `let`.
let y = x
let y = 2
```rust,noplaypen
// Gleam
let {x, y} = {1, 2.0}
```
```
# Elixir
{x, y} = {1, 2.0}
```
```
% Erlang
{X, Y} = {1, 2.0}.
x // => 1
y // => 2
```
15 changes: 13 additions & 2 deletions book/src/tour/list.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ same type. Attempting to construct a list of multiple types of element will
result in the compiler presenting a type error.

```rust,noplaypen
// Gleam
[1, 2, 3, 4] // List(Int)
[1.22, 2.30] // List(Float)
[1.22, 3, 4] // Type error!
Expand All @@ -17,6 +16,18 @@ result in the compiler presenting a type error.
Prepending to a list is very fast, and is the preferred way to add new values.

```rust,noplaypen
// Gleam
[1 | [2, 3]] // => [1, 2, 3]
```

Note that as all data structures in Gleam are immutable so prepanding to a
list does not change the original list, instead it efficiently creates a new
list with the new additional element.

```rust,noplaypen
let x = [2, 3]
let y = [1 | y]
x // => [2, 3]
y // => [1, 2, 3]
```
1 change: 1 addition & 0 deletions book/src/tour/module.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Module
114 changes: 62 additions & 52 deletions book/src/tour/record.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,87 +3,97 @@
Gleam's records are a collection of names and values where each value can be
of any type.

At runtime they are maps with atom keys. They are not the same as Erlang
records, which are tuples. They are similar to Elixir's structs, but do not
need to be declared prior to being used.

```rust,noplaypen
// Gleam
{
name = "Rex",
size = 40,
}
```
```
# Elixir
%{
name: "Rex",
size: 40
}
```
```
% Erlang
#{
name => <<"Rex">>,
size => 40
}.
```

Record values can be accessed using the `.` syntax, as in Elixir.

The Gleam compiler keeps tracks of what fields and values each record has and
will present a compile time error if you try to use a record field that does
not exist or has the incorrect type.
## Accessing field values

Record values can be accessed using the `record.field_name` syntax.

```rust,noplaypen
// Gleam
let pup = { name = "Rex", size = 40 }
let name = pup.name
pup.huh // Compile time error
```
name // => "Rex"
```
# Elixir
pup = %{ name: "Rex", size: 40 }
name = pup.name

pup.huh # Runtime error
```
```
% Erlang
Pup = #{name => <<"Rex">>, size => 40}.
#{name => Name} = Pup.

#{huh => Huh} = Pup. % Runtime error
```
## Inserting field values

A new record with updated or additional fields can be created using the update
syntax.
A new record with updated or additional fields can be created using the
`{ record | field_name = value }` update syntax.

The new/updated record fields do not have to have the same types as the
original fields.
The updated record fields do not have to have the same types as the original
fields.

```rust,noplaypen
// Gleam
let pup = { name = "Rex", size = 40 }
let dog = { pup | size = 70, playful = True }
pup // { name = "Rex", size = 40 }
dog // { name = "Rex", size = 70, playful = True }
```


## Types

The type of a record depends upon the names and types of its fields.

```rust,noplaypen
{ name = "Rex" } // Type { name = String }
{ name = "Rex", size = 40 } // Type { name = String, size = Int }
```
# Elixir
pup = %{ name: "Rex", size: 40 }
dog = %{ pup | size: 70 } |> Map.put(:playful, true)

pup # { name: "Rex", size: 40 }
dog # { name: "Rex", size: 70, playful: true }
The Gleam compiler keeps tracks of what fields and values each record has and
will present a compile time error if you try to use a record field that does
not exist or has the incorrect type.

```rust,noplaypen
let pup = { name = "Rex", size = 40 }
pup.address // Compile time error! Unknown field
pup.name + 1 // Compile time error! Wrong type
```


### Parameterised record fields

Gleam's type system aims to be as permissive when it comes to records passed
to functions. Take this function for example.

```rust,noplaypen
fn get_following_year(record) {
record.year + 1
}
```
% Erlang
Pup = #{name => <<"Rex">>, size => 40}.
Dog = Pup#{size => 70, playful => true}.

Pup. % #{name => <<"Rex">>, size => 40}
Dog. % #{name => <<"Rex">>, size => 70, playful => true}
The type of this function is `fn({ a | year = Int }) -> Int`.

The `a |` in `{ a | year = Int}` means "any other fields", so this function
can be called with any record so long as the record has a `year` field that
has an `Int` value.

```rust,noplaypen
let date = { day: 5, month: 1, year: 2019 }
let book = { title: "Sabriel", year: 1995 }
let soup = { kind: "Tomato", spicy: False }
let wine = { kind: "Fancy!", year: "Good" }
get_following_year(date) // => 2020
get_following_year(book) // => 1996
get_following_year(soup) // Compile time error! No year field
get_following_year(wine) // Compile time error! Wrong field type
```


## Erlang interop

At runtime records are maps with atom keys. They are not the same as Erlang
records, which are tuples. They are similar to Elixir's structs, but do not
need to be declared prior to being used.

24 changes: 0 additions & 24 deletions book/src/tour/string-and-atom.md

This file was deleted.

20 changes: 20 additions & 0 deletions book/src/tour/string.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# String

Gleam's has UTF-8 binary strings, written as text surrounded by double quotes.

```rust,noplaypen
"Hello, Gleam!"
```

Strings can span multiple lines.

```rust,noplaypen
"Hello
Gleam!"
```

Special characters such as `"` need to be escaped with a `\` character.

```rust,noplaypen
"Here is a double quote -> \" <-"
```
Loading

0 comments on commit cd16487

Please sign in to comment.