# The language

This part describes the language Sake itself, as the v0 interpreter (`bin/sake`) implements it, in ten chapters and one appendix. It is written for readers who know Ruby, people and language models alike. The syntax is Ruby's, so what the part explains is the meaning Sake gives to Ruby's spelling, and the rules Ruby does not have.

## One idea

A Sake program is written in Ruby's syntax, but instead of `name.upcase` you write `String.upcase(name)`. Only an **operation** names its type; a variable, a parameter, a return value or a field carries nothing.

```ruby
words = String.split("the cat and the hat", " ")
counts = Hash.new(0)
Array.each(words) { |w| counts[w] += 1 }
p(Hash.to_a(counts).Array.sort_by { |w, n| -n }.first(2))   # [["the", 2], ["cat", 1]]
```

Nearly every property of the language follows from this one rule.

- **One line is enough to read most calls.** `Array.each(words)` says which `each` runs without a declaration to look up; in a chain `Array.sort_by(xs) { }.first(3)`, `.first` is an operation of Array, the type the line named last.
- **Call targets are fixed before running.** `String.upcase(s)` names one function. A module's mixin function `M.f(x)`, a listed call `(A|B).f(x)` and an operator `a + b` choose by the type of the first argument, but the set of candidates (the types that include `M`, the listed types, the operator table) is fixed before running, and a type outside the set is reported before running. Nothing searches the receiver at run time (no `method_missing`, no reflection), so a misspelled name (`String.upcse`) or a wrong number of arguments is also reported for the whole program before it starts.
- **Types are inferred, never written.** The checker infers the types of the whole program and reports a value that does not fit an operation (`"" + 1`, an unchecked nil) before running; how much of that stops a run is chosen with `--strict`, levels 0 to 4.
- **Run-time checks remain.** Values carry type tags and every operation checks its arguments, so what the checks before running miss still stops at that operation's line.

A few spellings mean something else than in Ruby. `[a, b]` is a Tuple and `{x: 1}` a Record; an Array and a Hash are written `Any[...]` and `Hash.of(...)`. Inside a `class`, `@x` is "field x of the first argument", not an instance's own state. `class B < A` is shorthand for writing A's definitions in B, not inheritance. The chapters of this part take up these differences one by one.

## The chapters

| Chapter | Contents |
|---|---|
| [Overview and running programs](01-overview.md) | The four principles, the command and its exit status, the `--strict` levels 0 to 4 and their items (`type`, `nil`, `mixed`, `index-nil`, `exhaustive`, `rescue`, `unrescued`), the format of static and runtime errors. |
| [Program structure and name resolution](02-program.md) | Files and `require`, qualified calls `T.f(x)`, chains `T.f(x).g` and `_`, why there are no calls on values, unqualified calls (your own functions, `puts`, `p`), `include`, `module_function` and dispatch, listing the types `(A\|B).f(x)`, arity and blocks. |
| [Values and types](03-values.md) | How values print, Tuple versus Record versus Array, Hash and Set, nil and the narrowing of `nil \| T`, Ruby's other types (Symbol, Range, Regexp, Time, Rational, Complex). |
| [Functions and blocks](04-functions.md) | Polymorphic functions without annotations, optional and keyword parameters, blocks passed with `yield`, `once`. |
| [Operators and indexing](05-operators.md) | `a + b` is short for `Arithmetic.+(a, b)`, resolved by the type of the left operand; `Arithmetic`, `Comparable`, `Bitwise` and `Indexable` give your own types operators; `a[i]` and `a[i] = v`. |
| [Control flow and patterns](06-control.md) | `if`, `while`, `case`/`in`, narrowing with `x in T`, the exhaustiveness check of a `case`. |
| [Classes](07-classes.md) | Declaring a type with `class` and `attr_*` lines (`Struct.new` is the shorthand), what `@x` means, `initialize`, `class B < A`, one type per construction site. |
| [Exceptions and errors](08-exceptions.md) | `raise`, `rescue`, `ensure`, `retry`, declaring exception types, the inference of which exceptions escape a function, the kinds of static and runtime error. |
| [Built-in operations](09-builtins.md) | A survey of the about 550 built-in operations (named after Ruby's core library) and typed arrays `T[...]`. The exact signature and examples of each operation are in the [Built-in reference](part-reference.md). |
| [The library (sakelib)](10-library.md) | Ports of the standard library and of gems, minitest, how to write tests. |
| Appendix [Differences from Ruby and what is not supported](a1-ruby.md) | Ruby's spelling against Sake's, the syntax and features not supported, the design material. |

## How to read it

- **If you know Ruby and want to start writing**, read [Overview and running programs](01-overview.md), [Program structure and name resolution](02-program.md), [Values and types](03-values.md) and [Operators and indexing](05-operators.md) in that order, and keep the appendix's table at hand: it explains most static errors you will meet. Read [Classes](07-classes.md) when you declare a type.
- **If a language model is to write Sake**, hand it [cheatsheet.md](https://github.com/ko1/sake-lang/blob/main/docs/cheatsheet.md), which compresses the rules of this part into four pages (about 10k tokens). A guided introduction is [tutorial.md](https://github.com/ko1/sake-lang/blob/main/docs/tutorial.md).
- **To try things**, the [playground](https://ko1.github.io/sake-lang/playground/) runs in the browser, with completion, diagnostics as you type, and the inferred types on hover.
- **Conventions.** A `# value` in an example is the real output of `bin/sake`. A "static error" is reported before running, and nothing runs; a "runtime error" stops at that operation. "Level n" means `--strict=n`.

# Overview and running programs

Sake (/seɪk/) is an experimental language designed for the sake of finding a new place for types. It keeps Ruby's syntax, but you write the type on each **operation**, never on a variable, a parameter, a return value, or a field.

```ruby
name = "sake"
puts(String.upcase(name))      # => SAKE
```

Ruby's spelling, `name.upcase`, is a static error, and its hint gives the form above.

```ruby error
name = "sake"
puts(name.upcase)              # !> method call on a value `name.upcase` is not allowed
```

This book is the reference manual of the language as implemented by the v0 interpreter (`bin/sake`). For a guided introduction see [tutorial.md](https://github.com/ko1/sake-lang/blob/main/docs/tutorial.md); for a short summary to hand to a language model, [cheatsheet.md](https://github.com/ko1/sake-lang/blob/main/docs/cheatsheet.md) (about 10k tokens). The design notes and their reasons are in `DESIGN.md` (in Japanese).

## Principles

Nearly every property of Sake follows from four principles. Each chapter of this part works out their consequences.

1. **Ruby syntax.** A Sake program is a Ruby program as parsed by Prism. Sake accepts a subset of Ruby's syntax and gives some constructs a different meaning.
2. **Types are written on operations, not on bindings.** An operation is called with its type, `Type.op(subject, args...)`. Variables, parameters, return values, and fields carry no type annotations.
3. **Call targets are fixed before running.** `String.upcase(s)` names one function. A module's mixin function `M.f(x)`, a listed call `(A|B).f(x)` and an operator `a + b` choose by the type of the first argument, but the set of candidates is fixed before running. The candidates are the types that include `M`, the listed types, or the operator table. A type outside the set is reported before running. Nothing searches the receiver at run time (no `method_missing`, no reflection), so a misspelled name (`String.upcse`) or a wrong number of arguments is also reported for the whole program before it starts.
4. **Values carry type tags, and every operation checks them.** A wrong type is reported as an error at the operation that received it, with the line number. These checks are always enabled.

The checks of principle 4 catch what the checks before running let through. In the next example, `h["b"]` gives nil for the missing key, and `String.upcase` stops at that line.

```ruby error
h = Hash.of("a" => "x")
puts(String.upcase(h["b"]))    # !> TypeError: String.upcase: argument 1 must be String, got nil
```

## Running

`bin/sake` checks a program first and runs it when no problem is found. Options select checking only, or how strictly to check.

```
bin/sake FILE.sake              # check, then run
bin/sake -c FILE.sake           # check only
bin/sake --strict FILE.sake     # check more strictly (level 2), then run
bin/sake --strict=3 FILE.sake   # levels 0-4, or items: --strict=type,nil
bin/sake --types FILE.sake      # experimental: print the inferred types instead of running
bin/sake --dump=ast FILE.sake   # print the resolved program (SakeAST)
```

| Exit status | Meaning |
|---|---|
| 0 | success |
| 1 | runtime error |
| 2 | problem found before running (nothing was executed) |

The interpreter is written in Ruby and needs Ruby 4.0 (tested with 4.0.2 and Prism 1.9.0). It has no other dependencies.

## Strictness (`--strict`)

`--strict` sets which problems stop the program before it runs. The checker infers the types of the whole program and sorts the problems it finds into **items** (`type`, `nil`, ...). A level is a set of items that stop the program. Whatever is not reported is still checked while running.

| Level | Option | Items added | Stops |
|---|---|---|---|
| 0 | `--strict=0` | (none) | what is always checked |
| 1 | default | `type`, `rescue` | a type that does not fit; a `rescue` an earlier clause already covers |
| 2 | `--strict` | `nil`, `mixed` | an unchecked nil; types that met in a field |
| 3 | `--strict=3` | `index-nil`, `exhaustive` | the nil of a missed index; a `case` that is not exhaustive |
| 4 | `--strict=4` | `unrescued` | a `raise` that reaches the top level |

Each level includes the items of the levels below it. One item, `unused-rescue`, is in no level: it is a warning unless it is chosen (`--strict=2,unused-rescue`).

### Always checked (level 0)

These problems stop the program before running at every level: syntax, names, argument counts, blocks, calls on values, forbidden syntax, and the literal types in `T[...]`.

### `type` (level 1)

A value whose type, other than nil, does not fit the operation. It is reported both when the type is certain (`"" + 1`) and when the value may have a type that does not fit (`pick() + 1` where `pick` returns 1 or `""`).

```ruby error
def half(n) = n / 2
puts(half("ten"))              # !> the operands are (String, Integer), which the left operand's type does not support [type]
```

The report points at `n / 2` inside `half`. Its hint, `reached by the call at line 2`, names the call that led there.

### `rescue` (level 1)

A `rescue` clause that an earlier clause of the same `begin` already covers, so it is never reached. For an example, see "Grouping with a module" in [Exceptions](08-exceptions.md).

### `unused-rescue` (a warning)

A `rescue` of an exception the begin body never raises. It covers the exception types the program declares, and `RuntimeError`. Built-in kinds such as `KeyError` and `ZeroDivisionError` can come from ordinary operations, so they are always assumed possible and never reported.

It is a warning and the program runs. A defensive `rescue` is normal, and whether anything raises the exception depends on the whole program: in a library, a `rescue` for something only some apps raise would otherwise stop every other app. Choose the item to make it an error: `--strict=2,unused-rescue`.

```ruby
class ParseError < StandardError
end
begin
  p(10 / 2)                    # => 5
rescue ParseError              # warning: the begin body never raises ParseError [unused-rescue]
  p(0)
end
```

### `nil` (level 2)

A value that may be nil, used without a check. What is a `TypeError` while running at level 1 is reported before running at level 2.

```ruby error
words = String.split("a b", " ")
w = Array.find(words) { |x| x == "c" }
puts(String.upcase(w))         # !> argument 1 may be nil (nil | String) [nil]
```

The nil of a miss is excepted. A miss is the nil given by an index `x[k]`, by `Array.dig`, `Hash.dig`, `MatchData.begin`/`end`, and by these operations on an empty collection: `Array.first`, `last`, `pop`, `shift`, `min`, `max`, `minmax`, `at`, `slice`, `sample`, `delete_at`, `Set.first`, `min`, `max`, `min_by`, `max_by`. The `index-nil` item of level 3 covers them. How a nil is narrowed away is described in [Values and types](03-values.md).

### `mixed` (level 2; a warning at level 1)

`mixed` is a kind of `type` report. It is given when the failing types all appear, together with fitting ones, in one field of a class. The elements of a container held in a field count as well.

The checker gives a field one type per construction site ([Classes](07-classes.md)). When instances made at one place are used for different values, the types meet in that field. In the next example, the Heap that `make()` builds is used for Integers and for Strings.

```ruby error
class Heap
  attr_accessor items
  def initialize(h)
    @items = Any[]
  end
  def push(h, x) = Array.push(@items, x)
  def top(h) = Array.first(@items)
end
def make() = Heap.new(nil)
ints = make()
Heap.push(ints, 1)
strs = make()
Heap.push(strs, "a")
p(Heap.top(ints) + 1)          # !> the operands may be (nil, Integer), (String, Integer), which the left operand's type does not support [mixed]
```

Such a report is likely a meeting rather than a mistake, so it stops the program from level 2, and at level 1 it is printed as a warning. At level 1 the example above prints the warning, runs, and prints `2`.

The cost of this heuristic: a field that really got a wrong type at the same place is also `mixed`. For example, `Config.new("h", port)` where `port` is 80 on one call and `"eighty"` on another. Check such a value where it is stored, in `initialize` (`@port => Integer`).

### `index-nil` (level 3)

The nil of a miss (above), used without a check. The next program runs up to level 2 and prints `2`.

```ruby
xs = Any[1, 2, 3]
x = xs[0]
p(x + 1)                       # => 2
```

At level 3 it stops before running.

```
$ bin/sake --strict=3 index.sake
index.sake:3:3: error: Arithmetic.+: the operands may be nil, because x[k] (or `a, b = array`) gives nil when the element is missing [index-nil]
  hint: check the value first: `if x`, `while x`, `return unless x`, or `x != nil`
  hint: or use Array.fetch / Hash.fetch, which raise instead
```

### `exhaustive` (level 3)

A `case`/`in` that may get a value of an open type that no literal branch takes. An open type is one whose values cannot be listed: String, Integer, a Symbol not written as a literal, and so on. The next program runs up to level 2.

```ruby
def kind(s)
  case s
  in "a" then 1
  in "b" then 2
  end
end
p(kind(String.downcase("A")))  # => 1
```

```
$ bin/sake --strict=3 kind.sake
kind.sake:2:3: error: case/in: no `in` branch matches some values of String [exhaustive]
  hint: add an `else`, or `in` branches for the other values
  hint: reached by the call at line 7
```

### `unrescued` (level 4)

A `raise` that may reach the top level without being rescued. The item is meant for a program; a library's raises are meant for its callers, so it does not suit a library.

```ruby
def check(n)
  raise ArgumentError, "negative" if n < 0
  n
end
p(check(1))                    # => 1
```

```
$ bin/sake --strict=4 check.sake
check.sake:2:3: error: raise: ArgumentError may reach the top level without being rescued [unrescued]
  hint: rescue it, or check with a level below 4
```

### Naming items

Items can be named instead of a level.

- `--strict=type,nil` selects exactly these items.
- `--strict=2,index-nil` adds an item to a level.
- `--strict=3,-index-nil` removes an item from a level.

Each report ends with its item, such as `[type]`, which tells you which item to add or remove.

### How reports behave

- **Errors inside functions.** A report inside a polymorphic function adds a hint naming the call that led there (the `half` example above).
- **Unreached branches.** Branches are not evaluated, so a problem in a branch that never runs is still reported, as in Erlang's Dialyzer.
- **Internal errors.** If the type inference itself fails, the type checks are skipped with a warning, and the program runs.
- **Too many element pairs.** Comparing Arrays or Tuples (`<`, `<=>`, sorting) checks that each pair of element types can be compared. When the element types of one comparison make more than 50,000 pairs, that comparison's elements are not checked, with a warning at the comparison. The other checks are unaffected.

## Format of static errors

Static errors are reported all together, sorted by position, and nothing runs. Each line gives the file, the line, the column and the message; the `hint:` lines that follow suggest a fix.

```
FILE:LINE:COLUMN: error: MESSAGE
  hint: SUGGESTION
```

The `name.upcase` example at the top of this chapter prints:

```
greet.sake:2:11: error: method call on a value `name.upcase` is not allowed
  hint: String.upcase(name)
  hint: Symbol.upcase(name)
```

## Format of runtime errors

A runtime error prints the line of the operation that stopped and the chain of calls that led there (the `from` lines).

```
FILE:LINE: in FUNCTION: KIND: MESSAGE
  from FILE:LINE: in CALLER
  hint: SUGGESTION
```

```ruby error
def third(xs) = Array.fetch(xs, 2)
p(third(Any[1, 2]))          # !> IndexError: Array.fetch: index 2 outside of array bounds: -2...2
```

```
third.sake:1: in third: IndexError: Array.fetch: index 2 outside of array bounds: -2...2
  from third.sake:2: in <main>
```

The kinds are listed in [Exceptions and errors](08-exceptions.md).

# Program structure and name resolution

This chapter covers what a program is made of and how the names in it are resolved. One idea runs through it: every definition is collected before anything runs, and every call's callee is decided before running. So a function may be called on a line above its definition, and a misspelled name is reported before running.

## Files and `require`

A program is a file and the files it `require`s. The top level of a file holds definitions and statements. Definitions are collected together before running; statements run from top to bottom.

```ruby
puts(greet("Sake"))                  # => Hello, Sake
def greet(name) = "Hello, #{name}"   # callable from a line above its definition
def String.shout(s) = String.upcase(s) + "!"
puts(String.shout("hi"))             # => HI!
Point = Struct.new(:x, :y)
class Point
  def to_s(p) = "(#{@x}, #{@y})"
end
module Util
  module_function
  def origin = Point.new(0, 0)
end
puts(Util.origin)                    # => (0, 0)
```

### What the top level may contain

- **`require "path"`**: reads another file; **`generate "gen"`** joins the source a generator prints. See the next sections.
- **Function definitions**: `def name(params) ... end` and `def name(params) = expr`.
- **Namespaced function definitions**: `def Type.name(params) ...`, equivalent to defining `name` inside `class Type`.
- **Namespaces**: `class Name ... end` and `module Name ... end`.
- **Classes**: `class Name` with `attr_*` lines, or `Name = Struct.new(:field, ...)`.
- **Statements**: any other expression. Statements run in order.

### `require`

`require "path"` reads `path.sake`. The extension may be left out, and the path is relative to the directory of the file that requires it.

```ruby
# lib/units.sake
puts("loading units")
def km_to_m(km) = km * 1000
```

```ruby
# main.sake
require "lib/units"          # => loading units
puts("main")                 # => main
puts(km_to_m(3))             # => 3000
```

A **glob**, `require "sql/*"`, reads every file matching `sql/*.sake` next to the requiring file, in **name order** (the requiring file itself excepted); a glob that matches nothing is an error. Definitions are collected first, so the order matters only for top-level statements.

- **Decided before running.** The argument must be a string literal, and `require` must be a statement at the top level of a file. The files of a program are decided before running.
- **Once.** Each file is read once, whatever requires it. A cycle stops at a file already read.
- **Order.** A required file's top-level statements run before those of the file that requires it. All `require`s of a file are read first. That is why `loading units` comes before `main` above.
- **One collection of definitions.** The definitions of every file are collected together, so names may be used across files in any order. Defining a name in two files is an error, as it is in one file.
- **Messages.** Error messages name the file of each line (`lib/units.sake:4`, `from lib/broken.sake:3`).
- **Library.** A name with no sibling file, such as `require "json"`, is looked up in the [library](10-library.md) shipped with the interpreter (`sakelib/`).

### `generate`: code made before the program runs

Sake defines no function while running. Code that Ruby would make while booting (a function per column of a schema, a template compiled to a function) is made one stage earlier by `generate "gen", "arg", ...`: before the program is checked, `gen.sake` is checked and run as a program of its own (the other arguments are its `ARGV`), and **what it prints is joined into the program as if it were a required file** at that point. Then the whole program, generated code included, is checked and run.

```ruby
# gen.sake: a reader per line of cols.txt ("name", "age")
Array.each_with_index(String.lines(File.read("cols.txt"))) do |c, i|
  puts "# sake-origin: cols.txt:#{i + 1}"
  puts "def #{String.strip(c)}(row) = row[#{Kernel.inspect(String.strip(c))}]"
end
```

```ruby
# main.sake
generate "gen"
p(name(Hash.of("name" => "ko1")))
```

- **Every run.** The generator runs at every start, so the generated code is never stale, and it is always checked. It cannot see the program it generates for. The same generator with the same arguments runs once.
- **Like `require`.** The arguments are string literals, and `generate` is a statement at the top level of a file (elsewhere, `generate(...)` is an ordinary call). The generator is found as `require` finds a file.
- **Where errors point.** Generated code is named `(generate at main.sake:2)`. A line `# sake-origin: FILE:LINE` written by the generator makes the lines after it, up to the next such line, report as FILE:LINE (here `cols.txt:1`), in static errors, runtime errors and backtraces; `# sake-origin: none` ends it. A problem in the generator itself, or an error it ends with, is reported at the `generate` line, and nothing runs.
- **Seeing it.** `bin/sake --dump=generated main.sake` prints the generated source.

### Namespaces: `class` and `module`

`class Name ... end` and `module Name ... end` open a namespace. `class` declares a **type** (a class) or adds operations to an existing one, including a built-in type such as `String`. `module` is a namespace with **no type**.

The body of a namespace may contain only the following.

- `def name(...)` (no receiver)
- `include Module`
- `module_function` (in a module; see "Calling a module's functions" below)
- a nested `class` / `module`, or `Name = Struct.new(...)`
- a type alias `Name = A::B` (see "Type aliases" below)
- in a class, `attr_reader` / `attr_accessor` / `attr_writer` lines ([Classes](07-classes.md))

Any other statement in a body is a static error. `module` on a type is a static error too, with a hint that gives `class`.

```ruby error
module String                # !> `module String`: String is a type; add operations to a type with class
  def shout(s) = String.upcase(s)
end
```

### Namespaces nest

`class B` (or `module B`, or `B = Struct.new(...)`) written inside `module A` declares `A::B`. `class A::B` at the top level opens the same namespace, so either spelling adds operations to the same class.

```ruby
module Geo
  Point = Struct.new(:x, :y)
  class Point
    def norm2(p) = @x * @x + @y * @y
  end
  module Util
    module_function
    def origin = Point.new(0, 0)      # Point here is Geo::Point
  end
end
class Geo::Point                       # the Ruby spelling opens the same class
  def to_s(p) = "(#{@x}, #{@y})"
end
pt = Geo::Point.new(3, 4)
p(Geo::Point.norm2(pt))                # => 25
p(pt.Geo::Point.norm2)                 # => 25
puts(Geo::Util.origin)                 # => (0, 0)
p((pt in Geo::Point))                  # => true
```

- **The full name outside.** Write `A::B.f(x)`, `x.A::B.f`, `include A::M`, `class C < A::B`, `x in A::B`, `(A::B|C).f(x)`, `rescue A::E`.
- **The short name inside.** Inside `A`, a bare `B` means `A::B`. As in Ruby, the lookup starts at the innermost namespace, and falls back to the top-level `B`.
- **No `def A::B.f`.** It is not Ruby syntax. A module function of a nested module is defined inside it (`module A::B` + `module_function`).
- **A namespace is not a value.** `p(A::B)` is an error, and `A::B` alone has no meaning.

```ruby error
module Geo
  Point = Struct.new(:x, :y)
end
p(Geo::Point)                # !> type `Geo::Point` cannot be used as a value
```

### Type aliases

`Client = Handshake::Client` gives another name to an existing type: a class, a module, or a built-in type (`Str = String`). The alias is a constant, with Ruby's scope: it belongs to the namespace it is written in (the top level, or a `class` / `module` body), it is found as a type name is (the innermost namespace first, then outward, then the top level), and from outside it is `WebSocket::Client`. The right-hand side is a constant path, resolved where the alias is written; it may be another alias.

```ruby
module WebSocket
  module Handshake
    class Client
      attr_reader url
      def host(c) = String.split(@url, "/")[2]
    end
  end
  Client = Handshake::Client           # WebSocket::Client
  module Session
    module_function
    def open(url) = Client.new(url)    # Client here is WebSocket::Client
  end
end
C = WebSocket::Client                  # an alias of an alias
c = WebSocket::Session.open("ws://example.com/chat")
p(C.host(c))                           # => "example.com"
p(c.WebSocket::Client.host)            # => "example.com"
p((c in WebSocket::Handshake::Client)) # => true
```

- **A name, not a new type.** `Client.new`, `Client.f(x)`, `x.Client.f`, `def Client.f`, `include`, `class C < Client`, `in Client`, `rescue Client`, `raise Client` and `Client::Inner` all mean the type it names. Messages name that type; an undefined function's hint says which type the alias names.
- **One name per namespace.** An alias whose name is already a type or an alias of its namespace is a static error, as a duplicate definition is. A class is not defined through an alias (`class Client::Inner`): use the type's own name.
- **Only types.** A right-hand side that names no type, and a cycle of aliases, are static errors. `Pi = Math::PI` stays a value constant (an error with the hint `def pi = Math::PI`).

```ruby error
module Net
  class Conn
  end
  Socket = Net::Con          # !> `Socket = Net::Con`: Net::Con is not a type (a constant set to a constant is another name for a class or a module)
end
```

### Classes do not inherit

For any class, `class B < A` is shorthand for writing A's definitions in B. Afterwards A and B are unrelated types, and `A.f` takes only A's values ([Classes](07-classes.md)).

```ruby
class A
  attr_reader x
  def double(a) = @x * 2
end
class B < A
  attr_reader y
end
b = B.new(1, 2)
p(B.double(b))               # => 2
p((b in A))                  # => false
```

```ruby error
class A
  attr_reader x
  def double(a) = @x * 2     # !> A.x: argument 1 must be A, but is B [type]
end
class B < A
  attr_reader y
end
p(A.double(B.new(1, 2)))
```

### No `self`

`def self.x` is rejected, because Sake has no `self`. A `def x` inside a class already defines `A.x`.

```ruby error
class A
  attr_reader x
  def self.make = A.new(1)   # !> `def self.make` inside `A`: write `def make` (it defines A.make)
end
```

### One definition per name

Defining the same name twice in one namespace is an error. Redefining a built-in operation is also an error.

```ruby error
def f = 1
def f = 2                    # !> `f` is already defined at line 1
```

```ruby error
def String.upcase(s) = s     # !> `String.upcase` is a built-in operation and cannot be redefined
```

### No value constants

Constants can only be assigned from `Struct.new` (or `Exception.new`), or a type's name (a type alias, above). For a named value, define a function (`def pi = 3.14159`) and call it (`pi`). `PI = 3.14` is a static error whose hint gives that function, and each use of `PI` gets the hint to call `pi`. `once { ... }` computes a value once ([Built-in operations](09-builtins.md)).

```ruby
def pi = 3.14159
p(pi * 2)                    # => 6.28318
```

```ruby error
PI = 3.14                    # !> Sake has no value constants; a constant names a class (`Point = Struct.new(:x, :y)`, `Err = Exception.new(:msg)`), and a value goes in a function
p(PI * 2)                    # !> `PI` is not defined (Sake has no value constants)
```

## Qualified calls

`Type.op(args...)` calls the operation `op` of namespace `Type`. By convention the subject is the first argument. Both `Type` and `op` must exist, or the program is rejected before running. The error offers spelling suggestions and lists other namespaces that define `op`.

```ruby error
puts(String.upcse("a"))      # !> undefined function `String.upcse`
```

```ruby error
puts(Strng.upcase("a"))      # !> undefined type or module `Strng`
```

```ruby error
puts(Integer.upcase("a"))    # !> undefined function `Integer.upcase`
```

The hint of the last one is `` `upcase` is defined in `String.upcase`, `Symbol.upcase` ``.

## Chains and `_`

Two forms let a sequence of operations read from left to right, top to bottom. In both, the type of each operation is found on the same line.

### Chains

`x.T.f(args...)` is `T.f(x, args...)`: the step `.T` names a type or module, and the value to its left becomes the first argument. Steps can follow one another, also with the dot at the start of the next line.

A chain starts with `T.f(x)`. A step after it is written `.g` when the value has the same type, and `.U.g` when the type changes ([Steps without a type](#steps-without-a-type)). Starting from a variable, `x.T.f`, is allowed but not recommended: the type's name sits in the middle of the line and is hard to read, and the checker's hints do not suggest it.

```ruby
line = "a, b ,c"
r = String.split(line, ",")
  .Array.map { |s| String.strip(s) }
  .join("|")
p(r)                         # => "a|b|c"
```

### Steps without a type

A chain step written without a type continues with the type the chain names last. `String.strip(s).upcase` is `String.strip(s).String.upcase`, and the step after it continues with String too. The type is written only where the chain starts and where the value's type changes.

```ruby
xs = Any[3, 1, 2]
p(Array.sort(xs).reverse.first(2))           # => [3, 2]
p(String.strip("  ab ").upcase.reverse)      # => "BA"
words = Any["b", "a", "b"]
p(Array.tally(words).Hash.keys.Array.sort)  # => ["a", "b"]
```

In the last one, `Array.tally` gives a Hash, so the step names `.Hash.keys`, and `Hash.keys` gives an Array, so the next one names `.Array.sort`.

The type a step continues with is found by reading left on the same line, not chosen by the value's type. A variable or a literal receiver names no type, so `x.f` stays a static error.

- **If the type has no such operation**, it is a static error. The hint names the types that have one.
- **If the value is not of that type**, it is a type error, as `T.f(r)` would be. The hint names the value's type.

```ruby error
counts = Hash.of("a" => 2, "b" => 1)
p(Hash.sort_by(counts) { |k, n| n }.first)  # !> Hash.first: argument 1 must be Hash, but is Array
```

`Hash.sort_by` gives an Array, so the step names it: `.Array.first`.

`x.T` with no operation after it is a static error.

```ruby error
s = "x"
p(s.String)                  # !> `s.String` needs an operation after the type: `s.String.op(...)`
```

### `_`

`_` is the value of the **previous statement** in the same body (a function, a block, a branch, or the top level).

```ruby
line = "a, b ,c"
String.split(line, ",")
Array.map(_) { |s| String.strip(s) }
Array.join(_, "|")
p(_)                         # => "a|b|c"
```

`_` refers to a statement, not an expression, because a statement is unambiguous. An expression-level `_` would have to pick one of the sub-expressions of the line before, and there is no natural answer to which one. "The statement before" is one thing.

- **Not in a first statement.** Reading `_` in the first statement of a body, or right after a definition, is a static error.
- **Parentheses and interpolation.** In parentheses and in string interpolation, the first statement reads the `_` of the enclosing statement.
- **`_` as a name.** `_` may still be bound as a name to ignore (`|_, v|`, `a, _ = t`). But a `_` that names a local variable cannot be read.

```ruby
String.upcase("sake")
puts("got #{_}")             # => got SAKE
Any[1, 2]
p((Array.size(_) + 1))       # => 3
```

```ruby error
def f(x)
  p(_)                       # !> `_` is the previous statement's value, and no statement precedes it here
end
f(1)
```

```ruby error
a, _ = [1, 2]
p(_)                         # !> `_` is the previous statement's value, but here it names a local variable
```

## No calls on values

A call with a lowercase receiver, such as `x.op(...)`, `"lit".op`, or `3.times`, is a static error. The error suggests the qualified form, and when the receiver is a call that names no type (`f(x).op`) also the chain form (`f(x).T.op(...)`). A call on a call that names a type is not an error: it is a step that continues with that type.

```ruby error
s = " a "
puts(s.strip.upcase)         # !> method call on a value `s.strip.upcase` is not allowed
```

- **Chains** are rewritten as a whole: `s.strip.upcase` suggests `String.strip(s).upcase`, the first step as `T.f(x)`, and the steps after it without the type when it stays the same, with it when it changes.
- **Field access** gets the reader: `p.x` suggests `Point.x(p)`, and `p.x = v` suggests `p.Point.x = v`. With a setter function, write `Point.set_x(p, v)`.
- **Literal receivers** narrow the suggestions to the literal's type: `"lit".upcase` suggests only `String.upcase("lit")`.
- **`x.nil?`** suggests `x == nil`.

```ruby error
Point = Struct.new(:x, :y)
pt = Point.new(1, 2)
p(pt.x)                      # !> method call on a value `pt.x` is not allowed
```

```ruby error
3.times { puts("hi") }       # !> method call on a value `3.times` is not allowed
```

## Unqualified calls

A call without a receiver, `f(args)`, is resolved before running. The search goes in this order, and the first match wins.

1. The enclosing class or module, including its built-in operations, field readers and writers, and the functions it includes. Only the innermost one: inside `A::B`, a function of `A` is called as `A.f(...)` (names of types, unlike functions, are looked up outward).
2. Top-level functions.
3. `Kernel` (`puts`, `print`, `p`).

```ruby
def size(x) = "top-level size"
class Box
  attr_reader items
  def size(b) = Array.size(@items)
  def describe(b) = "#{size(b)} items"   # Box.size
end
b = Box.new(Any[1, 2])
puts(Box.describe(b))        # => 2 items
puts(size(b))                # => top-level size
```

- **Shadowing.** An inner definition shadows an outer one. It is not an error for the same name to exist at several levels.
- **The outer one, qualified.** An outer definition can always be reached with its namespace (`Kernel.puts`).
- **Top level.** Code at the top level has no enclosing namespace, so resolution starts at step 2.

```ruby
class Logger
  attr_reader prefix
  def puts(l, msg) = Kernel.puts("#{@prefix}#{msg}")
  def run(l) = puts(l, "start")          # Logger.puts
end
Logger.run(Logger.new("> "))             # => > start
puts("done")                             # => done
```

## include

`include M` in a class or module body copies the functions of the module `M` into the includer. This is Ruby's module, resolved statically.

```ruby
module Summary
  def total(x) = Array.inject(items(x), 0) { |a, v| a + v }
  def report(x) = "total=#{total(x)}"
end
class Basket
  include Summary
  attr_reader items
end
b = Basket.new(Any[1, 2, 3])
puts(Basket.report(b))       # => total=6
puts(Summary.total(b))       # => 6
```

### Copying

Each function `f` of `M` becomes `X.f` in the including namespace `X`. If `X` already has `f`, `X`'s own definition wins. Modules included by `M` are copied too.

```ruby
module A
  def bar(x) = "A"
  def baz(x) = "A"
end
class C
  include A
  attr_reader v
  def bar(x) = "C"
end
p(C.bar(C.new(1)))           # => "C"
p(C.baz(C.new(1)))           # => "A"
```

### Order

Modules are searched in Ruby's ancestor order: the module included **last** comes first. With `include A` then `include B`, a `bar` in both comes from B.

```ruby
module A
  def bar(x) = "A"
end
module B
  def bar(x) = "B"
end
class C
  include A
  include B
  attr_reader v
end
p(C.bar(C.new(1)))           # => "B"
```

### Resolution in the includer

Inside a copied function, unqualified names are resolved in `X`. So `M` can use functions that `X` provides, such as `each`. Nothing is dispatched at run time: each `X.f` is fixed before running. `@x` refers to a field of `X` when `X` is a class.

```ruby
module Named
  def label(x) = "<#{@name}>"
end
class Cat
  include Named
  attr_reader name
end
puts(Cat.label(Cat.new("tama")))   # => <tama>
```

### Requirements

The unqualified names that the bodies of `M`'s functions call (`each(x)`, say) and the fields `@x` they read, minus what `M` itself, the top level and `Kernel` define, are `M`'s requirements. Nothing is declared; they are collected from the bodies.

- **Missing in the includer.** If `X` lacks one (no `each`, no field `x`), `include M` is a static error. It names the function and what it needed.
- **Called directly.** Calling a function with requirements directly, as in `M.total(x)`, without going through an includer, is a static error too. The hint gives the call through an includer. This concerns a module function `total`; for a mixin function, `M.total(x)` is the dispatch of the next section.

```ruby error
module Summary
  def total(x) = Array.inject(items(x), 0) { |a, v| a + v }
end
class Empty
  include Summary            # !> `include Summary` in Empty: Summary.total needs `items`, which Empty does not define (used at line 2)
  attr_reader v
end
```

```ruby error
module Summary
  module_function
  def total(x) = Array.inject(items(x), 0) { |a, v| a + v }
end
class Basket
  include Summary
  attr_reader items
end
puts(Summary.total(Basket.new(Any[1])))   # !> Summary.total needs `items` from a namespace that includes Summary
```

### Restrictions

- Only modules can be included. Including a class is a static error.
- Include cycles are errors.
- **Not inheritance.** `include` adds no subtype relation. A value of a type that includes `M` does not become a value of `M`.

```ruby error
class A
  attr_reader x
end
class B
  include A                  # !> `include A`: A is not a module
  attr_reader y
end
```

## Calling a module's functions: module_function and dispatch

A name in a type or module is one function: Sake has no `def self.f` next to `def f`, because the "instance" form only means that the value comes first (`T.f(x, ...)`). Where Ruby has both a class method and an instance method of one name (`Net::HTTP.get(uri)` and `http.get(path)`), one of them takes another name, or one function tells the forms apart by the type of its first argument.

A module's functions are of two kinds, as in Ruby.

| Kind | How it is declared | `M.f(args)` |
|---|---|---|
| module function | after `module_function`, by `module_function :f`, or with `def M.f` | runs `M`'s own `f` (static) |
| mixin function | any other function of a module | **dispatches**: runs the `f` of the first argument's type, which must include `M` |

```ruby
module Describe
  def describe(x) = "#{prefix}#{name(x)}"   # a mixin function
  def prefix = "* "
  module_function :prefix                   # a module function
end
class Cat
  include Describe
  attr_reader name
end
class Dog
  include Describe
  attr_reader name
end
p(Describe.prefix)                          # => "* "
puts(Describe.describe(Cat.new("tama")))    # => * tama
puts(Describe.describe(Dog.new("pochi")))   # => * pochi
puts(Cat.describe(Cat.new("tama")))         # => * tama
```

### No subject

A mixin function cannot be called without arguments (`M.foo`), because there is nothing to dispatch on. Calling one when no type includes `M` is also a static error, with a hint to use `module_function`.

```ruby error
module Greeting
  def hello(x) = "hello"
end
puts(Greeting.hello(1))      # !> Greeting.hello is a mixin function, and no type includes Greeting
```

### Type check

The type inference checks that the first argument's possible types all include `M`, and reports one that does not as a `type` problem. While running, a type that does not include `M` raises `TypeError`.

```ruby error
module Describe
  def describe(x) = "#{name(x)}"
end
class Cat
  include Describe
  attr_reader name
end
Dog = Struct.new(:name)
def pick(flag) = flag ? Cat.new("tama") : Dog.new("pochi")
puts(Describe.describe(pick(true)))   # !> Describe.describe dispatches on its first argument, which can be Dog; the types that include Describe are Cat [type]
```

### Static calls

A type's own namespace is always static. In the example above, `Cat.describe(c)` runs Cat's `describe` (copied from `Describe`); `Describe.describe(x)` is the dispatching form.

### Kernel

Every type includes `Kernel`. `Kernel.to_s(x)` and `Kernel.inspect(x)` dispatch to a type's own `to_s` and `inspect`, or use the built-in form. `"#{x}"`, `puts`, and `p` are shorthands for them.

```ruby
class Temp
  attr_reader deg
  def to_s(t) = "#{@deg} C"
end
t = Temp.new(20)
puts(t)                      # => 20 C
puts("now #{t}")             # => now 20 C
puts(Kernel.to_s(t))         # => 20 C
p(Kernel.to_s(5))            # => "5"
```

### Required functions

A function of a module whose body is only `raise NotImplementedError` (with or without a message) is required: each type that includes the module defines it, and `M.f(x)` dispatches to that definition. Whether it takes a block comes from the types' definitions.

```ruby
module Shape
  def area(s) = raise NotImplementedError
  def describe(s) = "area=#{area(s)}"
end
class Sq
  include Shape
  attr_reader side
  def area(s) = @side * @side
end
puts(Shape.describe(Sq.new(3)))   # => area=9
puts(Shape.area(Sq.new(2)))       # => 4
```

- **A type that does not define it.** A type that includes the module without defining it is a `type` problem wherever a call can reach it, and `NotImplementedError` while running.
- **Not rescued.** `NotImplementedError` is a program error and cannot be rescued.
- **Message.** `raise T` with an exception type and no message gives the type's name as the message.

```ruby error
module Shape
  def area(s) = raise NotImplementedError
  def describe(s) = "area=#{area(s)}"   # !> Shape.area: Blob includes Shape but does not define area, which Shape requires (`raise NotImplementedError`) [type]
end
class Blob
  include Shape
  attr_reader v
end
puts(Shape.describe(Blob.new(1)))
```

## Listing the types: `(A|B).f(x)`

`(A|B).f(x, args...)` lists, on the operation itself, the types that `x` may have. While running, the type of `x` picks `A.f` or `B.f`; any other type raises `TypeError`. The result is the union of the results.

```ruby
Leaf = Struct.new(:weight)
Node = Struct.new(:weight, :kids)
def weight(t) = (Leaf|Node).weight(t)   # a field of the same name in two classes
p(weight(Leaf.new(2)))                  # => 2
p(weight(Node.new(5, Any[])))         # => 5
def size_of(x) = (String|Array|Hash).size(x)
p(size_of("abc"))                       # => 3
p(size_of(Any[1, 2]))                 # => 2
```

Two things are checked before running.

- **Every listed type has `f`.** Otherwise it is a static error.
- **No unlisted type arrives.** The checker reports a value of `x` whose type is not listed as a `type` problem.

```ruby error
def shout(x) = (String|Integer).upcase(x)   # !> Integer has no `upcase`, so `(String|Integer).upcase` cannot dispatch to it
p(shout("abc"))
```

```ruby error
def size_of(x) = (String|Array).size(x)   # !> (String|Array).size: argument 1 must be String or Array, but is Integer [type]
p(size_of(5))
```

- **The list names types.** Built-in types (`IO` among them) and classes, each at most once. `nil` cannot be listed; check for nil first.
- **The shapes may differ.** The listed functions may differ in shape: a user function's `*rest` is packed for its branch (`(IO|StringIO).print(io, a, b)`). A function that takes keywords cannot be listed; dispatch on the type with `case`.
- **Relation to mixin dispatch.** A mixin function's dispatch `M.f(x)` is the same selection as `(A|B|...).f(x)` listing every class that includes `M`. The difference is where the set is stated: `include` declares it on each class, `(A|B)` states it at the call. So nothing has to be declared, and it covers types that share an operation's name without sharing a module, such as built-in types or same-named fields.

## Arity and blocks

Since the callee is decided before running, the number of arguments and the presence of a block are checked before running.

- **Argument count.** Checked for both user functions and built-in operations.
- **A block to a user function.** A block is passed **iff** the function contains `yield` (or takes `&b`), except that a function that checks `block_given?` may be called without one.
- **A block to a built-in.** A built-in operation either requires a block or rejects one.

```ruby error
def area(w, h) = w * h
p(area(3))                   # !> wrong number of arguments for area (given 1, expected 2)
```

```ruby error
def twice(x) = x * 2
p(twice(3) { 1 })            # !> twice does not take a block (it has no `yield`)
```

```ruby error
p(Array.map(Any[1]))       # !> Array.map requires a block
```

### Splat arguments

`*xs` spreads a Tuple or an Array; anything else raises `TypeError`. It goes only into a rest parameter: a built-in's (`puts(*lines)`, `format(fmt, *row)`, `Any[*xs, 0]`, `Set.of(*xs)`, `Array.push(a, *xs)`) or a user function's `*rest` (`join(*parts)`, [Functions and blocks](04-functions.md)).

```ruby
def join(sep, *parts) = Array.join(parts, sep)
xs = Any["a", "b"]
p(join("-", *xs))            # => "a-b"
t = [1, 2]
p(Any[*t, 0])              # => [1, 2, 0]
puts(*xs)                    # => a
                             # => b
```

- **The arguments before it are written out.** So their count is still checked.
- **Static errors.** A splat into a function without `*rest`, or one that would fill a required or optional parameter, is a static error. So are splats into `Array.zip` / `Range.zip` (their Tuples are as long as the argument list) and `Hash.of(...)`.
- **In the typer.** A Tuple spreads position by position; an Array stands for any number of arguments of its element types.

```ruby error
def two(a, b) = a + b
t = [1, 2]
p(two(*t))                   # !> `*t`: splat arguments go only to a `*rest` parameter or to built-ins that take any number of arguments (puts, format, Any[...], Set.of, Array.push, ...)
```

# Values and types

Sake's values look much like Ruby's, but every value carries one type tag, and every operation checks it. This chapter lists the types and how each is written. Where the spelling is Ruby's but the meaning differs: `[a, b]` is a Tuple, `{x: 1}` is a Record, and `{}` and `{k => v}` are static errors.

| Type | How it is written (a literal, or the operation that makes it) | Notes |
|---|---|---|
| Integer | `42`, `-7` | arbitrary precision (the interpreter; the width is the implementation's: the C and Rust generators use 64 bits, and an overflow is an error) |
| Float | `1.5`, `2.0` | |
| String | `"abc"`, `'abc'`, `"a#{x}"` | interpolation uses `to_s` (see "Showing values") |
| true / false | `true`, `false` | internally one type, `Boolean`, which cannot be written in source |
| nil | `nil` | see "nil" below |
| Tuple | `[a, b, ...]` | length and positional types fixed at creation |
| Record | `{x: a, y: b}` | set of (field, type) pairs fixed at creation |
| Array | `Any[a, ...]` | no declared element type |
| Array of T | `T[a, ...]`, e.g. `Float[]`, `Point[p]` | element type T, checked on every write |
| Hash | `Hash.of("a" => 1, b: 2)`, `Hash.new(0)` | keys compare by value and type (Ruby's `eql?`) |
| Set | `Set.of(1, 2)` | elements compare by value and type (Ruby's `eql?`) |
| Symbol | `:name` | |
| Range | `1..5`, `1...5`, `1..` | ends are Integer, Float, String, or nil |
| Regexp, MatchData | `/(\d+)-(\d+)/`, `/#{x}/`, `String.match(s, re)` | |
| Rational, Complex | `2r`, `1/3r`, `Rational(1, 3)`, `2i`, `Complex(1, 2)` | Ruby's numeric tower |
| Time | `Time.now`, `Time.at(0)`, `Time.new(2026, 10, 1)` | |
| IO | `IO.stdout`, `File.open(path)` | one type for files and streams |
| Thread, Mutex, Queue, TCPServer, Socket | `Thread.new { }`, `Mutex.new`, ... | concurrency and networking |
| class | `Point.new(x, y)` | record with mutable fields ([Classes](07-classes.md)) |

- **Truthiness.** Only `nil` and `false` are falsy. Every other value, including `0` and `""`, is truthy.
- **Error messages.** `nil`, `true`, and `false` are shown as values (`got nil`). Every other value is shown by its type name (`got Integer`).

```ruby
p(0 ? "yes" : "no")            # => "yes"
p("" ? "yes" : "no")           # => "yes"
p(nil ? "yes" : "no")          # => "no"
p(false ? "yes" : "no")        # => "no"
```

## Showing values

Every value can be shown in two forms. `to_s` is the form for a reader, `inspect` the form that shows a programmer the kind of value as well; they are used as in Ruby.

| Form | Used by | Built-in form |
|---|---|---|
| `to_s` | `puts`, `print`, `"#{x}"`, `Array.join`, `format`'s `%s`, `:"#{x}"`, `/#{x}/` | as Ruby's `to_s`: `nil` shows as empty, Arrays and Tuples as `inspect` |
| `inspect` | `p`, `Kernel.inspect`, `format`'s `%p`, and elements inside an Array, Tuple, Hash, or Record | as Ruby's `inspect`: `#<struct Point x=1, y=2>` |

```ruby
Point = Struct.new(:x, :y)
puts("a")                      # => a
p("a")                         # => "a"
puts("#{nil}|")                # => |
puts("#{[1, "a"]}")            # => [1, "a"]
puts(format("%s %p", "a", "a"))  # => a "a"
p(Point.new(1, 2))             # => #<struct Point x=1, y=2>
```

### Your own form

A class can define its own `to_s` and `inspect` in its body. Each takes one required argument, the value, and must return a String. Parameters with defaults may follow (`to_s(n, base = 10)`).

```ruby
class Point
  attr_accessor x, y
  def to_s(p) = "(#{@x}, #{@y})"
  def inspect(p) = "Point(#{@x}, #{@y})"
end
pt = Point.new(1, 2)
puts(pt)                       # => (1, 2)
puts("at #{pt}")               # => at (1, 2)
p(pt)                          # => Point(1, 2)
p([pt])                        # => [Point(1, 2)]
```

A non-String result is reported before running as a `type` problem. If it is reached while running, it is a `TypeError`.

```ruby error
class Point
  attr_accessor x, y
  def to_s(p) = @x
end
puts(Point.new(1, 2))          # !> Point.to_s must return a String, but returns Integer [type]
```

- **Which one runs.** The set of types is closed, so which `to_s` runs is known whenever the value's type is.

## Tuples, Records, and arrays

The literals `[...]` and `{...}` have no operation with a type, so their **shape** fixes their type at creation: `[a, b]` is a Tuple and `{x: a, y: b}` is a Record. A growable collection is made by an operation with a type (`Any[...]`, `T[...]`, `Hash.of(...)`).

### What a literal is

- **Contents are mutable.** The contents of a literal may be replaced by values of the same type, and each write is checked. Tuples are written with `t[i] = v`. Records have no write syntax yet.
- **Length is fixed.** The length is part of the type, so an empty `[]` cannot grow.
- **nil.** A position created with `nil` has the type nil, and no other value can be stored there later. To leave room for a value, create it with a placeholder of the intended type, such as `0` or `""`.

```ruby error
xs = []
Array.push(xs, 1)              # !> `[...]` is a Tuple with a fixed length; for a growable Array, write `Any[...]`
```

```ruby error
t = [1, nil]
t[1] = 2                       # !> Indexable.[]=: the value must be Nil, but is Integer [type]
```

### Tuple

The literal `[a, b, ...]` is a Tuple. Its elements are read by multiple assignment, `x, y = t`, or by index, `t[0]`.

```ruby
t = [1, "a"]
x, y = t
p(x)                           # => 1
p(y)                           # => "a"
t[0] = 2
p(t)                           # => [2, "a"]
p(Tuple.size(t))               # => 2
p(Tuple.max([3, 9, 4]))        # => 9
```

- **Multiple assignment.** `x, y = t` takes a Tuple apart. It also takes an Array apart (`key, value = String.split(s, "=")`). As in Ruby, missing elements are nil and extra elements are dropped.
- **An element that may be missing.** A variable that may get one has the type `nil | T`, as `x[k]` does (the `index-nil` item, level 3).
- **Targets.** Elements and fields may be targets too: `a[i], a[j] = a[j], a[i]` swaps, and `@done, @rest = Array.partition(xs) { ... }` assigns to fields. Their receivers and indexes are evaluated first, then the right side.
- **The rest.** `first, *rest = xs` puts the remaining elements in a new Array. `a, *mid, z = xs` does the same, and `x, * = xs` drops them. Only a local variable takes the rest.
- **A splat inside `[...]`.** A Tuple's length must be known, so `[*xs, 1]` is rejected. `Any[*xs, 1]` makes an Array.
- **Indexing.** `t[0]` reads a position and `t[0] = v` replaces it with a value of the same type.
- **Operations.** `Tuple.size` and `Tuple.length` give the number of elements. `Tuple.max([a, b])` is Ruby's `[a, b].max`; the length is known, so the result is never nil.

```ruby
first, *rest = Any[1, 2, 3]
p(first)                       # => 1
p(rest)                        # => [2, 3]
key, value = String.split("a=1", "=")
p(value)                       # => "1"
a = Any[1, 2]
a[0], a[1] = a[1], a[0]
p(a)                           # => [2, 1]
```

### Record

The literal `{x: a, y: b}` is a Record: a small fixed group of values read by field name.

- **Type.** The type of a Record is its set of (field, type) pairs, for example `{x: Integer, y: Integer}`. Two Records with the same set have the same type.
- **Field order.** Order does not matter: `{y: 2, x: 1}` is printed as `{x: 1, y: 2}`, and the two are `==`.
- **Not a class value.** A Record is never a value of a class, even when the fields match: classes are nominal, and Record types are structural.
- **Reading fields.** Take fields apart with a pattern, `r => {x:, y: name}`. This binds the local `x` to field `x` and the local `name` to field `y`. Listing only some of the fields is allowed. `Record.to_h(r)`, `Record.keys(r)`, and `Record.values(r)` exist too.
- **A missing field.** It is reported before running at `type`. If it is reached while running, a missing field raises `KeyError`, and a value that is not a Record raises `TypeError`.
- **Restrictions.** Field names are written as labels (`x:`). An empty `{}` and `{key => value}` are static errors (a Hash is `Hash.of(...)`).

```ruby
Point = Struct.new(:x, :y)
r = {y: 2, x: 1}
p(r)                           # => {x: 1, y: 2}
p(r == {x: 1, y: 2})           # => true
p(r == Point.new(1, 2))        # => false
r => {x:, y: name}
p(x)                           # => 1
p(name)                        # => 2
p(Record.keys(r))              # => [:x, :y]
```

```ruby error
r = {x: 1}
r => {z:}                      # !> the pattern needs field `z`, but the value is {x: Integer} [type]
```

```ruby error
h = {"a" => 1}                 # !> `{"a" => ...}` is not a Hash in Sake: `{name: value}` makes a Record
```

### Array

`Any[a, ...]` creates an Array with no declared element type. Any value can be added to it. Arrays are mutable and shared by reference. `Any` is the prelude's name for Kernel (`Any = Kernel`): every type includes Kernel, so its Array takes every value. `Array[...]` is not this Array: like `T[...]` for every T below, it is an Array of Arrays.

### Array of T

`T[a, ...]` creates an Array whose element type is T, for every T. T is a built-in type (`Integer`, `Float`, `Rational`, `Complex`, `String`, `Symbol`, `Tuple`, and `Array`, `Hash`, `Set`, whose `[...]` makes an Array of Arrays, of Hashes, of Sets), a class, or a module. A Hash itself is made with `Hash.of(k => v)` or `Hash.new`, a Set with `Set.of(a, b)` or `Set.new(xs)`. A module's Array `M[...]` holds values of the types that include M, so a mixin function `M.f(x)` runs on each element.

- **Write checks.** Every write is checked: creation, `Array.push`, `Array.append`, and `Array.concat`. A mismatch raises `TypeError`. There is no implicit conversion, so an Integer cannot go into `Float[]`.
- **Static check.** If a literal argument of `T[...]` has another type, the error is reported before running. This catches `Point[1, 2]`, Ruby's spelling of `Point.new(1, 2)`.
- **Untyped results.** Arrays returned by `map`, `select`, `sort`, and similar operations have no declared element type.

```ruby error
fs = Float[1.5]
Array.push(fs, 2.5)
Array.push(fs, 3)              # !> Array.push: an element must be Float, but is Integer [type]
```

```ruby error
Point = Struct.new(:x, :y)
pt = Point[1, 2]               # !> Point.new(1, 2) creates one Point; Point[...] is an Array of Point
```

```ruby
Point = Struct.new(:x, :y)
pts = Point[Point.new(1, 2), Point.new(3, 4)]
xs = Array.map(pts) { |pt| Point.x(pt) }
Array.push(xs, "s")
p(xs)                          # => [1, 3, "s"]
```

```ruby
module Shape
  def area(s) = raise NotImplementedError
end
class Sq
  include Shape
  attr_reader n
  def area(s) = @n * @n
end
class Rect
  include Shape
  attr_reader w, h
  def area(s) = @w * @h
end
shapes = Shape[Sq.new(2), Rect.new(2, 3)]
p(Array.map(shapes) { |s| Shape.area(s) })   # => [4, 6]
```

### Ruby habits

- **`[]` where an Array is expected.** A Tuple or Record passed where an Array is expected, such as `result = []` followed by `Array.push(result, x)`, fails with a hint to write `Any[]` (the example above).
- **In-place operations that change the element type.** The checker gives a container one element type for the whole program. So after `Array.map!(xs) { |x| Integer.to_s(x) }` or `Hash.transform_keys!(h) { ... }` it takes the container to hold both the old and the new type, and reports operations that fit only one of them as `type` (partial). Use the in-place forms for a mapping within one type, and `Array.map` / `Hash.transform_keys` (a new container) to change it.

```ruby error
xs = Any[1, 2]
Array.map!(xs) { |x| Integer.to_s(x) }
p(Array.sum(xs))               # !> Array.sum: an element must be Integer|Float|Rational|Complex, but can be String [type]
```

## Hash and Set

A Hash and a Set are growable collections that find a key or an element by its value. The literal `{...}` is a Record, so both are made by an operation with a type.

### Constructors

`Hash.of(k => v, ...)`, `Hash.new`, and `Hash.new(default)` create a Hash; `Set.of(x, ...)` creates a Set. The keys of `Hash.of(name: v)` are Symbols.

```ruby
h = Hash.of("a" => 1, b: 2)
p(h)                           # => {"a" => 1, b: 2}
counts = Hash.new(0)
Array.each(String.split("the cat the", " ")) { |w| counts[w] += 1 }
p(counts)                      # => {"the" => 2, "cat" => 1}
Hash.each(counts) { |k, v| puts("#{k}: #{v}") }   # => the: 2
                                                  # => cat: 1
```

### Comparing keys and elements

Hash keys and Set elements compare as Ruby's `eql?` does: by value, with the type included. `1` and `1.0` are different keys, while `1 == 1.0` is true.

```ruby
p(Hash.of(1 => "a")[1.0])         # => nil
p(Set.of(1, 1.0))                 # => Set[1, 1.0]
p(1 == 1.0)                    # => true
```

- **Allowed.** Integer, Float, String, Symbol, true, false, nil, Time, and Tuples, Records, Arrays, Hashes, Sets, and values of a class made of these.
- **Not allowed (`TypeError`).** Regexp, Range, and values of a class that defines its own equality (`==`, or `Comparable` with `<=>`), because the key comparison could disagree with that equality.
- **Changing a key.** As in Ruby, changing an Array, Hash, Set, or class value after using it as a key makes it unfindable.
- **Keys are copied.** A Tuple or Record key is copied when it is stored, so a later write to the original does not change the key.

```ruby error
h = Hash.of(/a/ => 1)             # !> Regexp cannot be a Hash key or Set element
```

```ruby
k = [1, 2]
h = Hash.of(k => "a")
k[0] = 9
p(Hash.keys(h))                # => [[1, 2]]
```

### Default values

`Hash.new(default)` gives `default` for a missing key, as in Ruby, and the same object is shared, as in Ruby. The block form `Hash.new { ... }` is not available, because blocks are not values.

```ruby
h = Hash.new(Any[])
Array.push(h[:a], 1)
p(h[:b])                       # => [1]
```

```ruby error
h = Hash.new { |hh, k| 0 }     # !> Hash.new does not take a block
```

### Blocks and order

- **Blocks.** A block over a Hash receives `[key, value]` as one Tuple, so `|k, v|` takes it apart (the `Hash.each` example above).
- **Order.** Iteration follows insertion order, as in Ruby.

## nil

`nil` is an ordinary value. A value that may be absent has the type `nil | T`, and there is no Option wrapper. Before such a value goes to an operation that cannot take nil, a check removes nil from its type.

### Run time

An operation that receives `nil` where it needs T raises `TypeError ... got nil`. The interpreter then runs the static analysis, on the error path only, and adds hints naming the fields that may hold `nil` and the lines that store it.

```ruby error
h = Hash.of("a" => "x")
puts(String.upcase(h["b"]))    # !> TypeError: String.upcase: argument 1 must be String, got nil
```

### Narrowing

The static analysis narrows a **local variable** in the following places.

| Form | Where `x` is narrowed |
|---|---|
| `if x` / `while x` / `x && …` | non-nil in the branch taken when `x` is truthy |
| `x != nil` / `x == nil` | nil or non-nil in the matching branch |
| `!x` / `unless x` | the same, with the branches swapped (`if !x … else` is non-nil in the else) |
| `return unless x`, `next unless x`, `break unless x`, and other early exits | non-nil after the statement |
| `String.size(x)`, or any built-in operation taking `x` as an argument | after the call, a type that the operation accepts (it checks its arguments while running) |
| `x in T` / `x => T` / `case x in T` | the matching types ([Control flow and patterns](06-control.md)) |

```ruby
def find_even(xs) = Array.find(xs) { |x| x % 2 == 0 }
x = find_even(Any[1, 4])
if x
  p(x + 1)                     # => 5
end
p(x + 1) if x != nil           # => 5
def twice(xs)
  x = find_even(xs)
  return 0 unless x
  x * 2
end
p(twice(Any[1, 4]))          # => 8
p(twice(Any[1, 3]))          # => 0
```

A field read from a local variable (`Node.next(n)`, or `@next` in a function of the class) is narrowed too. But an operation that may change the field, between the test and the use, ends the narrowing there, because fields are mutable and another name may change them. Those operations are a field write, a call of a user function, a call that passes a block and `yield`, and an assignment to the variable. Inside a block, a field narrowed outside it is not narrowed.

```ruby error
class Node
  attr_accessor value, :next
end
def mk(v) = Node.new(v, nil)
def drop_next(n) = Node.set_next(n, nil)
a = mk(1)
Node.set_next(a, mk(2))
if Node.next(a)
  p(Node.value(Node.next(a)))  # narrowed
  drop_next(a)
  p(Node.value(Node.next(a)))  # !> Node.value: argument 1 may be nil (nil | Node) [nil]
end
```

When a call comes in between, copy the field into a local variable first, then test the local.

```ruby
class Node
  attr_accessor value, :next
end
def mk(v) = Node.new(v, nil)
a = mk(1)
Node.set_next(a, mk(2))
nx = Node.next(a)
if nx
  p(Node.value(nx))            # => 2
end
```

### `--strict`

Level 2 reports, before running, every operation that may receive an unchecked `nil`. Level 3 also covers results of `x[k]` ([Overview and running programs](01-overview.md)).

## Ruby's other types

Ruby's other types are made with the same spelling and give the same values as in Ruby. The differences are that operations are called with their type, and that there are no global variables (`$1`, `$stdout`).

### Range

Operations that iterate need a Range that starts with an Integer or a String. `"A".."Z"` walks with `String#succ`, as Ruby's does. `step`, `sum`, and `size` need an Integer. The checker reports a Range of another type (`1.0..2.0`) at `type`.

```ruby
p(Range.to_a("a".."e"))        # => ["a", "b", "c", "d", "e"]
p(Range.sum(1..4))             # => 10
p((1..3) == (1..3))            # => true
p(/a/i == /a/i)                # => true
```

```ruby error
Range.each(1.0..2.0) { |x| p(x) }   # !> Range.each: the Range's first value must be Integer|String, but is Float [type]
```

- **Equality.** Two Ranges are `==` when their ends and `exclude_end?` are; two Regexps when their source and options are.
- **Endless Ranges.** Operations that need a finite Range raise `RangeError` on an endless one.

```ruby error
p(Range.to_a(1..))             # !> RangeError: Range.to_a: cannot do this on an endless Range 1..
```

### Regexp and MatchData

Globals such as `$1` and `$~` do not exist; keep the MatchData in a variable and index it (`m[1]`). `String.match` gives nil when nothing matches, so check it first. `String.sub`, `gsub`, `index`, and `split` also take a Regexp. `/.../n` is a byte regexp, as in Ruby.

```ruby
m = String.match("2026-10", /(\d+)-(\d+)/)
if m
  p(m[1])                      # => "2026"
  p(m[2])                      # => "10"
end
p(String.sub("a-b", /-/, "+")) # => "a+b"
p(String.scan("a1b22", /\d+/)) # => ["1", "22"]
```

```ruby error
m = String.match("a1", /(\d)/)
puts($1)                       # !> Sake has no global variables (`$1`)
```

### Time

A zone is a fixed UTC offset, as Ruby's: `"+09:00"`, `"-0500"`, `"Z"`, `"UTC"`, a military letter, or seconds (`3600`). `Time.new`, `Time.at`, and `Time.now` also take it as the keyword `in:`. `Time.utc(t)` and `Time.localtime(t, zone)` give a converted copy (Ruby's `utc` and `localtime` change the receiver).

```ruby
t = Time.new(2026, 10, 1, 9, 0, 0, in: "+09:00")
p(t)                           # => 2026-10-01 09:00:00 +0900
p(Time.utc(t))                 # => 2026-10-01 00:00:00 UTC
p(t)                           # => 2026-10-01 09:00:00 +0900
p(Time.at(0, in: 3600))        # => 1970-01-01 01:00:00 +0100
```

### IO

`IO.stdin`, `IO.stdout`, and `IO.stderr` give the program's streams as values of type `IO`. Sake has no `$stdout` or `STDOUT`; as with `ARGV`, a value comes from an operation. `File.open(path, mode = "r")` gives an `IO` too; with a block it gives the block's value and closes the file after the block.

- **One type.** There is one type for files and streams, so one function can write to either.
- **Patterns and equality.** `IO` can be matched (`in IO`), and two IO values are `==` when they are the same stream or file.

```ruby
def say(io, s) = IO.puts(io, s)
say(IO.stdout, "to stdout")    # => to stdout
File.open("out.txt", "w") { |f| say(f, "to a file") }
p(File.read("out.txt"))        # => "to a file\n"
p(IO.stdout == IO.stdout)      # => true
```

### Encodings

Strings of incompatible encodings meeting (a byte from `Integer.chr(227)` next to UTF-8 text) raise `EncodingError`, which can be rescued.

```ruby
begin
  s = Integer.chr(227) + "あ"
rescue EncodingError => e
  puts("rescued")              # => rescued
end
```

# Functions and blocks

This chapter covers the functions defined with `def` and the blocks passed to calls. Neither carries a type. Since every call's callee is known before running, the number of arguments and the keyword names are checked before running.

## Functions

A function is defined with `def`, and its calls are checked against its parameter count and keyword names. Both the one-line `def f(x) = expr` and the form closed by `end` are available; as in Ruby style, the one-line form is for a short expression, and a body of several steps goes in `def ... end`.

```ruby
def area(w, h) = w * h
def describe(n)
  return "negative" if n < 0
  Integer.to_s(n)
end
p(area(3, 4))        # => 12
p(describe(-1))      # => "negative"
p(describe(7))       # => "7"
```

### Positional parameters and defaults

Required positional parameters come first, then optional ones with a default. As in Ruby, a default is evaluated at the call, after the earlier parameters, when the call does not give that argument. It may use the earlier parameters.

```ruby
def f(a, b = 1, c = b + 1) = [a, b, c]
p(f(10))             # => [10, 1, 2]
p(f(10, 5))          # => [10, 5, 6]
p(f(10, 5, 0))       # => [10, 5, 0]
```

- **Count.** A call gives between the required count and the full count of arguments. Any other count is an error before running.
- **Order.** Arguments are evaluated in the order written.

```ruby error
def area(w, h) = w * h
p(area(3))           # !> wrong number of arguments for area (given 1, expected 2)
```

### Keyword parameters

Keyword parameters come last in the parameter list. Each is required (`w:`) or has a default (`h: w`), and they mix freely with optional positional parameters.

```ruby
def greet(name, greeting = "Hello", punct: "!") = "#{greeting}, #{name}#{punct}"
p(greet("Ruby"))                     # => "Hello, Ruby!"
p(greet("Ruby", punct: "?"))         # => "Hello, Ruby?"
p(greet("Ruby", "Hi", punct: "."))   # => "Hi, Ruby."
punct = "!!"
p(greet("Sake", punct:))             # => "Hello, Sake!!"
```

Since the callee is known before running, the checker delivers `k: v` to its parameter by name. Each of the following is an error before running.

- An unknown keyword, a keyword given twice, or a missing required keyword.
- `k: v` to a function without keyword parameters. There is no implicit Hash argument: write `Hash.of(k: v)` for a Hash or `{k: v}` for a Record.

```ruby error
def size(w:, h: w) = w * h
p(size(w: 3, d: 2))  # !> size has no keyword parameter `d`
p(size(h: 3))        # !> size needs keyword argument `w:`
```

- **`f(k:)`** passes the variable `k`, as in Ruby.
- **Built-ins.** A few built-ins take Ruby's keywords too, checked the same way: `Time.at(t, in: "+09:00")`, `Dir.glob(pat, base: dir)`. The reference writes them as `[k: T]`.

### `*rest`: the remaining positional arguments

`*rest` after the optional parameters collects the remaining positional arguments in a new Array. A function with `*rest` takes any number of arguments. On the call side, `*xs` spreads a Tuple or an Array into the call.

```ruby
def join(sep, *parts) = Array.join(parts, sep)
p(join("-"))                  # => ""
p(join("-", "a", "b", "c"))   # => "a-b-c"
xs = ["x", "y"]
p(join("+", *xs))             # => "x+y"
```

- **Element type.** `rest` is one Array, so its elements share one type: the union of everything any call passes. To keep a type per position, pass one Tuple: call `notify("stock", ["AAPL", 120])` and take it apart inside with `name, price = event`.
- **Where it is built.** The Array is built at the call, since the callee is known.
- **Order.** The parameters go required, optional, `*rest`, keywords, `**opts`. A positional parameter after `*rest`, and a nameless `*`, are errors before running.
- **Where `*xs` may go.** `*xs` goes only to a `*rest` parameter or to a built-in that takes any number of arguments (`puts`, `format`, `Any[...]`, `Array.push`, ...). It cannot fill required or optional parameters.

```ruby error
def f(*rest, z) = z   # !> parameters after `*rest` are not supported: required, optional, `*rest`, keywords, `**opts`, in that order
```

### `**opts`: the remaining keywords

`**opts` at the end collects the keywords that are not parameter names in a Hash with Symbol keys. It too is built at the call.

```ruby
def tag(name, **opts) = [name, opts]
p(tag("div"))                            # => ["div", {}]
p(tag("div", id: "main", hidden: true))  # => ["div", {id: "main", hidden: true}]
```

- **Misspellings.** A function with `**opts` accepts any keyword, so a misspelled one is no longer an error there.
- **A nameless `**`** is an error before running.

### Passing keywords on: `g(**opts)`

`g(**opts)` passes the collected keywords on to a function `g` defined with `def`, as in Ruby. Each key goes to `g`'s keyword of that name, and `g`'s own `**` gets the rest.

```ruby
def parse(s, sep: ",", upcase: false)
  fields = String.split(s, sep)
  upcase ? Array.map(fields) { String.upcase(_1) } : fields
end
def read(s, **opts) = parse(s, **opts)
p(read("a;b", sep: ";"))                # => ["a", "b"]
p(read("a;b", sep: ";", upcase: true))  # => ["A", "B"]
```

- **Each key keeps its type.** The checker follows each key from the call that collected it, so here `sep` is a String and `upcase` a Boolean, not the union of both.
- **Only the function's own `**` parameter** can be passed on, not any Hash.
- **Checked like written keywords.** A key `g` has no keyword for, and a required keyword the keywords lack, are errors before running (at run time, Ruby's `ArgumentError`).
- **Keywords written beside it.** One written before `**opts` is a default that opts overrides (`parse(s, sep: ";", **opts)`); one written after overrides opts.
- **Changing opts first.** Once opts is written to (`Hash.delete(opts, :k)`, `opts[k] = v`), its keys are no longer followed one by one: each keyword gets the union of the values, and a key may be one `g` does not take.

```ruby error
def parse(s, sep: ",") = String.split(s, sep)
def read(s, **opts) = parse(s, **opts)
p(read("a;b", sepp: ";"))
```

The last line is reported at `**opts`: `parse: the keywords passed on with ** hold :sepp, which parse has no keyword parameter for`, with the hint `did you mean sep:?`.

### The block parameter `&b`

`&b` (or `&` alone) is a parameter whose only use is to pass the function's block on to another call. It is described under "Blocks".

### Agreement among a mixin function's definitions

A module's mixin function dispatches to the definition of its first argument's type ([Program structure and name resolution](02-program.md)). So the types' definitions must agree on the parameter counts, the keyword names, and on having `*rest` / `**opts`. A disagreement is an error before running, at the call.

```ruby error
module Shape
  def area(s) = raise NotImplementedError
end
class Sq
  include Shape
  attr_reader side
  def area(s) = @side * @side
end
class Rect
  include Shape
  attr_reader w, h
  def area(r, scale) = @w * @h * scale
end
p(Shape.area(Sq.new(2)))   # !> Shape.area dispatches to Rect.area, whose arguments or block differ from Shape.area
```

### Return value

A function's value is the value of its last expression, or of `return expr`. `return a, b` returns the Tuple `[a, b]`, so the caller can take it with `a, b = f(...)`.

```ruby
def minmax(xs) = return Array.min(xs), Array.max(xs)
lo, hi = minmax(Any[3, 1, 2])
p([lo, hi])          # => [1, 3]
```

### Polymorphism

Functions are polymorphic. Having no type annotations, a function works on any arguments its operations accept. A type error surfaces at the operation that fails: the checker reports that line, with a hint naming the call that reached it.

```ruby
def twice(x) = x + x
p(twice(2))          # => 4
p(twice("ab"))       # => "abab"
p(twice(1.5))        # => 3.0
```

```ruby error
def twice(x) = x + x   # !> Arithmetic.+: the operands are (:a, :a), which the left operand's type does not support
p(twice(:a))
```

### Local variables

Each function has its own scope. It has no access to the top-level locals.

```ruby error
count = 10
def bump = count + 1   # !> undefined local variable or function `count`
```

As in Ruby, reading a local whose assignment appears earlier in the text but has not run yet gives `nil`.

```ruby
def f
  x = 1 if false
  p(x)               # => nil
end
f
```

### Recursion

Recursion is allowed. A depth over 10,000 calls raises `SystemStackError`. `bin/sake` runs the program on a thread with a large stack, so that this limit applies rather than Ruby's stack. A long backtrace is shortened in the middle.

```ruby
def fact(n) = n <= 1 ? 1 : n * fact(n - 1)
p(fact(20))          # => 2432902008176640000
def down(n) = n == 0 ? 0 : down(n - 1)
p(down(9_000))       # => 0
```

```ruby error
def down(n) = n == 0 ? 0 : down(n - 1)
p(down(20_000))      # !> SystemStackError: stack level too deep
```

## Blocks

A block is passed to a built-in operation or to a user function that yields. Most iteration is written with block-taking operations such as `Array.each`.

```ruby
xs = Any[1, 2, 3]
p(Array.map(xs) { |x| x * 2 })      # => [2, 4, 6]
Hash.each(Hash.of(a: 1, b: 2)) do |k, v|
  puts "#{k}=#{v}"                  # => a=1
                                    # => b=2
end
Integer.times(3) { p it }           # => 0
                                    # => 1
                                    # => 2
```

### Not values

Blocks are second-class. They cannot be stored in a variable or returned from a function. `proc`, `lambda`, and `->` are not available.

```ruby error
f = proc { |x| x }   # !> undefined function `proc`
```

### Parameters

`|a, b|` lists plain names. `it` and `_1` … `_9` work as in Ruby.

```ruby
p(Array.map(Any[1, 2]) { _1 * 10 })   # => [10, 20]
p(Array.map(Any[1, 2]) { it + 1 })    # => [2, 3]
```

### Destructuring

When a block declares two or more parameters and receives a single Tuple or Array, its elements become the parameters. Missing ones are nil and extra ones are dropped, as in Ruby. A parameter can itself be taken apart with `( )`.

```ruby
pairs = Any[["a", 1], ["b", 2]]
Array.each(pairs) { |name, n| p("#{name}=#{n}") }              # => "a=1"
                                                               # => "b=2"
Array.each_with_index(pairs) { |(name, n), i| p([i, name]) }   # => [0, "a"]
                                                               # => [1, "b"]
p(Array.inject(pairs, 0) { |acc, (k, v)| acc + v })            # => 3
Array.each(Any[[1, 2]]) { |a, b, c| p([a, b, c]) }           # => [1, 2, nil]
```

### Rest parameter

`|a, *rest|` (and `|a, *rest, z|`, `|*all|`) collects the remaining arguments, or the remaining elements of a destructured Tuple or Array, in a new Array, as `a, *rest = x` does. `|*all|` alone does not destructure.

```ruby
Array.each(Any[[1, 2, 3]]) { |a, *rest| p([a, rest]) }      # => [1, [2, 3]]
Array.each(Any[[1, 2, 3]]) { |a, *rest, z| p([a, rest, z]) } # => [1, [2], 3]
Array.each(Any[[1, 2, 3]]) { |*all| p(all) }                # => [[1, 2, 3]]
```

### Parameter count

A block with no parameters ignores its arguments. Otherwise, a block called with the wrong number of arguments raises `ArgumentError` while running. With a rest parameter, only fewer arguments than the other parameters is wrong.

```ruby
Integer.times(2) { puts "hi" }      # => hi
                                    # => hi
```

```ruby error
Array.each_with_index(Any["a"]) { |x| p(x) }   # !> ArgumentError: block takes 1 parameter(s) but was given 2
```

### Scope

A block sees and can assign the enclosing local variables.

```ruby
sum = 0
Array.each(Any[1, 2, 3]) { |x| sum += x }
p(sum)               # => 6
```

### `yield` and optional blocks

`yield(args...)` calls the block given to the current function. A `yield` outside a function is a syntax error. A function that contains `yield` must get a block at every call.

```ruby error
def each_twice(xs)
  Array.each(xs) { |x| yield x; yield x }
end
each_twice(Any[1, 2])   # !> each_twice uses `yield` but no block is given
```

`block_given?` tells whether the current function got a block. A function that checks it may be called without one.

```ruby
def info(msg = nil) = puts(block_given? ? yield : msg)
info("plain")                 # => plain
info { "from block" }         # => from block
```

- **Branch analysis.** The checker knows for each call whether a block was given, so it analyzes only the branch taken. This holds for `block_given?` under `&&`, `||`, and `!` as well.
- **A `yield` that should be unreachable.** A `yield` reached without a block (or `&b` to a call that needs a block) is an error at the `type` level. While running it raises `LocalJumpError`, which cannot be rescued.

### Passing a block on: `&b`

`def f(xs, &b) = Array.map(xs, &b)` passes the function's own block to another call. `&` alone works too. `b` is used only as `&b`.

```ruby
def twice(xs, &b) = Array.map(xs, &b)
p(twice(Any[1, 2]) { |x| x * 2 })      # => [2, 4]
def each_pair(h, &) = Hash.each(h, &)
each_pair(Hash.of(a: 1)) { |k, v| p([k, v]) }   # => [:a, 1]
```

- **Calling without a block.** As in Ruby, a function that only passes its block on may be called without one. If nothing then reaches a call that needs a block, it is an error at the `type` level.
- **`break`.** A `break` in the passed block ends the call the block was written for (`twice(...)` above).

```ruby error
def twice(xs, &b) = Array.map(xs, &b)   # !> the block passed on is missing here, and this call needs one
p(twice(Any[1, 2]))
```

### `next`, `break`, and `return`

The three ways out are Ruby's.

| Form | What it ends | Value |
|---|---|---|
| `next [v]` | the current block call | `v` (default `nil`) is the block's value |
| `break [v]` | the call the block was given to | `v` (default `nil`) is that call's value |
| `return [v]` | the enclosing **function** | `v` is the function's value |

```ruby
xs = Any[1, 2, 3, 4]
p(Array.map(xs) { |x| next 0 if x % 2 == 0; x })   # => [1, 0, 3, 0]
p(Array.each(xs) { |x| break x if x > 2 })          # => 3
p(Array.each(xs) { |x| x })                         # => [1, 2, 3, 4]
def first_big(xs)
  Array.each(xs) { |x| return x if x > 2 }
  nil
end
p(first_big(xs))                                    # => 3
```

- **Inside a `while`.** Inside a `while` in the block, `break` leaves the `while`.
- **Type.** The checker adds the `break` values to the call's result type.

> [!NOTE]
> A block's parameters and locals belong to the enclosing function's frame. When a block is handed to a function that runs it **later, on a thread** (a concurrent-ruby style `post { ... }`), it reads the values as they are when the thread runs (the last value of a loop variable). In Ruby a block's parameters are fresh for each call. The difference is observable only with threads (see `TODO.md`).

## once

`once { ... }` gives the block's value. The block runs the first time that place in the program is reached, and later passes through it give the same value, for the whole program and every thread. It is Sake's way to compute a table or a named value once, since there are no value constants.

```ruby
def table = once { puts "computing"; Array.map(Any[1, 2, 3]) { |i| i * i } }
p(table)             # => computing
                     # => [1, 4, 9]
p(table)             # => [1, 4, 9]
Array.push(table, 16)
p(table)             # => [1, 4, 9, 16]
```

- **Shared.** The value is shared, as Ruby's constants are. As above, an Array kept by `once` can still be changed.
- **Re-entry.** A block that reaches its own `once` again while computing it is a program error: `SystemStackError`.
- **Type.** For the checks before running, its type is the union of the block's results over every call that may compute it.

```ruby error
def loop_once = once { loop_once }
p(loop_once)         # !> SystemStackError: once: the block reached its own once again while computing it
```

# Operators and indexing

This chapter covers the binary operators such as `a + b` and the index `x[k]`. Both are shorthands for a module's function, resolved by the type of the left operand (the receiver). A class gets operators and an index by including that module and defining the functions.

## Binary operators

Each operator belongs to a module, and `a OP b` is shorthand for calling that module's function: `a + b` is `Arithmetic.+(a, b)`. A module's function is resolved by the type of its first argument ([module functions and dispatch](02-program.md)), so the call runs `T.+(a, b)` where `T` is the type of `a`: an operator dispatches on its **left operand**. Error messages name the module's function (`Arithmetic.*: ...`).

```ruby
p(1 + 2)                  # => 3
p(Arithmetic.+(1, 2))     # => 3
p(Integer.+(1, 2))        # => 3
p("a" + "b")              # => "ab"
```

| Module | Operators | Built-in types that include it |
|---|---|---|
| `Arithmetic` | `+ - * / % **`, unary `-x` `+x` (`-@` `+@`) | Integer, Float, Rational, Complex, String, Time, Set |
| `Comparable` | `<=> < <= > >=` | Integer, Float, Rational, String, Time, Tuple, Array |
| `Bitwise` | `& \| ^ << >>`, unary `~x` | Integer, Set |
| `Indexable` | `[]`, `[]=` (see "Indexing") | Array, Hash, String, Tuple, MatchData |
| `Kernel` | `== != =~ !~` | every type |

### Explicit forms

`Arithmetic.+(a, b)` dispatches exactly as `a + b` does. `Integer.+(a, b)` calls Integer's `+` directly: `a` must be an Integer, and `b` may be any type that Integer's `+` accepts.

```ruby error
p(Integer.+("a", "b"))    # !> Integer.+: argument 1 must be Integer, but is String
```

### Giving your own type an operator

A class joins an operator by including the module and defining the operator in its body, such as `include Arithmetic` with `def +(a, b)`. With `include Comparable`, defining `<=>` is enough: `<`, `<=`, `>`, and `>=` come from it, and `Array.sort`, `min`, and `max` use that `<=>` too.

```ruby
class Money
  include Arithmetic
  include Comparable
  attr_reader cents
  def +(a, b) = Money.new(@cents + Money.cents(b))
  def <=>(a, b) = @cents <=> Money.cents(b)
  def to_s(m) = format("$%d.%02d", @cents / 100, @cents % 100)
end
a = Money.new(150)
b = Money.new(250)
puts(a + b)                                        # => $4.00
p(a < b)                                           # => true
puts(Array.join(Array.sort(Any[b, a]), " "))     # => $1.50 $2.50
```

When the left operand's type does not include the operator's module, or does not define the operator, it is a `type` problem before running and a `TypeError` while running.

```ruby error
Point = Struct.new(:x, :y)
p(Point.new(1, 2) + Point.new(1, 2))   # !> Arithmetic.+: Point does not include Arithmetic
```

### A built-in on the left: coerce

`2 + money` dispatches on Integer, whose table has no row for Money. A class that defines `coerce(b, a)`, giving a Tuple `[left, right]` as Ruby's protocol does, has the pair converted first and the operator run on it. With the `coerce` below, `2 + money` becomes `Money.new(200) + money`. The checker follows the conversion.

```ruby
class Money
  include Arithmetic
  attr_reader cents
  def +(a, b) = Money.new(@cents + Money.cents(b))
  def coerce(m, other) = [Money.new(other * 100), m]
end
m = Money.new(50)
p(2 + m)                  # => #<struct Money cents=250>
```

### Equality

`==` on a class's values compares the type and the fields, as Ruby's Struct does, unless the type defines its own `==`. `Array.include?`, `index`, and similar operations use the same equality.

```ruby
Point = Struct.new(:x, :y)
p(Point.new(1, 2) == Point.new(1, 2))   # => true
p(Point.new(1, 2) == Point.new(2, 1))   # => false
p(Point.new(1, 2) == "x")               # => false
p(Array.include?(Any[Point.new(1, 2)], Point.new(1, 2)))   # => true
```

A type that includes `Comparable` and defines `<=>` (and not `==`) is equal to a value of the same type when `<=>` gives 0, as Ruby's `Comparable#==`. A value of another type, such as nil, is never equal, and `<=>` is not called then. For the Money above, `a == Money.new(150)` is true and `a == nil` is false.

### The table for the built-in types

For the built-in types, the result type depends on both operand types, through a **closed table**.

| Operator | Rows |
|---|---|
| `+` | (Integer, Integer) → Integer; with a Float → Float; with a Rational (and no Float) → Rational; with a Complex → Complex; (String, String) → String |
| `-` `*` `/` `%` `**` | numeric pairs as for `+` |
| `*` | also (String, Integer) → String |
| `<` `<=` `>` `>=` `<=>` | numeric pairs, (String, String), (Tuple, Tuple), (Array, Array) → true/false (`<=>`: -1, 0, 1, or nil) |
| `==` `!=` | **any two values**: equal values of the same type (numbers compare across Integer, Float, Rational); values of different types are not equal, as in Ruby |
| `+` `-` | also (Array, Array) → Array (concatenation; difference) |
| `*` | also (Array, Integer) → Array (repetition) |
| `<` `<=` `>` `>=` `<=>` | also (Symbol, Symbol) |
| unary `-` `+` | Integer, Float, Rational, Complex → the same type |
| unary `~` | Integer → Integer |
| `&` `\|` `^` `<<` `>>` | (Integer, Integer) → Integer |
| `\|` `&` `-` | (Set, Set) → Set (union, intersection, difference) |
| `=~` | (String, Regexp), (Regexp, String) → Integer or nil; `!~` (String, Regexp) → true/false |
| `%` | (String, Integer / Float / String / Symbol / Tuple / nil / true / false) → String, Ruby's format (`"%d items" % 3`, `"%s-%s" % [a, b]`) |

```ruby
p("ab" * 3)                    # => "ababab"
p(Any[1, 2] + Any[3])      # => [1, 2, 3]
p(Any[1, 2, 3] - Any[2])   # => [1, 3]
p(Set.of(1, 2) | Set.of(2, 3))       # => Set[1, 2, 3]
p(5 & 3)                       # => 1
p(1 << 4)                      # => 16
p("abc" =~ /b/)                # => 1
p("abc" !~ /z/)                # => true
p("%s-%s" % ["a", "b"])        # => "a-b"
```

### No matching row

If no row matches the operand types, it is a `type` problem before running and a `TypeError` while running. The runtime message lists the rows that do exist. There is no implicit conversion.

```ruby error
p("a" - "b")   # !> Arithmetic.-: the operands are (String, String), which the left operand's type does not support
```

### Numbers

`/` and `%` on Integers follow Ruby, rounding toward the floor for negative numbers. Dividing an Integer by zero raises `ZeroDivisionError`; Float division by zero follows IEEE.

```ruby
p(7 / 2)        # => 3
p(-7 / 2)       # => -4
p(-7 % 2)       # => 1
p(7.0 / 2)      # => 3.5
p(1 / 2r)       # => (1/2)
p(1.0 / 0)      # => Infinity
```

- **Negative exponent.** `Integer ** negative Integer` raises `ArgumentError`, so that the type of `a ** b` does not depend on the value of `b`. Write `2r ** -1` for a Rational (`(1/2)`).
- **Complex.** A Complex has no ordering, so `<` and the like are not defined for it.

```ruby error
p(1 / 0)        # !> ZeroDivisionError: Arithmetic./: divided by 0
```

```ruby error
p(2 ** -1)      # !> ArgumentError: Arithmetic.**: Integer ** negative Integer (2 ** -1) is an error; for a Rational, write 2r ** -1
```

### Time

`Time ± number` gives a Time, and `Time - Time` gives a Float of seconds. Two Times compare.

```ruby
t = Time.at(0, in: "+00:00")
p(t + 60)                 # => 1970-01-01 00:01:00 +0000
p(Time.at(60) - t)        # => 60.0
p(t < Time.at(1))         # => true
```

### Rules of equality and ordering

Values of different types are never equal: `1 == :a` and `struct == "x"` are false, as in Ruby. A class's own `==` decides for its values. Only numbers compare across Integer, Float, and Rational.

```ruby
p(1 == 1.0)     # => true
p(1 == 1r)      # => true
p(1 == :a)      # => false
p("1" == 1)     # => false
```

Tuples, Arrays, Sets, Hashes, and Records are equal when their contents are. Two Records also need the same fields. Tuples and Arrays are ordered element by element, as Ruby's Arrays are.

```ruby
p([1, 2] == [1, 2])                 # => true
p([1, 2] == Any[1, 2])            # => false
p({x: 1} == {x: 1, y: 2})           # => false
p(Set.of(1, 2) == Set.of(2, 1))           # => true
p([1, 2] < [1, 3])                  # => true
p(Any[1, 2] <=> Any[1, 2, 0])   # => -1
```

When two elements cannot be compared, `<` and the like raise `ArgumentError` and `<=>` gives nil. Before running, the checker reports element types that cannot be compared.

```ruby error
p(Any["a", 1] < Any["a", "b"])   # !> Comparable.<: elements compared in order may be (Integer, String), which cannot be compared
```

### Not operators

- **`!x`** is `x ? false : true`. It works on any value and is not an operation of a type.
- **Compound assignment.** `x OP= e` means `x = x OP e`.
- **`&&` and `||`** short-circuit and return one of their operands, as in Ruby.

```ruby
p(!0)                  # => false
p(!nil)                # => true
x = 5
x += 2
p(x)                   # => 7
p(nil || "default")    # => "default"
p(1 && "second")       # => "second"
```

## Indexing

`x[k]` means `Indexable.[](x, k)`, and `x[k] = v` means `Indexable.[]=(x, k, v)`: they run the `[]` and `[]=` of `x`'s type.

```ruby
xs = Any[10, 20, 30]
p(xs[0])                  # => 10
p(xs[-1])                 # => 30
p(xs[1..])                # => [20, 30]
p(xs[0, 2])               # => [10, 20]
p(Indexable.[](xs, 1))    # => 20
xs[1] = 25
p(xs)                     # => [10, 25, 30]
```

The built-in types behave as follows.

| Receiver, index | `x[k]` | `x[k] = v` |
|---|---|---|
| Array, Integer | the element, or nil outside the Array | stores `v` (an Array of T checks `v`) |
| String, Integer | a one-character String, or nil outside the String | not available |
| Tuple, Integer | the element; outside the Tuple, `IndexError` | stores `v` if it has the type of that position |
| Array, Range / String, Range | the slice, or nil | not available |
| Array, Integer, Integer / String, Integer, Integer (`s[start, length]`) | the slice, or nil | not available |
| Hash, any key | the value, or the default (nil unless made by `Hash.new(default)`) | stores `v` |
| MatchData, Integer / String | the group, or nil | not available |

```ruby
s = "hello"
p(s[0])                   # => "h"
p(s[1..2])                # => "el"
p(s[1, 3])                # => "ell"
h = Hash.of("a" => 1)
p(h["a"])                 # => 1
p(h["z"])                 # => nil
p(Hash.new(0)["z"])       # => 0
m = String.match("2026-10", /(\d+)-(\d+)/)
if m
  p(m[0])                 # => "2026-10"
  p(m[2])                 # => "10"
end
```

### An index outside

Negative indexes count from the end, as in Ruby. A miss gives `nil`, as in Ruby, so the static type of `a[i]` is `T | nil`. A result used unchecked is reported by `index-nil` at level 3; level 2 lets it pass, and the program stops at that operation while running. `Array.fetch(a, i)` raises `IndexError` instead.

```ruby error
xs = Any[10, 20, 30]
p(xs[5] + 1)              # !> TypeError: Arithmetic.+: no implementation for (nil, Integer)
```

```ruby error
xs = Any[10, 20, 30]
p(Array.fetch(xs, 5))     # !> IndexError: Array.fetch: index 5 outside of array bounds: -3...3
```

A Tuple's length is part of its type, so an index outside it is an `IndexError`. With a constant index it is reported before running.

```ruby error
t = [1, "a"]
p(t[2])                   # !> Indexable.[]: the index is outside the Tuple [Integer, String]
```

### Writing

Writing past the end of an untyped Array fills the gap with nil, as in Ruby. An Array of T raises `IndexError` instead, because nil is not a T. Writing a value that is not a T is a `type` problem before running. A Tuple takes only a value of the position's type. A String cannot be written to.

```ruby
xs = Any[1]
xs[3] = 4
p(xs)                     # => [1, nil, nil, 4]
```

```ruby error
ys = Integer[1]
ys[3] = 4                 # !> IndexError: Array.[]=: index 3 is past the end of Integer[] (length 1); the gap would be nil
```

```ruby error
ys = Integer[1]
ys[0] = "a"               # !> Indexable.[]=: an element must be Integer, but is String
```

### Compound assignment

`x[k] OP= v` and `x[k] ||= v` evaluate `x` and `k` once. On a local, `y ||= v` assigns only when `y` is nil or false.

```ruby
h = Hash.new
h[:a] ||= Any[]
Array.push(h[:a], 1)
h[:a] ||= Any[]
p(h)                      # => {a: [1]}
y = nil
y ||= 5
y ||= 6
p(y)                      # => 5
```

### Giving your own type an index

A class joins with `include Indexable` and `def [](x, k)` / `def []=(x, k, v)`. With two indexes, `m[r, c]` calls `def [](m, r, c)` and `m[r, c] = v` calls `def []=(m, r, c, v)`.

```ruby
class Grid
  include Indexable
  attr_reader w, cells
  def [](g, r, c) = @cells[r * @w + c]
  def []=(g, r, c, v)
    @cells[r * @w + c] = v
  end
end
g = Grid.new(2, Any[0, 0, 0, 0])
g[1, 0] = 7
p(g[1, 0])                # => 7
p(Grid.cells(g))          # => [0, 0, 7, 0]
```

# Control flow and patterns

Sake's control flow is Ruby's. `if` and `while`, the modifier forms, an early `return`, and `case` are written as in Ruby. There are three differences. Iteration is written with block-taking operations such as `Array.each`, not with `for`. `case` branches on patterns with `in`, not with `when`. And a condition does more than test a value: it **narrows** the type the checker tracks for a variable. This narrowing is how a value that may be nil, or a union such as `Integer | String`, is used.

The `# => value` comments in this chapter are the real output of `bin/sake --strict=2`.

## Conditions

`if` / `elsif` / `else`, `unless` / `else`, and the ternary `c ? a : b` are as in Ruby. Each is an expression whose value is that of the branch taken. When no branch is taken, the value is `nil`.

```ruby
n = 5
r = if n > 10 then "big" elsif n > 3 then "mid" else "small" end
p(r)                                 # => "mid"
p(if n > 10 then "big" end)          # => nil
p(unless n > 10 then "not big" end)  # => "not big"
p(n > 3 ? "yes" : "no")              # => "yes"
```

### Truthiness

Only `nil` and `false` are false. Every other value is true, including `0`, `""`, and an empty Array. `&&`, `||`, and `!` are as in Ruby; `&&` and `||` return the last operand they evaluated.

```ruby
p(0 ? "truthy" : "falsy")            # => "truthy"
p("" ? "truthy" : "falsy")           # => "truthy"
p(nil ? "truthy" : "falsy")          # => "falsy"
p(nil || "default")                  # => "default"
p(0 && "zero is truthy")             # => "zero is truthy"
```

### Modifier forms

A condition may follow the statement: `stmt if c`, `stmt unless c`, `stmt while c`, and `stmt until c`.

```ruby
n = 5
puts("positive") if n > 0            # => positive
i = 0
i += 1 while i < 3
p(i)                                 # => 3
i -= 1 until i == 0
p(i)                                 # => 0
```

## Loops

`while` and `until` are as in Ruby. Inside a loop, `break` leaves the loop and `next` starts the next iteration. The value of a loop is `nil`, except that `break v` leaves it with the value `v`.

```ruby
i = 0
out = Any[]
r = while i < 10
  i += 1
  next if i % 2 == 0
  break if i > 7
  Array.push(out, i)
end
p(out)                               # => [1, 3, 5, 7]
p(r)                                 # => nil
```

### Iteration is a block-taking operation

`for` is not supported. To walk a sequence, write a block-taking operation such as `Array.each`, `Integer.times`, or `Range.each` ([Functions and blocks](04-functions.md)). Inside the block, `break v` ends the call the block was given to, with the value `v`; `next v` ends the current block call with `v`.

```ruby
xs = Any[3, 8, 12, 5]
big = Array.each(xs) { |x| break x if x > 10 }
p(big)                               # => 12
squares = Any[]
Integer.times(4) { |i| next if i == 0; Array.push(squares, i * i) }
p(squares)                           # => [1, 4, 9]
```

`loop { ... }` repeats until a `break`, and the value of the `break` is its value.

```ruby
i = 0
r = loop do
  i += 1
  break i * 10 if i == 3
end
p(r)                                 # => 30
```

### Forms that are not supported

`for` and `begin ... end while` are static errors.

```ruby error
for i in 1..3                        # !> `for` is not supported; iterate with an operation
  p(i)
end
```

## Leaving a function early

`return` ends the function at once. `return v` makes `v` the function's value, and `return a, b` returns the Tuple `[a, b]`. The modifier form `return v if c` is the way to dispose of a precondition before the main case. A `return` inside a block also returns from the enclosing **function**, not from the block ([Functions and blocks](04-functions.md)).

```ruby
def sign(n)
  return "negative" if n < 0
  return "zero" if n == 0
  "positive"
end
p(sign(-2))                          # => "negative"
p(sign(0))                           # => "zero"
p(sign(7))                           # => "positive"
```

## case / in

`case x` followed by `in P then ...` branches runs the first branch whose pattern matches. A branch's body may follow `then` on the same line or start on the next line. When no branch matches and there is no `else`, the result is the runtime error `NoMatchingPatternError`.

```ruby
def kind(v)
  case v
  in nil then "nothing"
  in true | false then "bool"
  in Integer | Float then "number"
  in String
    "text"
  end
end
p(kind(nil))                         # => "nothing"
p(kind(true))                        # => "bool"
p(kind(2.5))                         # => "number"
p(kind("s"))                         # => "text"
```

`case`/`when` is not supported. Ruby's `when` dispatches on the receiver's `===`; Sake has no `===`, and a match compares type tags and values. What can be written as a pattern is the table in the last section of this chapter, "Pattern matching".

```ruby error
x = 1
case x                               # !> `case`/`when` is not supported (Ruby's `===` dispatches on the receiver); match with `case x` / `in Type`
when 1 then puts("one")
end
```

## Narrowing by a condition

The checker infers the type of each local variable over the whole program. A condition **narrows** that type inside the branch taken: inside `if x`, `x` is not nil; inside `if x in Integer`, `x` is an Integer. This is how a value that may be nil (`nil | String`) or a union (`Integer | String`) is used without a `type` or `nil` report.

### Removing nil

A local variable `x` is narrowed in the following places (the same table as in the nil section of [Values and types](03-values.md)).

| Form | Where `x` is narrowed |
|---|---|
| `if x` / `while x` / `x && …` | non-nil in the branch taken when `x` is truthy |
| `x != nil` / `x == nil` | nil or non-nil in the matching branch |
| `!x` / `unless x` | the same, with the branches swapped (`if !x … else` is non-nil in the else) |
| `return unless x`, `next unless x`, `break unless x`, and other early exits | non-nil after the statement |
| `String.size(x)`, or any built-in operation taking `x` as an argument | after the call, a type that the operation accepts (it checks its arguments while running) |

```ruby
def greet(name = nil)
  if name
    "Hello, " + String.upcase(name)
  else
    "Hello"
  end
end
p(greet("ko1"))                      # => "Hello, KO1"
p(greet())                           # => "Hello"
```

Without the `if name`, the checker reports the call `greet()`. It analyzes a function for each call, so the report names the call that led there.

```ruby error
def greet(name = nil)
  String.upcase(name)                # !> String.upcase: argument 1 must be String, but is nil [type]
end
p(greet("ko1"))
p(greet())
```

An early exit is a common way to remove nil. `while x` narrows the same way.

```ruby
def size_or_zero(xs)
  x = Array.first(xs)
  return 0 unless x
  String.size(x)
end
p(size_or_zero(Any["abc"]))        # => 3
p(size_or_zero(Any[]))             # => 0
```

### When a field's narrowing ends

A field read from a local variable (`Node.next(n)`, `@next`) is narrowed to non-nil inside `if Node.next(n)` too. Fields are mutable, though, so an operation that may change the field between the test and the use ends the narrowing: a field write, a call of a user function, a call that passes a block and `yield`, and an assignment to the variable. Inside a block, a field narrowed outside it is not narrowed either. In those places, copy the field into a local variable first, then test the local.

```ruby
class Node
  attr_accessor value, :next
end
def next_value(n)
  nx = Node.next(n)
  if nx
    Node.value(nx)
  else
    0
  end
end
a = Node.new(1, nil)
b = Node.new(2, a)
p(next_value(b))                     # => 1
p(next_value(a))                     # => 0
```

```ruby error
class Node
  attr_accessor value, :next
end
def bump(n) = Node.set_value(n, Node.value(n) + 1)
def next_value(n)
  if Node.next(n)
    bump(n)
    Node.value(Node.next(n))         # !> Node.value: argument 1 must be Node, but is nil [type]
  else
    0
  end
end
p(next_value(Node.new(1, nil)))
```

### Narrowing to a type

`x in Integer` is true when `x` is an Integer, and inside that branch `x` is narrowed to Integer. `elsif` and `else` see the types that are left. The `in` branches of `case x` work the same way. A module name matches any type that includes it (`in Shape` is `in Circle | Square`), and a built-in exception type includes its Ruby subclasses (`in IndexError` also matches KeyError).

```ruby
def pick(flag) = flag ? 1 : "one"
def describe(x)
  if x in Integer
    x + 1
  elsif x in String
    String.size(x)
  else
    x
  end
end
p(describe(pick(true)))              # => 2
p(describe(pick(false)))             # => 3
```

### Which level reports it

A value that may be nil, passed to an operation without a check, is reported as the `nil` item at level 2 (`--strict`). The nil of a miss (`x[k]`, `Array.first`, `Hash.dig`, and the like) is the `index-nil` item at level 3. The levels and items are in [Overview and running](01-overview.md). A nil that was not reported still stops the program at run time, with a `TypeError` from the operation that received it.

## Pattern matching

`x in P` is true when `x` matches the pattern `P`. `x => P` asserts it: it raises `NoMatchingPatternError` when `x` does not match, and binds a Record pattern's fields. The `in P` branches of `case x` take the same patterns.

| Pattern | Matches |
|---|---|
| a type name: `Integer`, `String`, `Tuple`, `Hash`, `Point`, `Record`, `IO`, ... | a value of that type (its type tag) |
| `nil`, `true`, `false`, `1`, `"s"`, `:ok` | an equal value of the same type |
| `P \| Q` | either |
| `{x:, y: name}` | a Record with those fields; binds the locals `x` and `name` |
| `[P, Q]` | a Tuple of that length whose positions match `P` and `Q` (nested patterns allowed) |
| `x` (a bare name inside `[...]`, or alone) | anything; binds the local `x` |

Patterns outside this table (`*rest`, find patterns, pins `^x`, guards, and the `Integer => n` form) are not supported and are static errors ([Differences from Ruby](a1-ruby.md)).

### No dispatch

A match compares type tags and values. There is no `===`, so `case`/`when` is not supported. A type-name pattern looks at the value's type tag only; the B of `class B < A` does not match `A` ([Classes](07-classes.md)).

### Binding

Record and Tuple patterns take the elements out into local variables. `{x:}` binds field `x` to a local of the same name; `{y: name}` binds field `y` to the local `name`. `P => name` binds what the pattern `P` matched, narrowed as `P`: `in [Integer => code, String => body]`.

```ruby
pt = {x: 3, y: 4}
pt => {x:, y: why}
p(x)                                 # => 3
p(why)                               # => 4
pair = [1, "one"]
case pair
in [n, s] then p([s, n])             # => ["one", 1]
end
p((pair in [Integer, String]))       # => true
p((pair in [String, Integer]))       # => false
case pair
in [Integer => n, String => s] then p(String.upcase(s) * n)   # => "ONE"
end
```

### Narrowing

In `if x in Integer`, and in each `in` branch of `case x`, a local `x` is narrowed to the matching types. The `else` branch, and each later branch, sees the types that are left. This is how a union such as `Integer | String` is used without a `type` report ("Narrowing to a type" in the previous section).

```ruby
def pick(flag) = flag ? 1 : "one"
def f(x)
  case x
  in Integer then x + 1
  else String.size(x)
  end
end
p(f(pick(true)))                     # => 2
p(f(pick(false)))                    # => 3
```

### Exhaustiveness

A `case` without `else` that may leave a possible **type** unmatched is reported as `type`. The set of types is closed, so the checker can check this.

```ruby error
def show(x)
  case x                             # !> case/in: no `in` branch matches String [type]
  in Integer then "int"
  end
end
p(show(41))
p(show("abc"))
```

Symbol literals are tracked as values. When `op` only ever holds those literals, `case op in :add ... in :sub` is complete.

```ruby
def name(op)
  case op
  in :add then "plus"
  in :sub then "minus"
  end
end
p(name(:add))                        # => "plus"
p(name(:sub))                        # => "minus"
```

When literal branches may leave some **values** of an open type unmatched (some String, Integer, or a Symbol made at run time), the report is the `exhaustive` item (level 3). The program may well be correct, and `NoMatchingPatternError` still stops it if not. The next example runs at level 2 and is reported at `--strict=3`.

```ruby error
def name(op)
  case op                            # !> case/in: no `in` branch matches some values of String [exhaustive]
  in "add" then "plus"
  in "sub" then "minus"
  end
end
p(name("add"))
p(name(String.downcase("SUB")))
```

### Assertion: x => P

After `x => P`, a local `x` is narrowed to the matching types, as in an `in` branch. Inside `initialize`, a field `@x` of the new instance is narrowed the same way. It is how a fact such as "a port is an Integer" is written where the value is stored: `p => Integer`, then `@port = p`.

- A value that surely does not match is reported as `type` (level 1).
- A value that may not match (another type may come) is checked when it runs, and reported only as `exhaustive` (level 3). The failure is `NoMatchingPatternError`, which can be rescued, as in Ruby.
- `x => P` is itself a check for nil, so a value that may be nil is not reported. It is treated like `Array.fetch`, which checks for a missing index.

```ruby error
x = "abc"
x => Integer                         # !> `=> Integer`: the value is String, which does not match [type]
```

```ruby
def pick(flag) = flag ? 1 : nil
x = pick(true)
x => Integer
p(x + 1)                             # => 2
```

### Parentheses

As in Ruby, `x in P` must be in parentheses when it is an argument: `p((x in Integer))`. `x in T ? a : b` and `cond && x in T` also parse differently from what they look like; write `(x in T)`.

```ruby
x = 1
p((x in Integer))                    # => true
r = (x in Integer) ? "int" : "other"
p(r)                                 # => "int"
p(x > 0 && (x in Integer))           # => true
```

```ruby error
x = 1
p(x in Integer)                      # !> syntax error: unexpected 'in'; expected a `)` to close the arguments
```

# Classes

A type a program defines is a class. Declared with `attr_*` lines in a `class` or made with `Struct.new(:x, :y)`, it is the same thing: its values hold fields (and print as Ruby's Struct values, `#<struct Point x=3, y=4>`), and its operations are called with the type, `Point.norm2(pt)`. Classes do not inherit and values take no method calls, so what a Sake class shares with Ruby's is declaring a type and gathering its operations.

The `# => value` comments in this chapter are the real output of `bin/sake --strict=2`.

## Declaring a type: class and attr_*

`class C ... end` declares the type `C`. The `attr_*` lines of its body give the fields, and its `def`s give the operations. `C.new` makes a value; a field is read with the reader `C.x(c)` and written with the writer `C.set_x(c, v)`.

```ruby
class Account
  attr_reader owner
  attr_accessor balance
  def initialize(a)
    @balance = 0 if @balance == nil
  end
end
acct = Account.new("ko1")
p(acct)                              # => #<struct Account owner="ko1", balance=0>
p(Account.owner(acct))               # => "ko1"
Account.set_balance(acct, 100)
p(Account.balance(acct))             # => 100
rich = Account.new("you", 500)
p(Account.balance(rich))             # => 500
```

Every `class` is a type. Its fields are declared in the body of its first `class`; a later `class Account` adds functions only. The field names are written bare.

| Line | Meaning |
|---|---|
| `attr_accessor x, ...` | fields with a reader `C.x(c)` and a writer `C.set_x(c, v)` |
| `attr_reader x, ...` | the reader only; inside the type's functions, `@x = v` still writes them |
| `attr_writer x, ...` | the writer only; inside the type's functions, `@x` still reads them |
| `private attr_... x` | the reader and writer are for the class's own functions only; `new` still takes it |

A `private` field can be read and written by the class's functions on any of its values (`@x`, or `T.x(other)`). `T.x(c)` from outside is a static error.

```ruby
class Counter
  attr_reader label
  private attr_accessor n
  def bump(c) = @n += 1
  def same?(c, other) = n(c) == n(other)
end
c = Counter.new("hits", 0)
Counter.bump(c)
p(Counter.bump(c))                   # => 2
p(Counter.same?(c, Counter.new("x", 2)))   # => true
```

```ruby error
class Counter
  attr_reader label
  private attr_accessor n
end
c = Counter.new("hits", 0)
p(Counter.n(c))                      # !> field `n` of Counter is private (private attr_*)
```

### Field order and new

Fields are in the order written, across lines. That is the order of `C.new`'s arguments. `C.new` takes the fields positionally, whatever their access, and also by name: `Logger.new("app", level: :warn)` gives the first field by position and `level` by keyword.

- Prefer the names for a type with several fields: a positional `new` is not checked against the field order.
- An unknown name, and one field given twice, are static errors.
- Without `initialize`, every field must be given.
- With `initialize`, trailing fields may be left out, or skipped for a later keyword. A field left out is nil when initialize starts, and initialize sets it.

```ruby
class Logger
  attr_reader name, level
  def initialize(l)
    @level = :info if @level == nil
  end
end
a = Logger.new("app")
p(Logger.level(a))                   # => :info
b = Logger.new("app", level: :warn)
p(Logger.level(b))                   # => :warn
c = Logger.new(level: :debug, name: "db")
p(c)                                 # => #<struct Logger name="db", level=:debug>
```

```ruby error
class Logger
  attr_reader name, level
  def initialize(l)
    @level = :info if @level == nil
  end
end
Logger.new("app", lvl: :warn)        # !> Logger has no field `lvl`
```

### initialize

`def initialize(c)` in a class runs **after** `C.new` has stored the fields, with the new instance. As in Ruby, it is the place for first values (`@items = Any[]`), checks (`@port => Integer`), and conversions (`@celsius = Float(@celsius)`). `@level = :info if @level == nil` keeps a value that `new` gave.

- It takes exactly that one parameter, the new instance. Two or more is a static error.
- Calling `C.initialize` directly is a static error; `C.new` calls it.
- The checker analyzes initialize for each `C.new` call. There, `@x` reads the value that call gave, so a wrong argument is reported for that call. The fields hold what initialize leaves in them.

```ruby
class Temp
  attr_reader celsius, label
  def initialize(t)
    @celsius = Float(@celsius)
    @label = "C" if @label == nil
  end
end
t = Temp.new(21)
p(Temp.celsius(t))                   # => 21.0
p(Temp.label(t))                     # => "C"
p(Temp.new(1, "K"))                  # => #<struct Temp celsius=1.0, label="K">
```

```ruby error
class Box
  attr_reader value
  def initialize(b)
    @value => Integer                # !> `=> Integer`: the value is String, which does not match [type]
  end
end
p(Box.new(1))
p(Box.new("s"))
```

> [!WARNING]
> Ruby's `def initialize(x) = @x = x` is a static error in Sake: `x` is the new instance, not a value, so this would store the instance in its own field. `C.new(...)` has already stored the fields; initialize only checks or converts them.

```ruby error
class Box
  attr_reader value
  def initialize(x) = @value = x     # !> `@value = x` stores the new Box in its own field: `x` is the instance, not a value
end
```

### No default values

`attr_reader items = Any[]` is a static error. A field's first value is set in `initialize`: it is the one place that runs for each new instance, and it can read the other fields (`@len = String.bytesize(@src)`). The checker follows the fields through initialize, so a field that initialize always sets is not nil afterwards.

```ruby error
class Bag
  attr_reader items = Any[]        # !> a field has no default value; set it in initialize, which runs after Bag.new
end
```

```ruby
class Bag
  attr_reader items
  def initialize(b)
    @items = Any[] if @items == nil
  end
  def add(b, x) = Array.push(@items, x)
end
b = Bag.new
Bag.add(b, 1)
p(Bag.items(b))                      # => [1]
```

### A field named like a reserved word

A field named like a reserved word is declared as a Symbol: `attr_accessor :next`. Written bare it would not parse. Its reader is `Node.next(n)`, and `@next` inside the functions works as usual.

```ruby
class Node
  attr_accessor value, :next
  def last_value(n)
    nx = @next
    nx ? last_value(nx) : @value
  end
end
list = Node.new(1, Node.new(2, nil))
p(Node.next(Node.next(list)))        # => nil
p(Node.last_value(list))             # => 2
```

### One type per construction

For the checker, each `C.new` is its own type. Instances made at different places keep their own field types, so a `Heap` of Integers and a `Heap` of Strings do not mix. When one `C.new` sits in a function called with different argument types (`expect(42)` and `expect("x")`), the types are separate per call too. A generic wrapper (`Ok.new(yield(@value))` in `Result.map`) is one type per caller.

- Messages name such a type with its site (`Heap@L7`) only when the type has several.
- Values built from values (`Value.new(a + b)` inside a function taking Values) stay one type per place.

```ruby
class Heap
  attr_reader items
  def push(h, x) = Array.push(@items, x)
  def top(h) = Array.first(@items)
end
ints = Heap.new(Any[])
Heap.push(ints, 3)
words = Heap.new(Any[])
Heap.push(words, "job")
p(Heap.top(ints) + 1)                # => 4
p(String.upcase(Heap.top(words)))    # => "JOB"
```

### class B < A

`class B < A` is shorthand for writing A's definitions in B. It is not inheritance.

- A's fields come first, then B's.
- A's functions are B's too. Inside them, unqualified names and `@x` mean B's.
- A's `include`s are B's.
- B's own definition of a function wins over A's.
- Nothing relates A and B afterwards. A B is not an A: `A.f(b)` is a type error, and `b in A` is false.
- `<` takes any class of the program, also `Struct.new(...)` written in place (`class P < Struct.new(:x)`). A module is included with `include`.

```ruby
class Shape
  attr_reader name
  def describe(s) = "a shape called " + @name
end
class Circle < Shape
  attr_reader r
  def area(c) = 3.0 * @r * @r
end
class Square < Shape
  attr_reader side
  def describe(s) = "a square called " + @name
end
c = Circle.new("c1", 2.0)
p(c)                                 # => #<struct Circle name="c1", r=2.0>
p(Circle.describe(c))                # => "a shape called c1"
p(Circle.area(c))                    # => 12.0
p(Square.describe(Square.new("s1", 3.0)))   # => "a square called s1"
```

```ruby error
class Shape
  attr_reader name
  def describe(s) = @name            # !> Shape.name: argument 1 must be Shape, but is Circle [type]
end
class Circle < Shape
  attr_reader r
end
p(Shape.describe(Circle.new("c", 2.0)))
```

### Exception types

`class E < Exception` (or `< StandardError`) declares an exception type: `message` is its first field, then the fields of its `attr_*` lines. The fields after `message` may always be left out: `E.new("msg")` gives the message only. The form `raise E, "msg"` is for an exception type with no fields besides `message` ([Exceptions and errors](08-exceptions.md)).

```ruby
class ParseError < StandardError
  attr_reader line
end
begin
  raise ParseError.new("bad token", 7)
rescue ParseError => e
  p(ParseError.message(e))           # => "bad token"
  p(ParseError.line(e))              # => 7
end
begin
  raise ParseError.new("no line")
rescue ParseError => e
  p(ParseError.line(e))              # => nil
end
```

### Shorthands: Struct.new and Exception.new

`Struct.new(:x, :y)` is shorthand for `class C` with `attr_accessor x, y`. Ruby's form `class C < Struct.new(:x, :y)` also works: it declares those fields first, then the body's `attr_*` lines and functions. `Exception.new(:line)` is shorthand for `class C < Exception` with `attr_accessor line`.

```ruby
class Point < Struct.new(:x, :y)
  attr_reader label
  def to_s(pt) = @label + "(" + Integer.to_s(@x) + ", " + Integer.to_s(@y) + ")"
end
p(Point.to_s(Point.new(1, 2, "P")))  # => "P(1, 2)"
Oops = Exception.new(:line)
begin
  raise Oops.new("oops", 3)
rescue Oops => e
  p(Oops.line(e))                    # => 3
end
```

### Static errors in a declaration

Each of the following is a static error, and the checker's hint shows the fix.

- `attr_reader :x`: a field that is not a reserved word, written as a Symbol (write it bare).
- A field declared in a `class C` after the first one.
- A default value, `attr_reader x = v`.
- A write from outside the class to a read-only (`attr_reader`) field.
- The old form `class C < {reader: [...]}`; the hint gives the `attr_*` lines.

## The operations of Struct.new

```ruby
Point = Struct.new(:x, :y)
```

This defines the namespace `Point` with the following operations.

| Operation | Meaning |
|---|---|
| `Point.new(x, y)` | create; positional arguments, one per field |
| `Point.x(p)`, `p.Point.x` | read field `x` (the reader has the field's name) |
| `Point.set_x(p, v)`, `p.Point.x = v` | write field `x` in place; returns `v` |
| `p.Point.x += v`, `\|\|=`, `&&=` | `p.Point.x = p.Point.x + v` with `p` evaluated once |
| `Point[p1, ...]` | an Array of Point |
| `p == nil`, `p != nil` | comparison with nil |

```ruby
Point = Struct.new(:x, :y)
pt = Point.new(3, 4)
p(pt)                                # => #<struct Point x=3, y=4>
p(Point.x(pt))                       # => 3
p(Point.set_x(pt, 10))               # => 10
pt.Point.y += 1
p(pt)                                # => #<struct Point x=10, y=5>
pts = Point[Point.new(1, 2), Point.new(3, 4)]
p(pts)                               # => [#<struct Point x=1, y=2>, #<struct Point x=3, y=4>]
p(pt == nil)                         # => false
```

### Mutability

Values are mutable and shared by reference. A change made through one variable is visible through every other variable that holds the same value.

```ruby
Point = Struct.new(:x, :y)
pt = Point.new(3, 4)
q = pt
Point.set_y(q, 0)
p(pt)                                # => #<struct Point x=3, y=0>
```

### Adding operations

Add your own operations in `class Point ... end` or with `def Point.f`. Inside them, the readers and writers can be called unqualified (`x(p)`, `set_x(p, v)`). A function named like a field (`def x(p) = ...`) replaces its reader, and one named like its writer (`def set_x(p, v)`) replaces the writer, also for `p.Point.x = v` and `+=`, as a method after `attr_accessor` does in Ruby; `@x` still reads and writes the field.

```ruby
Point = Struct.new(:x, :y)
def Point.shift(pt, dx)
  @x += dx
  pt
end
class Point
  def norm2(pt) = @x * @x + @y * @y
  def x(pt) = 100
end
pt = Point.new(3, 4)
p(Point.shift(pt, 1))                # => #<struct Point x=4, y=4>
p(Point.norm2(pt))                   # => 32
p(Point.x(pt))                       # => 100
```

### Fields by name

`Point.field_names` is the Array of the fields' names as Symbols, in order (Ruby's `Struct.members`). `Point.read_field(p, name)` and `Point.write_field(p, name, v)` read and write the field a Symbol names, as `@x` does (Ruby's `p[name]` and `p[name] = v` on a Struct). Code that walks every field, such as a conversion to a Hash or from a row of a table, is written once with them.

```ruby
Point = Struct.new(:x, :y)
pt = Point.new(3, 4)
p(Point.field_names)                 # => [:x, :y]
p(Array.map(Point.field_names) { |f| Point.read_field(pt, f) })   # => [3, 4]
Point.write_field(pt, :y, 10)
p(Point.read_field(pt, :y) + 1)      # => 11
```

- A literal name is that field: `Point.read_field(pt, :y)` has the type of `y`, and `:z` is a static error ("Point has no field :z"). A Symbol narrowed to literals (`case name in :x | :y`) reaches those fields only.
- Any other Symbol reads the union of the fields' types, and writes the value into every field as far as the checker knows. To keep each field's type, write in a `case` over the names: `in :id then Post.write_field(post, name, Integer(v))`.
- A Symbol that names no field raises `KeyError`.
- Outside the class's functions, the fields a name may reach must have a public reader (`read_field`) or writer (`write_field`). Inside them, every field, private ones too; a base class's function (`class B < A`) reads B's fields with `read_field(b, :id)` even though A has no `id`.

### Field shorthand @x

Inside a function of a class (in `class Point` or `def Point.f`), `@x` means field `x` of the function's **first parameter**. The first parameter is the subject by convention.

| Written | Means |
|---|---|
| `@x` | `Point.x(p)` |
| `@x = v` | `Point.set_x(p, v)` |
| `@x OP= v` | `Point.set_x(p, Point.x(p) OP v)` |
| `@x \|\|= v` | `Point.x(p) \|\| Point.set_x(p, v)` |

- `p` is the first parameter's current value, even inside a block whose parameter has the same name.
- The usual runtime check applies: the first argument must be a Point.
- `@x` is a static error in each of these cases: outside a function of a class, in a function with no parameters, and when the field does not exist.

```ruby error
Point = Struct.new(:x, :y)
def far(pt) = @x > 100               # !> `@x` means a field of the first argument, so it is only available in a function of a class
```

### Printing

`p` prints a class instance as `#<struct Point x=1, y=2>`. `puts` prints it the same way.

### Restrictions of Struct.new

`Struct.new` must be assigned to a constant, at the top level or directly in a class or module body (`module Geo` with `Point = Struct.new(:x, :y)` makes `Geo::Point`). It takes symbols only, and no block; functions are defined in `class Point`.

There is no `Data.define`: it is rejected with a hint to use `Struct.new`. Ruby's `Data` is immutable, but Sake's named types are mutable, which is what Ruby's `Struct` provides.

```ruby error
Point = Data.define(:x, :y)          # !> Sake's named types are mutable, so they are made with Struct.new, not Data.define
```

### Copying: Kernel.dup

`Kernel.dup(x)` is a shallow copy: new containers and class instances, the same elements. A type that defines `dup` gets its own.

```ruby
Point = Struct.new(:x, :y)
a = Point.new(1, Any[2])
b = Kernel.dup(a)
Point.set_x(b, 9)
Array.push(Point.y(b), 3)
p(a)                                 # => #<struct Point x=1, y=[2, 3]>
p(b)                                 # => #<struct Point x=9, y=[2, 3]>
```

# Exceptions and errors

Exceptions in Sake are written as in Ruby: `raise` throws one, `begin`/`rescue`/`else`/`ensure` receives it, and `retry` starts over. Two things differ. An exception type is an ordinary class, and types are grouped by a module, not by inheritance. And the checker tracks, before the program runs, which exceptions may leave each function. The first half of this chapter is how to write exceptions; the second half lists the kinds of static and runtime error. The format of the reports is in [Overview](01-overview.md).

## Exceptions

```ruby
ParseError = Exception.new(:line)        # fields: message, line

def parse(s)
  raise ParseError.new("empty", 1) if s == ""
  Integer(s)
end

begin
  parse("")
rescue ParseError => e
  p(ParseError.line(e))                  # => 1
rescue KeyError, IndexError => e
  p(Exception.message(e))
rescue => e                              # any rescuable exception
  raise                                  # re-raise
else
  p(:ok)                                 # when nothing was raised
ensure
  p(:ensure)                             # => :ensure
end
```

The shape is Ruby's. The sections below go through declaring an exception type, the forms of `raise` and `rescue`, what the checker looks at, and what happens when nothing catches an exception.

### Declaring an exception type

An exception type is a class ([Classes](07-classes.md)). Its first field is `message`, followed by the fields you declare. As for any other class, `Name.new("msg", ...)`, `Name.message(e)`, `Name.field(e)`, and `@field` inside a function all work.

```ruby
class ParseError < Exception
  attr_reader line
end

e = ParseError.new("empty", 1)
p(ParseError.message(e))                 # => "empty"
p(ParseError.line(e))                    # => 1
p(e)                                     # => #<ParseError: empty line=1>
```

- **Two spellings.** `class Name < Exception` with `attr_reader field` lines, and its shorthand `Name = Exception.new(:field, ...)`, declare the same thing.
- **Grouped by a module.** `class B < A` is a copy, and `rescue A` does not catch B. To group exception types, include a module in them and write `rescue M` ([below](#grouping-with-a-module)). The built-in types follow Ruby's parents: `rescue IndexError` also catches `KeyError`, `rescue IOError` `EOFError`, `rescue RangeError` `FloatDomainError`, and `rescue ArgumentError` `Math::DomainError`. A class declared under a built-in exception, such as `class ConvError < RangeError`, is caught by `rescue RangeError` too.
- **Built-in exception types.** These are raised by built-in operations, and each has only `message`: `RuntimeError`, `ArgumentError`, `TypeError`, `KeyError`, `IndexError`, `ZeroDivisionError`, `RangeError`, `IOError`, `EOFError`, `RegexpError`, `FloatDomainError`, `EncodingError`, `ThreadError`, `NoMatchingPatternError`, `Math::DomainError`. Each is described in the reference's [Exceptions](ref/Exceptions.md).

### Forms of raise

`raise` is syntax, not an operation. It has four forms.

| Written | Raises |
|---|---|
| `raise "msg"` | `RuntimeError` |
| `raise T, "msg"` | `T.new("msg")`; T has no fields besides `message` |
| `raise value` | that exception value |
| `raise` (inside a rescue clause) | the exception being handled, again |

```ruby
begin
  raise "boom"
rescue => e
  p(e)                                   # => #<RuntimeError: boom>
end
begin
  raise KeyError, "no such key"
rescue KeyError => e
  p(e)                                   # => #<KeyError: no such key>
end
begin
  raise IndexError.new("out")
rescue IndexError => e
  p(Exception.message(e))                # => "out"
end
```

A type with fields besides `message` cannot be raised with `raise T, "msg"`. Build the value and write `raise T.new(...)`.

```ruby error
E = Exception.new(:code)
raise E, "msg"                           # !> E has fields besides message; raise it with `raise E.new(...)`
```

### rescue

`rescue A, B => e` catches the listed types. A name that is a module catches the exception types that include it, and a built-in parent (`IndexError` and the others) also catches its Ruby subclasses.

To catch everything, write `rescue => e`. `rescue StandardError => e` and `rescue Exception => e` mean the same. The type of `e` is then a union, so to handle each type differently, narrow it with `case e in A ...` ([Control flow and patterns](06-control.md)).

```ruby
def check(x)
  raise ArgumentError, "negative" if x < 0
  x
end
begin
  check(-1)
rescue ArgumentError, ZeroDivisionError => e
  v = case e
      in ArgumentError then "argument"
      in ZeroDivisionError then "zero"
      end
  p(v)                                   # => "argument"
end
```


### Grouping with a module

A Ruby library makes its exceptions children of `class Error < StandardError` and catches them together with `rescue MyLib::Error`. In Sake the parent is a module that each exception type includes. `rescue M => e` catches every exception type that includes M (also through a module that includes M), and `e` is their union. A function of the module, `M.f(e)`, dispatches to each type's definition.

```ruby
module ParseFailure
  def describe(e) = "parse: " + Exception.message(e)
end
class BadToken < StandardError
  include ParseFailure
end
class Unclosed < StandardError
  include ParseFailure
  attr_reader line
end
def parse(s)
  raise BadToken, "bad token" if s == "?"
  raise Unclosed.new("unclosed", 3) if s == "("
  s
end
begin
  parse("(")
rescue ParseFailure => e
  p(ParseFailure.describe(e))            # => "parse: unclosed"
  case e
  in Unclosed then p(Unclosed.line(e))   # => 3
  in BadToken then p("token")
  end
end
```

A module is not raised (`raise ParseFailure, "x"` is a static error). When the Ruby code raises the parent class itself, add one concrete type that includes the module. Level 1 stops a `rescue A` for a type an earlier `rescue M` already catches; a `rescue M` whose begin body raises none of the module's types is an `unused-rescue` warning.

```ruby error
module ParseFailure
end
class BadToken < StandardError
  include ParseFailure
end
def parse(s)
  raise BadToken, "bad token" if s == "?"
  s
end
begin
  parse("?")
rescue ParseFailure => e
  p(1)
rescue BadToken => e                     # !> rescue BadToken: an earlier rescue (ParseFailure) already catches it [rescue]
  p(2)
end
```

### Reading the message

`Exception.message(e)` reads the message of an exception value of any type. Use it when the type of `e` is a union. When the type is known, `ParseError.message(e)` does the same.

The message of an exception raised by a built-in operation is Ruby's text. The report of one that ends the program unrescued also names the operation (`ZeroDivisionError: Arithmetic./: divided by 0`).

```ruby
begin
  p(1 / 0)
rescue ZeroDivisionError => e
  p(Exception.message(e))                # => "divided by 0"
end
```

### Backtrace and cause

`Exception.backtrace(e)` gives the calls in effect when the exception was raised, as an Array of Strings in Ruby's form `"FILE:LINE:in 'FUNCTION'"`, innermost first. It is nil for an exception value that was never raised (made with `E.new` only). Raising it again with `raise e` or a bare `raise` keeps the backtrace of the first raise.

`Exception.cause(e)` gives the exception a rescue clause was handling when `e` was raised, or nil. Both may be nil, so under `--strict` check them before use.

Unlike Ruby, lines inside built-in operations (Ruby's `'Integer#/'` and the like) are not in the list.

```ruby
def parse(s)
  Integer(s)
rescue ArgumentError
  raise "not a number: #{s}"
end

begin
  parse("x")
rescue => e
  bt = Exception.backtrace(e)
  p(Array.map(bt) { |l| String.sub(l, /\A.*?:/, "") }) if bt   # => ["4:in 'parse'", "8:in '<main>'"]
  c = Exception.cause(e)
  p(c) if c                                # => #<ArgumentError: invalid value for Integer(): "x">
end
p(Exception.backtrace(RuntimeError.new("not raised")))   # => nil
```

### Program errors

`SystemStackError`, `LocalJumpError`, and `NotImplementedError` are program errors and cannot be rescued. Naming one in `rescue` is a static error.

```ruby error
begin
  p(1)
rescue SystemStackError => e             # !> SystemStackError cannot be rescued: it is a program error, which the checks before running report
  p(e)
end
```

`TypeError` (an operation given a value of the wrong type) and `NoMatchingPatternError` can be rescued, as in Ruby. Most of the type mistakes they report, however, are stopped by the checks before running ([Overview](01-overview.md)). The next example stops before running at the default level, as a `type` problem. Run with `--strict=0`, `len(1)` raises a `TypeError`, which is rescued like any other exception, and the message is `"argument 1 must be String, got Integer"`.

```ruby error
def len(x)
  String.length(x)                       # !> String.length: argument 1 must be String, but is Integer [type]
end
begin
  len(1)
rescue TypeError => e
  p(Exception.message(e))
end
```

### Other forms

`def f ... rescue ... end`, the modifier `expr rescue fallback`, and `retry` work as in Ruby. `ensure` runs once, when the begin block is left. It runs when `return` or `exit` leaves the block, too.

```ruby
def safe_div(a, b)
  a / b
rescue ZeroDivisionError
  0
end
p(safe_div(6, 0))                        # => 0

n = Integer("x") rescue -1
p(n)                                     # => -1

tries = 0
begin
  tries += 1
  raise "again" if tries < 3
rescue RuntimeError
  retry
end
p(tries)                                 # => 3
```

### Checking the flow of exceptions

The checker tracks which explicitly raised types may leave each function. Two items use this (the levels are in [Overview](01-overview.md)).

- **`unused-rescue` (a warning at levels 1 to 4; level 0 runs no type inference).** The checker reports a rescue clause for a type that the begin body never raises. Built-in kinds such as `ZeroDivisionError` can come from ordinary operations, so they are always assumed possible and never reported. The program still runs: a defensive `rescue` is normal, and whether anything raises depends on the whole program (a library's `rescue` for what only some apps raise). `--strict=2,unused-rescue` makes it an error. The related `rescue` item (level 1) stops a clause an earlier clause already covers.
- **`unrescued` (level 4).** The checker reports a `raise` that may reach the top level without being rescued, as `raise: RuntimeError may reach the top level without being rescued [unrescued]`.

```ruby
ParseError = Exception.new(:line)
begin
  p(Integer("1"))                        # => 1
rescue ParseError => e                   # warning: the begin body never raises ParseError [unused-rescue]
  p(e)
end
```

### Uncaught exceptions

An exception nothing rescues ends the program in the same form as a runtime error: `FILE:LINE: in FUNCTION: ParseError: message`. The exit status is 1.

```ruby error
def check(x)
  raise ArgumentError, "x must be positive" if x <= 0   # !> ArgumentError: x must be positive
  x
end
check(0)
```

### Deadlock

When every thread is waiting, the program ends with Sake's `ThreadError`. A `Queue.pop` with nothing left to push, a `Mutex.synchronize` taken again inside itself, and `Thread.join`s waiting for each other are the cases ([Built-in operations](09-builtins.md)).

```ruby error
q = Queue.new
Queue.pop(q)                             # !> ThreadError: deadlock: every thread is waiting
```

## Kinds of static error

Static errors are reported all together, sorted by position, and nothing runs. The exit status is 2. The format is in [Overview](01-overview.md).

| Kind | Example |
|---|---|
| syntax error | anything Prism cannot parse |
| undefined type, operation, or function | `String.upcse(s)` |
| wrong argument count | |
| block mismatch | a block passed where none is taken, or missing where one is required |
| a call on a value | `name.upcase` |
| forbidden construct | see below |
| unsupported syntax | appendix [Differences from Ruby](a1-ruby.md) |
| duplicate definition | |
| literal type mismatch in `T[...]` | `Integer["a"]` |
| an item selected by `--strict` | by default, a value whose type does not fit |

The forbidden constructs are `send`, `public_send`, `__send__`, `method_missing`, `define_method`, the `eval` family, `instance_variable_get`/`set`, `const_get`/`set`, `binding`, `self`, and `@x` outside a function of a class. Each either leaves the callee undecided until run time or names a concept Sake does not have (instance state).

## Kinds of runtime error

A runtime error stops the program at the line of the operation. The exit status is 1. Each is an exception, so it can be rescued. The exceptions are `SystemStackError` and the deadlock detected when every thread is waiting (the `ThreadError` of re-entering `Mutex.synchronize` can be rescued).

| Kind | Raised by |
|---|---|
| `TypeError` | an operation given a value of the wrong type (see below) |
| `IndexError` | an index outside the bounds (see below) |
| `ArgumentError` | block parameter count; negative sizes; `Integer ** negative`; comparing incomparable values in `sort` |
| `ZeroDivisionError` | Integer `/` or `%` by zero |
| `KeyError` | `Hash.fetch` of a missing key; a Record pattern naming a missing field |
| `RangeError` | an operation that needs a finite Range, given an endless one |
| `RegexpError` | `Regexp.new` with an invalid pattern |
| `IOError` | a file, directory, or socket operation failing (see below) |
| `EOFError` | raised by no built-in (see below) |
| `EncodingError` | an operation on Strings whose encodings do not fit |
| `FloatDomainError` | converting NaN or Infinity to Integer |
| `Math::DomainError` | e.g. `Math.sqrt(-1)` |
| `SystemStackError` | recursion deeper than 10,000 |
| `ThreadError` | deadlock |

- **`TypeError`.** An operation received a value of the wrong type. An operator not supported by the left operand's type. A write of another type into a typed Array. Multiple assignment from a value other than a Tuple or an Array.
- **`IndexError`.** A Tuple index outside the Tuple. `Array.fetch` outside the Array. Writing past the end of an Array of T (`xs[5] = 3`; the gap would be nil).
- **`IOError`.** `File.read`, `Dir.mkdir`, and the like failing. In Ruby these are `Errno::ENOENT` and co., and the message is Ruby's. Socket errors (refused, reset, unknown host) are `IOError` too.
- **`EOFError`.** No built-in raises it. A read at the end gives nil or `""`, and Ruby's EOFError is folded into `IOError`. The type exists, so it can be named in `rescue`.

```ruby error
h = Hash.of("a" => 1)
p(Hash.fetch(h, "b"))                    # !> KeyError: Hash.fetch: key not found: "b"
```

# Built-in operations

There are about 550 built-in operations, and their names are those of Ruby's core library. This chapter is a guide by namespace: what each type is for, the two or three operations you reach for first, a short example, and a link to the chapter with the details. **Every operation's exact signature, arguments, nil cases, exceptions, and examples are in Part 2, the built-in reference, one chapter per type** ([Array](ref/Array.md), [String](ref/String.md), [Hash](ref/Hash.md), [Kernel](ref/Kernel.md), ...).

The table in each section groups the operations by what you want to do; the "Result" column gives the result type. Operations marked "block" require one. `[x]` is an optional argument. The rules for operators (`+`, `<`, `[]`) are in [Operators and indexing](05-operators.md); the properties of each type's values are in [Values and types](03-values.md).

## Kernel

Kernel is the set of operations callable by bare name from anywhere in the program. You write `puts(x)`, `Integer(s)`, `loop { }`, and need not write `Kernel.puts(x)`. The first ones you use are `p` and `puts` for output, `Integer(s)` to convert a string to a number, and `loop` to repeat. Details: [Kernel](ref/Kernel.md).

```ruby
puts("total:", 3)
# => total:
# => 3
p(format("%05.1f", 3.14159))     # => "003.1"
p(Integer("42") + 1)             # => 43
x = loop { break 7 }
p(x)                             # => 7
p(Math.PI > 3)                   # => true
```

| Operation | Result | Notes |
|---|---|---|
| `puts(*xs)` | nil | prints like Ruby's `puts`; Arrays and Tuples print one element per line |
| `print(*xs)` | nil | no newline |
| `p(x)`, `p(x, y)`, `pp(x)` | x · `[x, y]` · x | prints each argument in Ruby's `inspect` format, one per line; `p()` gives nil |
| `format(fmt, *xs)`, `sprintf` | String | Ruby's format; arguments are Integer, Float, String, Symbol, nil, true, false |
| `Integer(x)`, `Float(x)`, `Rational(a, [b])`, `Complex(re, [im])` | the number | Ruby's strict conversions; `ArgumentError` on bad input |
| `rand`, `rand(n)`, `srand(seed)` | Float, or Integer/Float below n | |
| `sleep(secs)` | Integer | |
| `loop { }` | the value of a `break` | runs the block until a `break` |
| `dup(x)` | a copy of x | Ruby's `obj.dup`; a type that defines `dup` gets its own |
| `gets` | String or nil | a line of standard input |
| `exit(status)` | | Integer, or true for 0 / false for 1 (default 0); `ensure` clauses run; code after it is not reached |
| `warn(x, ...)` | nil | to the error stream, as `puts` does to the output |
| `system(cmd)` | true / false / nil | Ruby's `system` |
| `at_exit { }` | nil | a block run when the program ends |
| `ARGV` | Array of String | the program's arguments; written like Ruby's constant, but an operation (`Kernel.ARGV`); every use gives the same Array, so `Array.shift(ARGV)` works |
| `Kernel.PROGRAM_NAME` | String | the running file's name; unlike `ARGV` it cannot be written bare (a capitalized name is a type) |
| `__FILE__`, `__LINE__`, `__dir__` | String, Integer, String | this file (the main file as given, a required file's full path), this line, and this file's directory as a full path, as Ruby's: `File.join(__dir__, "views")` |
| `equal?(a, b)` | true/false | identity |
| `block_given?` | true/false | whether the current function got a block |

- **Constants are operations.** `Math::PI`, `Math::E`, `Float::INFINITY`, `Float::NAN`, `Float::EPSILON`, `Float::MAX`, `Float::MIN` are read as operations (`Math.PI`), as `ARGV` is. Sake has no value constants, and no other built-in constants (a nested name such as `A::B` is a namespace, never a value).

## Integer / Float

Integer is an integer without an upper bound; Float is a double-precision floating-point number. Arithmetic and comparison are written with operators; conversions such as `Integer.to_s(n, 16)` and iterations such as `Integer.times(n) { }` are called with the type. The first ones you use are `/` and `%` (integer division between Integers), `Float.round`, and `Integer.times`. Details: [Integer](ref/Integer.md), [Float](ref/Float.md), [Arithmetic](ref/Arithmetic.md).

```ruby
p(7 / 2)                         # => 3
p(7.0 / 2)                       # => 3.5
p(Integer.divmod(7, 2))          # => [3, 1]
p(Integer.to_s(255, 16))         # => "ff"
p(Float.round(3.14159, 2))       # => 3.14
Integer.times(3) { |i| print(i) }
puts                             # => 012
```

| Operation | Result |
|---|---|
| `+ - * / % **` | Integer (Float with a Float) |
| `< <= > >= == !=` | true/false |
| `Integer.to_s(n, base = 10)`, `to_f`, `abs`, `succ`, `pred` | String, Float, Integer, Integer, Integer |
| `even?`, `odd?`, `zero?` | true/false |
| `times(n) { \|i\| }` · `upto(a, b) { }` · `downto(a, b) { }` · `step` | n · a · a (block) |
| `& \| ^ << >>` | Integer |
| `divmod(a, b)` | `[quotient, remainder]` Tuple (`[Integer, Float]` for a Float) |
| `gcd`, `lcm`, `pow(a, b, [mod])`, `bit_length`, `sqrt(n)`, `clamp(n, lo, hi)` | Integer |
| `digits` · `chr` · `between?(n, lo, hi)` | Array of Integer · String · true/false |
| `Float.to_i`, `floor`, `ceil`, `truncate` · `round(f)` / `round(f, digits)` | Integer · Integer / Float |
| `nan?` · `finite?` · `infinite?` | true/false · true/false · 1, -1, or nil |
| `Float.to_r`, `rationalize` | Rational |

- **Rounding any real number.** `Arithmetic.round(x)`, `floor`, `ceil`, `truncate` (with an optional digit count), `abs`, `to_f`, `to_i`, `zero?` take any real number (Integer, Float, Rational), as Ruby's `x.round` does; the result's type follows x's (round without digits gives an Integer). `Float.round` names a Float only.

## String

A String is a sequence of characters with an encoding, and it is mutable. An operation whose name ends in `!` changes the subject in place; the others return a new String. The first ones you use are `String.split`, `String.sub`/`gsub`, `String.include?`, and `+` to concatenate. Details: [String](ref/String.md).

```ruby
s = "Hello, World"
p(String.length(s))              # => 12
p(String.upcase(s))              # => "HELLO, WORLD"
p(String.split(s, ", "))         # => ["Hello", "World"]
p(String.sub(s, "World", "Sake"))   # => "Hello, Sake"
p(String.include?(s, "World"))   # => true
p(String.index(s, "x"))          # => nil
p(s + "!")                       # => "Hello, World!"
```

| Purpose | Operation | Result |
|---|---|---|
| concatenate, repeat, format | `+(a, b)`, `*(s, n)`, `%` | String |
| compare | `== != < <= > >=` | true/false |
| length and numbers | `length`, `size`, `count(s, chars)`, `to_i(s, base = 10)` · `to_f` | Integer · Float |
| transform | `upcase`, `downcase`, `capitalize`, `swapcase`, `reverse`, `strip`, `lstrip`, `rstrip`, `chomp`, `chop`, `to_s` | String |
| replace | `sub(s, pat, repl)`, `gsub(...)` | String; `sub!`, `gsub!` in place |
| pad and swap characters | `ljust(s, n, [pad])`, `rjust`, `center` · `tr(s, a, b)`, `delete(s, chars)`, `squeeze(s, [chars])`, `succ`, `next` | String |
| query | `empty?`, `include?(s, t)`, `start_with?`, `end_with?`, `match?(s, re)`, `casecmp?` | true/false |
| split | `chars`, `lines`, `bytes`, `split(s, [sep, [limit]])`, `scan(s, re)` | Array |
| positions | `index(s, t, [start])`, `rindex`, `byteindex` | Integer or nil |
| regexp match | `match(s, re, [pos])` | MatchData or nil |
| to other types | `ord` · `hex` · `oct` · `to_sym`, `intern` | Integer · Symbol |
| bytes and conversions | `byteslice(s, i, [n])`, `b`, `force_encoding(s, enc)`, `encode`, `encoding`, `valid_encoding?`, `unpack(s, fmt)`, `unpack1` | as Ruby's |
| iterate | `each_line(s) { }`, `each_char(s) { }`, `each_byte` | s (block) |

- **The repl of a replacement.** The repl of `sub` and `gsub` is a String (with `\1`), a Hash (match => replacement), or a block `{ |m| ... }`.

## Array

An Array is a sequence whose length varies. `Any[1, 2]` makes one (the literal `[1, 2]` is a Tuple). The first ones you use are `Array.push`, `Array.each`, `Array.map`, and `Array.select`. Details: [Array](ref/Array.md).

```ruby
xs = Any[3, 1, 2]
Array.push(xs, 5)
p(xs)                                      # => [3, 1, 2, 5]
p(Array.map(xs) { |x| x * 2 })             # => [6, 2, 4, 10]
p(Array.select(xs) { |x| x > 1 })          # => [3, 2, 5]
p(Array.sort(xs))                          # => [1, 2, 3, 5]
p(Array.reduce(xs, 0) { |acc, x| acc + x })   # => 11
p(Array.first(xs))                         # => 3
p(Array.join(xs, "-"))                     # => "3-1-2-5"
p(Array.tally(Any["a", "b", "a"]))       # => {"a" => 2, "b" => 1}
```

| Purpose | Operation | Result |
|---|---|---|
| make | `Any[...]` · `Array.new(n)`, `Array.new(n, v)`, `Array.new(n) { \|i\| }` | a new Array |
| length and query | `length`, `size` · `empty?`, `include?(a, x)` | Integer · true/false |
| add and remove (in place) | `push(a, *xs)`, `append`, `unshift`, `concat(a, b)`, `insert(a, i, *xs)`, `delete_if`, `clear` | a |
| one element | `first`, `last`, `min`, `max`, `pop`, `shift`, `at(a, i)`, `sample`, `delete(a, x)`, `delete_at(a, i)` | an element, or nil |
| a part | `first(a, n)`, `last(a, n)`, `shift(a, n)`, `take`, `drop`, `slice(a, i, n)` | a new Array |
| fail when missing | `fetch(a, i, [default])` | the element, the default, or `IndexError` |
| to a String | `join(a, [sep])` | String |
| sum | `sum(a, [init])` | the sum (see below) |
| reorder and the like | `reverse`, `sort`, `sort_by`, `uniq`, `compact`, `flatten`, `rotate`, `shuffle`, `dup` | a new Array |
| iterate | `each`, `each_with_index`, `each_slice(a, n)`, `each_cons(a, n)`, `cycle` | a (block) |
| map and filter | `map`, `flat_map`, `select`, `filter`, `reject`, `partition`, `group_by`, `tally`, `to_h`, `zip`, `product` | a new Array / Hash / Tuple |
| test and count | `any?`, `all?`, `none?`, `count` | true/false · Integer (block optional) |
| fold | `reduce(a, init) { }`, `inject`, `each_with_object(a, memo) { }` | the accumulated value |
| search | `find`, `detect`, `min_by`, `max_by`, `index(a, x)`, `find_index`, `assoc`, `rassoc` | an element or nil · Integer or nil |
| rewrite (in place) | `map!`, `select!`, `reject!`, `sort!`, `uniq!`, `compact!`, `flatten!`, `reverse!`, `slice!`, `fill`, `replace` | a |
| to bytes | `pack(a, fmt)` | String |

- **`sum`.** The elements are numbers, or of a type including `Arithmetic`. An empty Array gives `init` (default 0).
- **Iteration without a block.** `each_slice` and the like, called without a block, give an Array.
- **Rewriting in place.** `map!` and the others are for mappings within one type.

## Enum

Enum is Ruby's Enumerable under a short name, defined by the prelude (`sakelib/prelude.sake`), which is read before every program. `Enum.map(x) { }` and the others dispatch on the type of the first argument: an Array, Hash, Set or Range answers with its own operation (`Enum.map(xs)` is `Array.map(xs)` when xs is an Array). A class of your own joins with `include Enum` and `def each(c) = ... yield(v) ...`, and gets the rest (`map`, `select`, `reject`, `find`, `reduce`, `sum`, `count`, `include?`, `any?`/`all?`/`none?`/`one?`, `min_by`/`max_by`, `sort_by`, `to_a`, `first`, `take`/`drop`, `group_by`, `partition`, `each_with_object`, `compact`, `filter_map`, `flat_map`), which are written over `each`. Details: [Enum](ref/Enum.md).

```ruby
p(Enum.map(Any[3, 1, 2]) { |v| v * 10 })        # => [30, 10, 20]
p(Enum.map(Hash.of(a: 1, b: 2)) { |k, v| "#{k}=#{v}" })   # => ["a=1", "b=2"]
p(Enum.count(1..4) { |v| v > 2 })                 # => 2

class Deck
  include Enum
  attr_reader cards
  def each(d) = Array.each(@cards) { |c| yield(c) }
end
d = Deck.new(Any["A", "K", "Q"])
p(Deck.select(d) { |c| c < "K" })                  # => ["A"]
p(Enum.first(d), Deck.count(d))                   # => "A"
                                                  # => 3
```

Tuple does not include Enum: its length and position types are fixed, so it is indexed and destructured instead.

## Tuple, Range

A Tuple is a sequence with a fixed length and a type per position; the literal `[1, "a"]` makes one. A Range is a pair of ends: `1..5`, `1...5`, and the endless `1..`. Tuples have few operations, and the usual move is `Tuple.to_a` to get an Array. For Ranges, `Range.each` counts, and `Range.to_a` and `Range.include?` are the common ones. Details: [Tuple](ref/Tuple.md), [Range](ref/Range.md).

```ruby
t = [1, "a"]
p(Tuple.size(t))                 # => 2
p(Tuple.to_a(t))                 # => [1, "a"]
p(Tuple.max([3, 7, 5]))          # => 7
p(Range.to_a(1..5))              # => [1, 2, 3, 4, 5]
p(Range.sum(1..5))               # => 15
p(Range.include?(1...5, 5))      # => false
p(Range.map(1..3) { |i| i * i })    # => [1, 4, 9]
p(Range.first(1.., 3))           # => [1, 2, 3]
```

| Operation | Result |
|---|---|
| `Tuple.size`, `length` · `to_a` · `max`, `min` · `minmax` | Integer · Array · an element (never nil) · `[min, max]` |
| `Range.each`, `each_with_index`, `step(r, n)` | r (block) |
| `to_a`, `map`, `select`, `filter`, `reject`, `zip` | Array |
| `reduce`, `inject` · `sum` · `size` · `count` | accumulated value · Integer |
| `any?`, `all?`, `none?` · `find`, `detect` · `include?`, `cover?`, `member?`, `exclude_end?` | true/false · an element or nil · true/false |
| `first`, `last`, `min`, `max`, `begin`, `end` | an element or nil (`first(r, n)`, `last(r, n)` give an Array) |

## Hash, Set

A Hash maps keys to values; `Hash.of("a" => 1)` or `Hash.new(default)` makes one (`{a: 1}` is a Record). A Set is a collection without duplicates, made with `Set.of(1, 2)`. The first ones you use are the index `h[k]` and `h[k] = v`, `Hash.each`, `Hash.fetch`, and `Set.add` and `Set.include?`. Details: [Hash](ref/Hash.md), [Set](ref/Set.md).

```ruby
h = Hash.of("a" => 1)
h["b"] = 2
p(h)                                       # => {"a" => 1, "b" => 2}
p(h["c"])                                  # => nil
p(Hash.fetch(h, "a"))                      # => 1
p(Hash.map(h) { |k, v| [k, v * 10] })      # => [["a", 10], ["b", 20]]
counts = Hash.new(0)
counts["x"] += 1
p(counts)                                  # => {"x" => 1}
s = Set.of(1, 2)
Set.add(s, 2)
p(s)                                       # => Set[1, 2]
p(Set.include?(s, 2))                      # => true
p(Set.union(s, Set.of(9)))                    # => Set[1, 2, 9]
```

| Purpose | Operation | Result |
|---|---|---|
| make | `Hash.of(k => v, ...)` · `Hash.new([default])` · `Set.of(...)` | a new Hash · Set |
| length and query | `length`, `size`, `empty?`, `key?`, `has_key?`, `include?`, `member?`, `value?` | Integer · true/false |
| one entry | `fetch(h, k, [default])` · `dig(h, k)` · `delete(h, k)` · `key(h, v)` | the value · value or nil · removed value or nil · key or nil |
| rewrite (in place) | `store(h, k, v)`, `merge!`, `update`, `clear`, `transform_values!`, `transform_keys!`, `select!`, `reject!`, `compact!` | h |
| a new Hash | `merge(a, b)`, `invert`, `select`, `filter`, `reject`, `transform_values`, `transform_keys`, `compact`, `slice`, `except` | a new Hash |
| to an Array | `keys` · `values` · `to_a` · `map` · `sort_by` · `group_by` · `partition` | Array |
| iterate | `each`, `each_pair`, `each_key`, `each_value` | h (block) |
| test and search | `any?`, `all?`, `none?`, `count`, `sum`, `find`, `detect`, `min_by`, `max_by` | with a block |
| rewrite a Set (in place) | `Set.add(s, x)`, `add?`, `delete`, `merge`, `subtract`, `clear` | s |
| Set to Array | `to_a`, `each`, `map`, `select`, `filter`, `reject` | Array |
| set operations | `union`, `intersection`, `difference`, `\|`, `&`, `-` · `subset?`, `superset?`, `disjoint?`, `intersect?` | a new Set · true/false |

## Regexp, MatchData, Symbol, Time, Math

A Regexp is a regular expression, made by the literal `/\d+/`. The result of a match is a MatchData, or nil when nothing matched. A Symbol is a name as a value (`:name`), a Time is one instant, and Math is the set of elementary functions. The first ones you use are `Regexp.match` and `m[1]`, `String.scan`, `Time.now`, and `Math.sqrt`. Details: [Regexp](ref/Regexp.md), [MatchData](ref/MatchData.md), [Symbol](ref/Symbol.md), [Time](ref/Time.md), [Math](ref/Math.md).

```ruby
m = Regexp.match(/(\d+)-(\d+)/, "tel 03-1234")
if m
  p(m[1])                        # => "03"
  p(MatchData.captures(m))       # => ["03", "1234"]
end
p(String.scan("a1 b22", /\d+/))  # => ["1", "22"]
p(Symbol.to_s(:name))            # => "name"
t = Time.at(0, in: "UTC")
p(Time.year(t))                  # => 1970
p(Time.strftime(t, "%Y-%m-%d"))  # => "1970-01-01"
p(Math.sqrt(16))                 # => 4.0
```

| Operation | Result |
|---|---|
| `Regexp.new(s, flags = "")` · `escape(s)` · `source(r)` · `match(r, s, [pos])` · `match?` | Regexp · String · String · MatchData or nil · true/false (flags: letters of `imx`) |
| `MatchData.captures` · `named_captures` · `names` · `to_a` · `to_s`, `pre_match`, `post_match` · `begin(m, i)`, `end(m, i)` | Array · Hash · Array · Array · String · Integer |
| `Symbol.to_s` · `length`, `size` | String · Integer |
| `Time.now` · `at(seconds, [in:])` · `new(y, [m, d, h, min, s, zone])` · `utc(t)`, `getutc` · `getlocal(t, [zone])`, `localtime` | Time |
| `year`, `month`, `day`, `hour`, `min`, `sec`, `wday`, `yday`, `to_i`, `utc_offset` · `to_f` · `to_s`, `strftime(t, fmt)` · `zone` · `utc?` | Integer · Float · String · String or nil · true/false |
| `Math.sqrt`, `cbrt`, `sin`, `cos`, `tan`, `atan`, `exp`, `log`, `log2`, `log10`, `atan2(y, x)`, `hypot(x, y)` | Float |

- **No `Symbol.to_proc`.** A block is not a value, so `&:name` cannot be written ([Functions and blocks](04-functions.md)).

## Rational, Complex

A Rational is a rational number, always reduced (`1/3r`); a Complex is a complex number (`1 + 2i`). The arithmetic operators work on both, and `Rational(1, 3)` and `Complex(1, 2)` are Kernel operations. Details: [Rational](ref/Rational.md), [Complex](ref/Complex.md).

```ruby
r = Rational(1, 3)
p(r + 1/6r)                      # => (1/2)
p(Rational.numerator(r))         # => 1
p(Rational.to_f(r))              # => 0.3333333333333333
c = Complex(1, 2)
p(c * c)                         # => (-3+4i)
p(Complex.abs(Complex(3, 4)))    # => 5.0
p(Integer.fdiv(7, 2))            # => 3.5
```

| Operation | Result |
|---|---|
| `Rational(a, [b])` · `Integer.to_r` · `Float.to_r` · `Float.rationalize` | Rational |
| `Rational.numerator`, `denominator`, `to_i`, `floor`, `ceil`, `round`, `truncate` · `to_f` · `abs` | Integer · Float · Rational |
| `Complex(re, [im])` · `Complex.real`, `imaginary` · `abs`, `arg` · `conjugate` · `rectangular` · `polar` | Complex · a real number · Float · Complex · `[re, im]` · `[abs, arg]` |
| `Integer.fdiv(a, b)` | Float |

## File, Dir, IO, ENV, Process

File and Dir are sets of operations on files and directories by path (a String); they have no values. IO is the type of open files and of the standard streams (`IO.stdin`, `IO.stdout`, `IO.stderr`). ENV is the environment, Process the process's information and clocks, Open3 child processes, and Zlib compression. The first ones you use are `File.read`, `File.write`, `File.open(path) { |io| }`, and `Dir.glob`. Details: [File](ref/File.md), [Dir](ref/Dir.md), [IO](ref/IO.md), [ENV](ref/ENV.md), [Process](ref/Process.md), [Open3](ref/Open3.md), [Zlib](ref/Zlib.md).

```ruby
dir = Dir.mktmpdir
path = File.join(dir, "notes.txt")
p(File.write(path, "one\ntwo\n"))          # => 8
p(File.readlines(path))                    # => ["one\n", "two\n"]
File.open(path, "a") { |io| IO.puts(io, "three") }
p(Array.length(File.readlines(path)))      # => 3
p(Dir.children(dir))                       # => ["notes.txt"]
p(ENV.fetch("NO_SUCH_VAR", "default"))     # => "default"
out, status = Open3.capture2("echo", "hi")
p(out)                                     # => "hi\n"
File.delete(path)
Dir.rmdir(dir)
```

| Operation | Result |
|---|---|
| `File.read(path)` · `readlines` · `write(path, s)` · `exist?`, `file?`, `directory?`, `symlink?`, `zero?`, `empty?`, `readable?`, `writable?`, `executable?`, `absolute_path?` | String · Array · Integer · true/false |
| `File.size`, `mtime`, `atime`, `ftype` · `rename`, `symlink`, `link`, `readlink`, `unlink`, `delete`, `chmod`, `utime` | Integer · Time · String · ... |
| `File.basename(p, [suffix])`, `dirname(p, [levels])`, `extname`, `join(*parts)`, `expand_path(p, [dir])`, `absolute_path`, `realpath` · `split(p)` | String · `[dir, base]` |
| `File.open(path, mode = "r", [perm])` · `File.open(...) { \|io\| }` | IO · the block's value (the file is closed) |
| `Dir.children`, `entries` · `glob(pat, [base:])` · `each_child(path) { }` · `exist?`, `empty?` · `mkdir(path, [mode])`, `rmdir` · `pwd`, `home`, `tmpdir` · `mktmpdir([prefix, [dir]])` | Array of String · ... |
| `IO.puts(io, ...)`, `print`, `write(io, s)` · `gets(io)`, `read(io, [n])`, `readlines`, `getc` · `each_line(io) { }` · `eof?`, `closed?`, `tty?` · `flush`, `close` · `seek(io, off, [whence])`, `pos`, `rewind`, `truncate`, `size` | nil · String or nil · ... |
| `ENV.fetch(name, [default])`, `get(name)`, `set(name, v)`, `key?`, `delete`, `to_h`, `keys` | String · ... |
| `Process.clock_gettime(Process.CLOCK_MONOTONIC)`, `pid` | Float · Integer |
| `Open3.capture2(cmd, ...)`, `capture2e`, `capture3` | Tuples such as `[out, status]` |
| `Zlib.inflate`, `deflate`, `gzip`, `gunzip` | String |

- **Closed IO.** Reading from or writing to a closed IO, or one not opened for it, raises `IOError`.
- **Order with stderr.** Writing to `IO.stderr` flushes stdout first, so their lines keep their order.

## Threads and sockets

A Thread runs a block concurrently, a Mutex gives mutual exclusion, and a Queue passes values between threads. TCPServer and Socket are TCP listening and connections. The first ones you use are `Thread.new { }` and `Thread.value`, `Mutex.synchronize`, and `Queue.push`/`pop`. Details: [Thread](ref/Thread.md), [Mutex](ref/Mutex.md), [Queue](ref/Queue.md), [TCPServer](ref/TCPServer.md), [Socket](ref/Socket.md).

```ruby
count = 0
m = Mutex.new
ts = Array.map(Any[1, 2, 3]) { |w| Thread.new { Mutex.synchronize(m) { count += w }; w * 10 } }
p(Array.map(ts) { |t| Thread.value(t) })   # => [10, 20, 30]
p(count)                                   # => 6
q = Queue.new
producer = Thread.new { Range.each(1..3) { |i| Queue.push(q, i) }; Queue.close(q) }
got = Any[]
loop do
  x = Queue.pop(q)
  break if x == nil
  Array.push(got, x)
end
Thread.join(producer)
p(got)                                     # => [1, 2, 3]
```

| Operation | Result |
|---|---|
| `Thread.new { ... }` · `Thread.value(t)` · `join(t, [limit])` · `alive?(t)` · `current` · `kill(t)` · `raise(t, msg)` | a Thread · the block's value (waits; an error in the thread is raised here) · t or nil · true/false |
| `Mutex.new` · `synchronize(m) { }` · `lock`, `unlock`, `try_lock`, `locked?`, `owned?` | a Mutex · the block's value |
| `Queue.new` · `push(q, x)` · `pop(q, [timeout])` · `close`, `size`, `empty?`, `closed?` | a Queue · q · the oldest element (waits; nil once closed and empty) |
| `TCPServer.new(host, port)` · `accept(s)` · `port(s)` · `close(s)` | a TCPServer (port 0 picks a free one) · a Socket (waits) · Integer · nil |
| `Socket.connect(host, port, [timeout])` · `connect_ssl(host, port, [timeout])` · `set_timeout(s, secs)` | a Socket (`connect_ssl`: TLS, the peer verified) |
| `Socket.gets(s)` · `read(s, n)` · `write(s, str)` · `close_write(s)` · `close(s)` | String or nil · String or nil · Integer · nil · nil |

- **Variables in a thread.** A block shares the variables around it, as every block does. For a thread, the variables of the blocks around `Thread.new` (their parameters and locals), and the thread block's own, are copied when the thread starts; the function's variables stay shared. So `Array.map(xs) { |w| Thread.new { ... w ... } }` in the example gives each thread its own `w`, and the shared counter `count` is a function variable updated inside `Mutex.synchronize`.
- **Leaving a thread block.** `break` and `return` out of a `Thread.new` block (and `return` in a block inside it) are static errors, since the block runs after the call has returned; `next v` ends it with `v`.
- **Errors.** An error inside a thread is raised by `Thread.value` or `Thread.join`. A thread that is never joined ends silently when it fails, and every thread stops when the main program ends.
- **Checking.** The typer runs a thread's block once, at `Thread.new`: its value is the type of `Thread.value`; a Queue's element type is the union of what is pushed (`Queue.pop` adds nil). Interleavings are not analyzed; each operation still checks its arguments while running.

## Typed arrays

`Integer[...]`, `Float[...]`, `Rational[...]`, `Complex[...]`, `String[...]`, `Symbol[...]`, `Tuple[...]`, and `D[...]` for each class `D` create an Array whose element type is that type ([Values and types](03-values.md)). Every write is checked against the element type: a value of another type is a `type` problem statically and a `TypeError` at run time.

```ruby
xs = Integer[1, 2]
Array.push(xs, 3)
p(xs)                            # => [1, 2, 3]
Point = Struct.new(:x, :y)
pts = Point[Point.new(0, 0)]
p(Array.length(pts))             # => 1
p(String[])                      # => []
```

```ruby error
xs = Integer[1, 2]
Array.push(xs, "a")              # !> Array.push: an element must be Integer, but is String [type]
```

# The library (sakelib)

The interpreter ships with `sakelib/`, Ruby's standard library and the main gems rewritten in Sake. This chapter covers how a library is loaded, what has been ported, the test framework minitest, and how each port is checked. Each library's API is in `sakelib/notes/NAME.md`; this chapter is the overview.

## Loading and names

A library is loaded with `require "json"`. The names of the libraries and of their operations follow Ruby's, so the names you know from Ruby work as they are. Only the shape of the call differs.

- **Where it looks.** `require "name"` first looks for `name.sake` in the directory of the requiring file. When there is none, it reads `sakelib/name.sake`, shipped with the interpreter. The sakelib copy is also taken when the `name.sake` next door is the requiring file itself, or a program rather than a library. From whatever directory the program runs, `require "json"` reads the same `sakelib/json.sake`.
- **Names with `/`.** Ruby's `net/http` and `open-uri` are written `net_http` and `open_uri`.
- **Module functions as in Ruby.** `JSON.parse(s)` and `Base64.encode64(s)` are spelled as in Ruby.
- **Instance methods take the subject first.** Ruby's `ss.scan(/\w+/)` is `StringScanner.scan(ss, /\w+/)` ([Program structure and name resolution](02-program.md)).
- **Nested names are Ruby's.** Namespaces nest ([Program structure and name resolution](02-program.md)), so `Net::HTTP`, `Concurrent::Future`, `JSON::ParserError` and `Benchmark::Tms` are spelled as in Ruby: `Net::HTTP.get(http, path)`, `rescue JSON::ParserError`. Only a nested name whose outer name is a built-in type cannot exist (there is no `Enumerable`, so ActiveSupport's `Enumerable::SoleItemExpectedError` is the top-level `SoleItemExpectedError`). Each library's notes have the table.

```ruby
require "base64"
require "strscan"

e = Base64.encode64("hello")
p(e)                                  # => "aGVsbG8=\n"
p(Base64.decode64(e))                 # => "hello"
ss = StringScanner.new("foo bar")
p(StringScanner.scan(ss, /\w+/))      # => "foo"
p(StringScanner.scan(ss, /\w+/))      # => nil
p(ss.StringScanner.scan(/\s+/))       # => " "
```

Each library's notes are in `sakelib/notes/NAME.md`: what differs from Ruby and why, what is missing, and a table of Ruby's API against Sake's. The libraries not ported, with the reason, are in `sakelib/notes/not-ported.md`.

## Ports of the standard library

The parts of Ruby's standard library that can be written in Sake are ported. Most of what cannot rests on `method_missing`, `define_method`, special variables or first-class blocks, which Sake leaves out on purpose. What needs the OS or a C implementation, such as TLS, UDP and the terminal, is built into the interpreter rather than written as a library.

```ruby
require "json"

doc = JSON.parse("{\"name\": \"ann\", \"tags\": [\"a\", \"b\"]}")
p(doc)                                # => {"name" => "ann", "tags" => ["a", "b"]}
if doc in Hash
  p(doc["name"])                      # => "ann"
  puts(JSON.generate(doc))            # => {"name":"ann","tags":["a","b"]}
end
```

`JSON.parse` returns one of Hash, Array, String, Integer, Float, true/false and nil, so narrow it with `if doc in Hash` before indexing ([Control flow and patterns](06-control.md)). Written without the narrowing, `doc["name"]` makes the checker report that the receiver may be a type that cannot be indexed.

| Area | Libraries |
|---|---|
| Data formats | json, csv, yaml, toml, ini, base64, digest, zlib, xml, erb, mustache |
| Strings and parsing | strscan, optparse, getoptlong, shellwords, abbrev, prettyprint, pp, text, diff, terminal_table |
| Numbers | bigdecimal, matrix, prime, securerandom, units |
| Time | time, date |
| I/O and OS | fileutils, pathname, find, tempfile, stringio, logger, benchmark |
| Network | net_http, open_uri, uri, cgi, ipaddr, webrick |
| Concurrency | monitor, mutex_m, timeout, observer, event_emitter |
| Data structures | pqueue, lru_cache, trie, tsort, state_machine, semver |

### Partial ports and notes

- **yaml** is a subset of Psych; **xml** a subset of REXML.
- **digest** computes MD5 and SHA in Sake (slowly). **zlib** has only the CRC-32 and Adler-32 checksums, no compression.
- **net_http** is `Net::HTTP`, `Net::HTTPResponse`, `Net::HTTP::Get` and the rest, as in Ruby. https goes through the built-in `Socket.connect_ssl`.
- **timeout** runs the block in a Thread of its own and, when the time is up, really stops it with the built-in `Thread.raise`.
- **pp** is the PP that wraps at a width, distinct from the built-in one-line `pp`.

### Built in, so not required

Some things Ruby requires are built-in operations in Sake. `require "tmpdir"` is a static error saying there is no file to read.

| Ruby's require | Sake's built-in |
|---|---|
| `tmpdir` | `Dir.mktmpdir`, `Dir.tmpdir` (the latter added by tempfile) |
| `open3` | `Open3.capture2`, `capture2e`, `capture3`, `Kernel.system` |
| `set` | `Set.of(...)`, `Set.add`, ... ([Values and types](03-values.md)) |
| `socket` | `TCPServer`, `Socket` (TCP only) |

### Small libraries after gems

In the table above, toml (toml-rb), ini (inifile), mustache, units (ruby-units), text, diff (diff-lcs), terminal_table, event_emitter (Node's EventEmitter), pqueue, lru_cache (lru_redux), trie, state_machine (AASM-like) and semver are not Ruby's standard library but were written after popular gems or libraries of other languages. The head of each one's notes names its model.

### Instead of what is not ported

- Instead of **singleton**, `once { T.new(...) }` ([Functions and blocks](04-functions.md)).
- Instead of **forwardable / delegate**, write the delegating operations one by one.
- Instead of **ostruct**, a Record `{a: 1}` or a Hash.

## Ports of gems

Popular gems are ported too, as far as Sake can express them. Each port keeps the gem's names where Sake can (`ActiveSupport::Inflector.pluralize(s)`, `Redis.get(r, k)`).

| Area | Libraries |
|---|---|
| Terminal and CLI | colorize, ruby_progressbar, highline, thor, awesome_print |
| ActiveSupport | active_support_inflector, active_support_core_ext, active_support_number_helper |
| Text | kramdown, liquid, i18n, faker |
| Web | rack, rackup, webrick, httparty |
| Data | redis, dotenv, money, rubyzip, chronic, jwt |
| Concurrency and testing | concurrent_ruby, rspec, minitest |

### Notes

- **active_support_core_ext** is the modules `Blank`, `StringExt`, `ArrayExt`, `HashExt` and the class `ActiveSupport::Duration`.
- **kramdown** is the GFM dialect, **redis** a RESP2 client, **jwt** HS256/384/512.
- **concurrent_ruby** has `Concurrent::Future`, `Concurrent::Promise`, `Concurrent::Atom`, `Concurrent::Map`, `Concurrent::Semaphore` and more.
- **rspec** is written in the shape of minitest, naming the example: `RSpec.expect(ex, x).To.eq(y)`.

### Dropped features

The features dropped and why are tabulated in `sakelib/notes/not-ported.md`. The main three:

- **Stored blocks**, such as `then(rescuer) {}` and registered callbacks. Blocks are second-class and cannot be stored ([Functions and blocks](04-functions.md)).
- **Reflection**: `constantize`, `send`, `define_method`. There is no call by name.
- **APIs that pass a class as a value**, such as `expect(x).to be_a(T)`. A type is not a value; write `x in T` or one function per type.

## minitest

`sakelib/minitest.sake` is a test framework in the style of Ruby's Minitest, for programs written in Sake. Sake's own test suite (`test/sake/*_test.sake`) is written with it.

### Writing a test

Sake has no reflection, so a test is not a method found by name but a block given to `Minitest.test`. Every assertion names the test it belongs to (the block's parameter `t`) as its first argument, as every Sake operation names its subject.

```ruby
require "minitest"

suite = Minitest.suite("strings")
Minitest.test(suite, "upcase and split") do |t|
  Minitest.assert_equal(t, "ABC", String.upcase("abc"))
  Minitest.assert_equal(t, Any["a", "b"], String.split("a,b", ","))
  Minitest.assert(t, String.empty?(""))
end
Minitest.run(suite)      # prints the report; exits with 1 when a test failed
```

```
# Running strings:

.

Finished in 0.026718s

1 runs, 3 assertions, 0 failures, 0 errors
```

- **Tests run on the spot.** `Minitest.test` runs the block at once and records the result. Tests run in the order defined.
- **`Minitest.run(suite)`** prints the report and exits with code 1 when there was a failure or an error. `Minitest.report(suite)` only prints, and returns true or false for whether everything passed.

### Assertions

Functions of `Minitest`; each takes the test `t` first and an optional message last.

| Function | Checks |
|---|---|
| `assert(t, cond)`, `refute(t, cond)` | truthy / falsy |
| `assert_equal(t, expected, actual)`, `refute_equal` | `==` |
| `assert_nil(t, x)`, `refute_nil` | nil |
| `assert_in_delta(t, expected, actual, delta = 0.001)` | difference within delta |
| `assert_includes(t, collection, x)`, `refute_includes` | contains the element |
| `assert_empty(t, collection)`, `refute_empty` | empty |
| `assert_match(t, pattern, s)` | matches the Regexp |
| `assert_raises(t) { ... }` | the block raises; gives the exception's message |

There is no `assert_output`: a Sake program cannot capture its own output, so print the value and compare.

### Failures and errors

A failed assertion raises `Minitest::Assertion` and is recorded as a failure (F). Any other exception is recorded as an error (E), as in Ruby's Minitest.

```ruby
require "minitest"

suite = Minitest.suite("numbers")
Minitest.test(suite, "integer division floors") do |t|
  Minitest.assert_equal(t, 3, 7 / 2)
end
Minitest.test(suite, "divides by zero") do |t|
  Minitest.assert_equal(t, 0, 1 / 0)
end
Minitest.test(suite, "wrong expectation") do |t|
  Minitest.assert_equal(t, 4, 2 + 3)
end
Minitest.run(suite)      # exit code 1
```

```
# Running numbers:

.EF

Finished in 0.035481s

  1) Error:
divides by zero
divided by 0

  2) Failure:
wrong expectation
expected 4, got 5

3 runs, 2 assertions, 1 failures, 1 errors
```

## Tests

Each library port has a Ruby twin: `test/sakelib/NAME.sake`, the program written with Sake's library, and `test/sakelib/NAME.rb`, the same program with Ruby's library. The two must print the same output; that sameness is the definition of a correct port.

```ruby
# the shape of test/sakelib/shellwords.sake
require "shellwords"
p(Shellwords.split("a 'b c' d"))      # => ["a", "b c", "d"]
p(Shellwords.escape("it's"))          # => "it\\'s"
```

```ruby
# its twin shellwords.rb, printing the same two lines in Ruby
require "shellwords"
p(Shellwords.split("a 'b c' d"))
p(Shellwords.escape("it's"))
```

- **Running them.** `ruby test/test_sakelib.rb` runs each `.sake` with `bin/sake --strict` (level 2) and each `.rb` with Ruby, inside `test/sakelib/`, and compares the outputs.
- **When the gem is not installed.** The twin of a gem that is not installed runs on `test/sakelib/ref/NAME.rb`, a plain-Ruby reference with the gem's names and output.
- **Notes.** Where the twins differ, and where Sake departs from Ruby, is written in `sakelib/notes/NAME.md`.

# Built-in reference

One chapter per built-in type or module, listing **every** operation of that namespace. Section headings are the operation names; aliases share a section (`find, detect`). The first line of each section is the exact signature:

- `Array.first(x, [Integer])`: `x` is a value of the namespace's type (the subject), `[T]` an optional argument, `*T` any number of arguments, `[k: T]` a keyword, ` { }` a required block, and ` [{ }]` an optional block.
- `T[...]`, such as `Integer[*Any]`, is the constructor of an **Array** of T values (a typed Array).

The text of each section says what the operation does and returns, what the arguments mean, when the result is **nil** and at which strictness level the checker reports its unchecked use, which **exceptions** it raises, what the checker rejects statically, whether it changes the subject in place or returns a new value, and how it differs from Ruby's method of the same name. The `# => value` annotations in the examples are real output: `ruby tools/check_reference.rb` runs every example under `--strict=2` and compares, and also checks the signatures and that no operation is missing (an example marked `ruby error` is one confirmed to be rejected or to fail).

The chapters go: Kernel and the exceptions, numbers, strings and regular expressions, sequences and maps, time, input/output and the OS, threads and sockets. The rules of the operators (`+`, `<`, `[]`) are in [Operators and indexing](05-operators.md); the nature of each type's values in [Values and types](03-values.md).

# Kernel

Kernel is the set of operations that any part of a program calls by name alone: `puts(x)`, `Integer(s)`, `loop { }`, without writing `Kernel.puts(x)` (that form works too; a name resolves to the current namespace first, then to the top-level functions, then to Kernel, see [Functions](../04-functions.md)). As with every other namespace, the argument types are checked statically, and a value of the wrong type is a `type` problem.

Here are output and input (`puts`, `print`, `p`, `pp`, `format`, `gets`, `warn`), the strict numeric conversions (`Integer`, `Float`, `Rational`, `Complex`), showing and copying values (`to_s`, `inspect`, `dup`, `equal?`), control (`loop`, `block_given?`, `once`), random numbers and time (`rand`, `srand`, `sleep`), and the process (`exit`, `at_exit`, `system`, `ARGV`, `PROGRAM_NAME`). `raise` is syntax, not an operation: see [Exceptions and errors](../08-exceptions.md). `Math.PI` and `Float.INFINITY` are read as operations too, but belong to the chapters of their namespaces (Sake has no value constants; a named value is made with `once`).

## Kernel[]

`Kernel[*Any]`

Makes an Array of any values. Every type includes Kernel, so by the rule of `M[...]` for a module (an Array of the types that include it) this Array takes every value; the prelude names it `Any = Kernel`, and it is written `Any[...]`. The elements may be of any types, mixed, and any value can be added later (the checker adds the type of each added value to the element type). The empty Array is `Any[]`. The literal `[1, 2]` is a Tuple, so a sequence that grows is written `Any[...]`; a typed Array is made with the type's name, `Integer[1, 2]`, and `Array[...]` is an Array of Arrays.

```ruby
xs = Any[1, "a"]
Array.push(xs, :sym)
p(xs)                          # => [1, "a", :sym]
e = Any[]
Array.push(e, 1)
p(e)                           # => [1]
p([1, 2] == Any[1, 2])         # => false
```

```ruby error
xs = [1, 2]
Array.push(xs, 3)              # !> `[...]` is a Tuple with a fixed length; for a growable Array, write `Any[...]`
```

## puts

`Kernel.puts(*Any)`

Writes each argument on its own line to the output and returns nil (Ruby's `puts`). A String is written as is (a newline is added when it has none), nil is an empty line, any other value is written in its `to_s` form. Arrays and Tuples are written one element per line, nested ones flattened (an empty Array is an empty line). Without arguments it writes an empty line. A class instance is written with its type's own `to_s(x)` when there is one, otherwise in the `#<struct ...>` form that `p` uses ([to_s](#to_s)).

```ruby
puts("a", 1, :sym)
# => a
# => 1
# => sym
puts(Any[1, [2, 3]])
# => 1
# => 2
# => 3
puts(Hash.of("k" => 1), 1..3)
# => {"k" => 1}
# => 1..3
```

## print

`Kernel.print(*Any)`

Writes each argument in its `to_s` form with no newline between them and returns nil (Ruby's `print`). Arrays and Tuples are written as `[1, 2]` (not one element per line as `puts` does); nil is the empty string.

```ruby
print("a", 1, nil, "\n")        # => a1
print(Any[1, 2], [3, 4], "\n") # => [1, 2][3, 4]
```

## p

`Kernel.p(*Any)`

Writes each argument in its `inspect` form, one per line (Ruby's `p`): Strings quoted, Symbols as `:sym`, class instances as `#<struct Point x=1, y=2>`, exception values as `#<KeyError: msg>`. The result is Ruby's: the value when there is one argument, a Tuple of the arguments when there are several, nil when there are none (the checker sees it the same way). A class instance whose type defines `inspect(x)` is written with that.

```ruby
x = p(1, "a")
# => 1
# => "a"
p(x)                             # => [1, "a"]
p(p())                           # => nil
Point = Struct.new(:x, :y)
p(Point.new(1, 2))               # => #<struct Point x=1, y=2>
p(Hash.of("a" => 1), Set.of(1), 2r)
# => {"a" => 1}
# => Set[1]
# => (2/1)
```

## pp

`Kernel.pp(Any)`

Writes one value in the form `p` uses and returns the value. Ruby's `pp` wraps long values; in Sake it is the same as `p(x)`.

```ruby
a = pp(Any[1, {k: 2}])        # => [1, {k: 2}]
p(a)                             # => [1, {k: 2}]
```

## format, sprintf

`Kernel.format(String, *Any)`

`Kernel.sprintf(String, *Any)`

Makes a String with Ruby's `format` directives: `%d`, `%f`, `%e`, `%g`, `%x`, `%o`, `%b`, `%c`, `%s`, `%p`, `%%`, with width, precision and the flags `-`, `+`, `0`. `%s` uses a value's `to_s` (a class's own `to_s(x)` included), `%p` its `inspect`. `%<name>d` and `%{name}` take one Record (`{a: 1}`) or one Hash with Symbol keys (`Hash.of(a: 1)`) as the single argument; the same holds for the operator form `fmt % x` ([String](String.md)). Too few or too many arguments, a value that is not a number for a numeric directive, and a missing key (`key<a> not found`) are all `ArgumentError`.

```ruby
p(format("%05.2f|%-4s|%p|%x", 3.14159, :ab, "q", 255))   # => "03.14|ab  |\"q\"|ff"
p(sprintf("%+d %s %s", 3, nil, true))                     # => "+3  true"
p(format("%<a>d-%<b>s", {a: 1, b: "x"}))                  # => "1-x"
p("%<a>05d" % {a: 42})                                    # => "00042"
p(format("%<n>d-%{s}", Hash.of(n: 1, s: "x")))               # => "1-x"
Point = Struct.new(:x, :y)
p(format("%s", Point.new(1, 2)))                          # => "#<struct Point x=1, y=2>"
```

```ruby error
p(format("%d %d", 1))            # !> ArgumentError: Kernel.format: too few arguments
```

```ruby error
p(format("%<a>d", {b: 1}))       # !> ArgumentError: Kernel.format: key<a> not found
```

## gets

`Kernel.gets()`

Reads one line from standard input and returns it as a String including its trailing newline, or nil at the end of the input. The result is `String | nil`, so `String.chomp(gets)` without a check is stopped by `--strict` (level 2) as a `nil` problem; test it first, `if (line = gets)`. Unlike Ruby's `gets` it takes no separator argument.

```ruby
line = gets
p(line)                          # => "3\n"
if (rest = gets)
  p(String.split(String.chomp(rest), " "))   # => ["1", "2"]
end
p(gets)                          # => nil
```

```ruby error
p(String.chomp(gets))            # !> argument 1 may be nil
```

## warn

`Kernel.warn(*Any)`

Writes each argument to the error output (stderr) in the form `puts` uses and returns nil. Without arguments it writes nothing. Reporting a usage mistake and then calling `exit` is the typical pair.

```ruby error
warn("usage: prog FILE")         # !> usage: prog FILE
exit(2)
```

## Integer

`Kernel.Integer(String|Integer|Float)`

Ruby's strict `Integer()`. A String must be a decimal integer or one with a `0x`, `0b`, `0o` or `0` prefix; `_` separators and surrounding whitespace are allowed, anything else (`"12abc"`, `""`, `"1e3"`) is an `ArgumentError`. A Float is truncated, an Integer returned as is. `String.to_i` silently reads as far as it can; this operation fails unless the whole String is an integer. A NaN or infinite Float is a `FloatDomainError` (the message is the value's name, `NaN` or `Infinity`), rescuable like the others.

```ruby
p(Integer("42"))                 # => 42
p(Integer(" 0x1f "))             # => 31
p(Integer("1_000"))              # => 1000
p(Integer(3.9))                  # => 3
p(String.to_i("12abc"))          # => 12
```

```ruby error
p(Integer("12abc"))              # !> ArgumentError: Kernel.Integer: invalid value for Integer(): "12abc"
```

```ruby error
p(Integer(Float.NAN))            # !> FloatDomainError: Kernel.Integer: NaN
```

## Float

`Kernel.Float(String|Integer|Float)`

Ruby's strict `Float()`. A String must be a decimal real (`"1e3"`, `"1_0.5"`, surrounding whitespace allowed), otherwise `ArgumentError`. An Integer becomes a Float, a Float is returned as is.

```ruby
p(Float("1.5"))                  # => 1.5
p(Float("1e3"))                  # => 1000.0
p(Float(2))                      # => 2.0
```

```ruby error
p(Float("abc"))                  # !> ArgumentError: Kernel.Float: invalid value for Float(): "abc"
```

## Rational

`Kernel.Rational(Integer|Float|Rational|String, [Integer|Rational])`

Ruby's `Rational(a, b = 1)`. `a` is an Integer, a Float, a Rational, or a String of the form `"1/3"`, `"0.75"` or `"3"`; `b` is an Integer or a Rational; the result is `a / b` reduced. A Float is taken exactly, as `Float.to_r` takes it: `Rational(0.5)` is `(1/2)`, but `Rational(0.1)` is the long binary fraction `0.1` really stands for (for the short `(1/10)` use `Float.rationalize`). A String that cannot be read is an `ArgumentError`, a `b` of 0 a `ZeroDivisionError`, a NaN or infinite Float a `FloatDomainError`.

```ruby
p(Rational(3, 6))                # => (1/2)
p(Rational("0.75"))              # => (3/4)
p(Rational("1/3"))               # => (1/3)
p(Rational(1r/2, 1r/4))          # => (2/1)
p(Rational(0.5))                 # => (1/2)
p(Rational(1.5, 1r/2))           # => (3/1)
p(Rational(0.1) == Float.to_r(0.1))   # => true
p(Float.rationalize(0.1))        # => (1/10)
```

```ruby error
p(Rational(1, 0))                # !> ZeroDivisionError: Kernel.Rational: divided by 0
```

## Complex

`Kernel.Complex(Integer|Float|Rational, [Integer|Float|Rational])`

Ruby's `Complex(re, im = 0)`. The real and imaginary parts are real numbers; Strings are not accepted (use `String.to_c`).

```ruby
p(Complex(1, 2))                 # => (1+2i)
p(Complex(1.5))                  # => (1.5+0i)
p(Complex(1r, 2.5))              # => ((1/1)+2.5i)
```

## to_s

`Kernel.to_s(Any)`

The String that `puts`, interpolation `"#{x}"` and `format`'s `%s` use for a value. A String is itself, nil is `""`, a Symbol is its name, numbers use Ruby's `to_s`, and Arrays, Tuples, Hashes, Sets, Records and Ranges are written as `inspect` writes them. A class instance uses its type's `to_s(x)` when the type defines one (also one that comes from an included module), otherwise `#<struct T ...>`. It corresponds to Ruby's `x.to_s`; since Sake has no method calls on values, this one operation turns a value of any type into a String.

```ruby
class Temp
  attr_reader celsius
  def to_s(t) = "#{@celsius}C"
end
t = Temp.new(21)
p(to_s(t))                       # => "21C"
puts("now #{t}")                 # => now 21C
p(to_s(nil))                     # => ""
p(to_s(Any[1, "a"]))           # => "[1, \"a\"]"
p(to_s(:sym))                    # => "sym"
```

## inspect

`Kernel.inspect(Any)`

The String that `p` writes for a value (Ruby's `x.inspect`): a String with quotes and escapes, a Symbol as `:sym`, nil as `"nil"`, a container with its elements inspected. A class instance uses its type's `inspect(x)` when the type defines one (also from an included module), otherwise `#<struct T f=v, ...>`; an exception value is `#<E: message>`. `p`, `pp`, `format`'s `%p` and the elements inside a container are shown with it.

```ruby
module Pretty
  def inspect(v) = "<pretty>"
end
class Box
  include Pretty
  attr_reader n
end
p(inspect("a\n"))                # => "\"a\\n\""
p(inspect(nil))                  # => "nil"
p(inspect(Box.new(1)))           # => "<pretty>"
p(Any[Box.new(1)])             # => [<pretty>]
```

## dup

`Kernel.dup(Any)`

A shallow copy of a value (Ruby's `obj.dup`): an Array, Tuple, Hash, Set, Record, class instance or String becomes a new container holding the same elements (the elements' containers are shared). An Integer, Symbol, nil, true/false or Range is returned as is. The type is the original's. A class instance whose type defines `dup(x)` gets that function (the result has the function's type). Inside a type's functions, copy an element with `Kernel.dup(@items)`: a bare `dup` resolves to the type's own.

```ruby
a = Any[Any[1], Any[2]]
b = dup(a)
Array.push(b, Any[3])
Array.push(b[0], 9)
p(a)                             # => [[1, 9], [2]]
p(b)                             # => [[1, 9], [2], [3]]
class Box
  attr_accessor items
  def dup(b) = Box.new(Kernel.dup(@items))
end
x = Box.new(Any[1])
y = dup(x)
Array.push(Box.items(y), 2)
p(Box.items(x))                  # => [1]
p(Box.items(y))                  # => [1, 2]
```

## equal?

`Kernel.equal?(Any, Any)`

True when the two arguments are the same value (the same object), Ruby's `a.equal?(b)`. `==` compares contents; this is identity. The result of `dup` is `==` to the original but not `equal?`. Equal Integers, Symbols, nil and true/false are always identical; two String literals written separately are not.

```ruby
a = Any[1]
b = a
p(equal?(a, b))                  # => true
p(a == dup(a))                   # => true
p(equal?(a, dup(a)))             # => false
p(equal?("a", "a"))              # => false
p(equal?(:a, :a))                # => true
```

## loop

`Kernel.loop() { }`

Runs the block until a `break` (Ruby's `loop`). The result is the `v` of `break v` (nil for a bare `break`); the checker sees the union of the values of every `break` in the block. `next` goes on to the next round. A `loop` without a `break` never returns (Sake has no `StopIteration` to end it).

```ruby
i = 0
v = loop do
  i += 1
  next if i < 3
  break i * 10
end
p(v)                             # => 30
p(loop { break })                # => nil
```

## block_given?

`Kernel.block_given?()`

True when the current function was called with a block. A function that checks `block_given?` may be called without one (a function that only `yield`s requires it, see [Functions](../04-functions.md)). The checker folds `block_given?` in a condition to its value for each call, so a branch that reaches `yield` is not run for a call without a block. At the top level it is false.

```ruby
def each_or(x)
  return yield(x) if block_given?
  x
end
p(each_or(1))                    # => 1
p(each_or(1) { |v| v + 10 })     # => 11
p(block_given?)                  # => false
```

## once

`Kernel.once() { }`

The block's value. The block runs once, the first time that place in the program is reached, and the value is kept for the rest of the program: every later evaluation (another call, another thread) gets the same value. Since Sake has no value constants, this is how a table or a named value is computed once: `def table = once { ... }`. What is kept is the value itself; an Array can still be changed afterwards (as Ruby's constants can). A block that reaches its own `once` again while computing it ends the program with `SystemStackError` (not rescuable).

```ruby
def table = once { puts("computing"); Any[1, 2, 3] }
p(table)
# => computing
# => [1, 2, 3]
p(table)                         # => [1, 2, 3]
p(equal?(table, table))          # => true
```

```ruby error
def rec(n) = once { rec(n) }
rec(1)                           # !> SystemStackError: once: the block reached its own once again while computing it
```

## rand

`Kernel.rand([Integer|Float])`

Ruby's `rand`, with a stricter argument. Without an argument, a Float at least 0.0 and below 1.0. With an Integer `n`, an Integer at least 0 and below `n`. With a Float `x`, a Float at least 0.0 and below `x`. The bound must be positive: 0, 0.0 and a negative number are an `ArgumentError` (`invalid argument - 0`), where Ruby takes `|n|` and gives a Float for 0. A Range is not accepted (a `type` problem statically).

```ruby
f = rand
p(f >= 0.0 && f < 1.0)           # => true
i = rand(6)
p(i >= 0 && i < 6)               # => true
g = rand(2.5)
p(g >= 0.0 && g < 2.5)           # => true
```

```ruby error
p(rand(0))                       # !> ArgumentError: Kernel.rand: invalid argument - 0
```

## srand

`Kernel.srand([Integer])`

Sets the seed of the random numbers and returns the previous seed (an Integer), Ruby's `srand`. Without an argument it picks a new seed at random. The same seed gives the same sequence of `rand`.

```ruby
srand(7)
a = Array.map(Any[1, 2, 3]) { rand(1000) }
srand(7)
b = Array.map(Any[1, 2, 3]) { rand(1000) }
p(a == b)                        # => true
p(srand(1))                      # => 7
```

## sleep

`Kernel.sleep([Integer|Float|Rational])`

Pauses for the given number of seconds and returns the seconds slept, rounded to an Integer (Ruby's `sleep`). Without an argument it pauses forever (until another thread wakes it).

```ruby
p(sleep(0))                      # => 0
p(sleep(1r/100))                 # => 0
```

## exit

`Kernel.exit([Integer|Boolean])`

Ends the program with the status `status`: an Integer as is, true as 0, false as 1, 0 when left out. No `rescue` catches it (`rescue => e` lets it through), while the `ensure` clauses on the way out and the `at_exit` blocks run. The checker treats the code after `exit` as unreachable (its type is `[]`: it returns no value).

```ruby
at_exit { puts("at_exit") }
begin
  exit(true)
rescue => e
  puts("not caught")
ensure
  puts("ensure")                 # => ensure
end
puts("not reached")              # => at_exit
```

```ruby error
puts("start")                    # !> start
exit(3)
puts("not reached")
```

## at_exit

`Kernel.at_exit() { }`

Registers the block and returns nil. The registered blocks run when the program ends (normally, by `exit`, or by an uncaught exception), the last registered first (Ruby's `at_exit`).

```ruby
at_exit { puts("bye 1") }
at_exit { puts("bye 2") }
puts("main")
# => main
# => bye 2
# => bye 1
```

## system

`Kernel.system(String, *String)`

Runs a command as a child process and waits for it (Ruby's `system`). One argument goes through the shell; with several, the first is the command and the rest its arguments. The result is true for exit status 0, false for any other status, and nil when the command could not be started (`Boolean | nil`). The child's output goes straight to the output; to get it as a String use `Open3.capture2` or `capture3`.

```ruby
p(system("echo", "hi"))
# => hi
# => true
p(system("false"))               # => false
p(system("/no/such/command"))    # => nil
```

## ARGV

`Kernel.ARGV()`

The program's command-line arguments, an Array of Strings. It is written like Ruby's constant but is an operation, and every use gives the same Array, so shortening it with `Array.shift(ARGV)` or an `OptionParser`'s `parse!` shows in every later `ARGV`. Its Strings are frozen: an operation that changes one in place (`String.concat`, ...) is a `TypeError`.

```ruby
p(ARGV)                          # => []
Array.push(ARGV, "x")
p(ARGV)                          # => ["x"]
p(equal?(ARGV, ARGV))            # => true
```

## PROGRAM_NAME

`Kernel.PROGRAM_NAME()`

The path of the running program (its first file), Ruby's `$0`. Unlike the other Kernel operations, a bare `PROGRAM_NAME` is read as a type name and rejected (`type PROGRAM_NAME cannot be used as a value`), so write `Kernel.PROGRAM_NAME`.

```ruby
p(String.end_with?(Kernel.PROGRAM_NAME, ".sake"))   # => true
```

# Exceptions

The built-in exception types. Built-in operations raise them when they fail at run time, and a program raises them itself with `raise E, "msg"`. Each is declared as a class with the single field `message` ([Classes](../07-classes.md)), so the operations of any class apply:

- `E.new(message)` makes an exception value (`message` is usually a String; its type is not checked). `raise E, "msg"` is short for `raise E.new("msg")`, and `raise E` uses the type's name as the message. `raise "msg"` raises a `RuntimeError`. `raise` is syntax, described in [Exceptions and errors](../08-exceptions.md).
- `E.message(e)` reads the message and `E.set_message(e, s)` replaces it (`e.E.message` is the same read). The old `E.get_message` is rejected with a hint to use `E.message`. `Exception.message(e)` reads the message of an exception value of any type: use it where the type is a union, as after `rescue => e`.
- `Exception.backtrace(e)` gives the calls in effect when it was raised (an Array of `"FILE:LINE:in 'FUNCTION'"`, innermost first), and `Exception.cause(e)` the exception a rescue clause was handling then. Both are nil for a value never raised ([Backtrace and cause](../08-exceptions.md)).
- `rescue E => e` catches the listed types. The built-in types keep Ruby's parents: `rescue IndexError` also catches `KeyError`, `rescue IOError` `EOFError`, `rescue RangeError` `FloatDomainError`, `rescue ArgumentError` `Math::DomainError`, and each also catches a class declared under it (`class E < RangeError`). A user type `class B < A` is a copy, so `rescue A` does not catch B; a module that the types include groups them (`rescue M`). Several types are `rescue A, B => e`; every type is `rescue => e` (`rescue StandardError` and `rescue Exception` mean the same).
- `p(e)` prints `#<E: message>`, and `==` compares the fields.
- The message of an exception raised by a built-in operation is Ruby's text (`divided by 0`); the report of one that ends the program also names the operation (`ZeroDivisionError: Kernel.Rational: divided by 0`).
- A Ruby exception raised inside a built-in operation never escapes as a raw Ruby error with a backtrace: it becomes the Sake exception of the same kind, rescuable like any other, with Ruby's message (`Tuple` in place of Ruby's `Sake::Tuple`). Ruby's `EOFError`, `Errno::*` and `ClosedQueueError` become `IOError`, `FrozenError` becomes `TypeError`, `Math::DomainError` stays `Math::DomainError`, and Ruby's own `RuntimeError` (`String.undump` of a bad String) becomes `ArgumentError`, so that Sake's `RuntimeError` comes only from a program's `raise`.

`Math::DomainError` is a built-in exception type too: it can be written in `rescue Math::DomainError => e` and `raise Math::DomainError, "msg"`, but being a nested name it cannot be written as `Math::DomainError.new(...)` or `Math::DomainError.message(e)` (read it with `Exception.message(e)`). `Math.sqrt(-1)`, `Math.log(-1)`, `Integer.sqrt(-1)`, `Integer.digits(-1)` and `x ** 0.5` with a negative Float `x` (`-8.0 ** 0.5 is not a real number (a negative base with a fractional exponent)`; Ruby gives a Complex) raise it. `SystemStackError` (too deep a recursion, a `once` reaching itself), `LocalJumpError` and `NotImplementedError` are program errors: they cannot be rescued, and naming them in `rescue` is a static error.

Most type mistakes (`TypeError`) and pattern misses (`NoMatchingPatternError`) are stopped before running as `type` problems, so at run time they reach only the places the checker could not see (an operation taking `Any`, a program run with `--strict=0`).

```ruby
begin
  Integer("x")
rescue ArgumentError, TypeError => e
  puts(Exception.message(e))       # => invalid value for Integer(): "x"
end
e = KeyError.new("missing")
p(e)                               # => #<KeyError: missing>
p(KeyError.message(e))             # => "missing"
KeyError.set_message(e, "other")
p(Exception.message(e))            # => "other"
p(e == KeyError.new("other"))      # => true
```

## RuntimeError

`RuntimeError.new(message)`

The type that `raise "msg"` (a `raise` without a type) raises. No built-in operation raises it on its own: only `Thread.raise(t, msg)` does, in the other thread, where `Thread.value(t)` or `Thread.join(t)` deliver it (a Ruby `RuntimeError` inside an operation, as from `String.undump`, becomes `ArgumentError`). It is the type for a program's "other failures". Accordingly the checker warns about a `rescue RuntimeError` whose body cannot raise one (`the begin body never raises RuntimeError [unused-rescue]`, with the hint that `Thread.value` / `Thread.join` count only in a program that calls `Thread.raise`). In the example below the clause catches nothing, and the `ArgumentError` from `String.undump` stops the program.

```ruby
begin
  raise "plain"
rescue RuntimeError => e
  p(e)                             # => #<RuntimeError: plain>
end
```

```ruby error
begin
  String.undump("\"abc")
rescue RuntimeError => e           # !> warning: rescue RuntimeError: the begin body never raises RuntimeError [unused-rescue]
  p(e)
end
```

## ArgumentError

`ArgumentError.new(message)`

An argument's value is not acceptable. Raised by: `Integer`, `Float` and `Rational` for a String they cannot read; `format` for too few or too many arguments, a value that is not a number, or a missing key; `rand(0)` and a negative bound; a negative size (`Array.first(a, -1)`); `Integer ** negative Integer`; `Regexp.new(s, flags)` with bad flags; `Time.new`, `Time.at`, `Time.now` with a value out of range or a bad zone; `Integer.to_s(n, base)` with a bad base; `String.unpack` with a bad format; `String.ljust` with an empty pad, `tr` with a reversed range, `String.undump` of a String that is not a dumped one (`unterminated dumped string`, Ruby's RuntimeError); `ENV.set("", v)` and `Process.clock_gettime(999)` (an argument the OS refuses); `Zlib` on damaged data (`incorrect header check`); a block called with the wrong number of arguments. Elements that cannot be compared (`Array.sort`, `max`, `min`, `Tuple.max`, `Set.sort`, and the `*_by` forms through their block's results) are a static `type` problem when the checker sees the element types; the run-time `ArgumentError` is reached only where it cannot, as with a NaN.

```ruby
begin
  Array.sort(Any[1.0, Float.NAN])
rescue ArgumentError => e
  puts(Exception.message(e))       # => cannot compare elements of types Float
end
begin
  2 ** -1
rescue ArgumentError => e
  puts(Exception.message(e))       # => Integer ** negative Integer (2 ** -1) is an error; for a Rational, write 2r ** -1
end
begin
  String.undump("\"abc")
rescue ArgumentError => e
  puts(Exception.message(e))       # => unterminated dumped string
end
```

```ruby error
Array.sort(Any[1, "a"])          # !> Array.sort: elements compared in order may be (Integer, String), which cannot be compared [type]
```

## TypeError

`TypeError.new(message)`

A value of the wrong type: an operation given an argument of another type, an operator the left operand's type does not support, a write of another type into a typed Array (`Integer[]`, ...), multiple assignment from a value that is neither a Tuple nor an Array, an in-place change of a frozen String (a Hash key, a Set element, a Symbol's name, an element of `ARGV`: `cannot change this String in place: it is a Hash key, a Set element, a Symbol's name, or a program argument`), `Exception.message` given a value that is not an exception. A Ruby `TypeError` or `NoMethodError` inside an operation is this type too, where the checker could not see the element types: `Array.pack(Any["a"], "C")` (`no implicit conversion of String into Integer`), `Range.first(1.0..2.0, 2)` (`can't iterate from Float`), `ENV.replace` with a non-String key or value under `--strict=0` (`keys and values must be String, got String => Integer`). The checks before running stop most of these statically; at run time only the places the checker let through reach it.

```ruby
begin
  Array.pack(Any["a"], "C")
rescue TypeError => e
  puts(Exception.message(e))       # => no implicit conversion of String into Integer
end
```

```ruby error
Array.push(Integer[], "a")         # !> Array.push: an element must be Integer, but is String
```

## KeyError

`KeyError.new(message)`

A missing key: `Hash.fetch` (without a default and a block), `Hash.fetch_values`, `ENV.fetch`, a Record pattern naming a field the Record lacks. A missing key in `format`'s `%<name>` is an `ArgumentError`.

```ruby
h = Hash.of("a" => 1)
begin
  Hash.fetch(h, "b")
rescue KeyError => e
  puts(Exception.message(e))       # => key not found: "b"
end
p(Hash.fetch(h, "b", 0))           # => 0
```

## IndexError

`IndexError.new(message)`

An index out of range: `Array.fetch`; an index outside a Tuple that reaches run time (a literal index is stopped statically); a write past the end of an Array of T (`Integer[1, 2][5] = 1`, since the gap would be nil); `Array.insert` past the end (`index 3 is past the end of the Array (length 1); the gap would be nil`, where Ruby fills the gap); `MatchData.begin(m, 5)` for a group the Regexp lacks (`index 5 out of matches`); `Array.transpose` with rows of different lengths; `String.byteindex` with an offset inside a character. `x[k]` gives nil outside the range and never raises.

```ruby
def at(t, i) = t[i]
begin
  at([1, 2], 5)
rescue IndexError => e
  puts(Exception.message(e))       # => index 5 is outside a Tuple of length 2
end
begin
  Array.fetch(Any[3, 1], 9)
rescue IndexError => e
  puts(Exception.message(e))       # => index 9 outside of array bounds: -2...2
end
```

## ZeroDivisionError

`ZeroDivisionError.new(message)`

Division of an Integer (or a Rational) by zero: `/`, `%`, `Integer.divmod`, `div`, `modulo`, `remainder`, `ceildiv`, `Rational(a, 0)`, `Rational.quo`. A Float's `1.0 / 0` is Infinity, as in Ruby, not an exception.

```ruby
begin
  7 % 0
rescue ZeroDivisionError => e
  puts(Exception.message(e))       # => divided by 0
end
p(1.0 / 0)                         # => Infinity
```

## RangeError

`RangeError.new(message)`

A value outside what can be represented: an endless Range given to an operation that needs a finite one (`Range.to_a(1..)`, `Range.last(1..)`, `Range.sort`, ...), a beginless one to `Range.first` (`cannot get the first element of beginless range`), an Integer that is no character in `Integer.chr` (`256 out of char range`), a negative exponent in `Integer.pow(2, -1, 7)`, a Complex initial value in `Range.sum`.

```ruby
begin
  Range.to_a(1..)
rescue RangeError => e
  puts(Exception.message(e))       # => cannot do this on an endless Range 1..
end
begin
  Integer.chr(256)
rescue RangeError => e
  puts(Exception.message(e))       # => 256 out of char range
end
```

## FloatDomainError

`FloatDomainError.new(message)`

NaN or Infinity turned into an Integer or a Rational: `Float.to_i`, `Arithmetic.to_i`, `Arithmetic.round`, `floor`, `ceil`, `truncate`, `Float.to_r`, `Float.rationalize`, `Float.numerator`, `denominator`, `Kernel.Integer(x)`, `Kernel.Rational(x)`. The message is the value's name. All of them are rescuable.

```ruby
begin
  Float.to_i(Float.INFINITY)
rescue FloatDomainError => e
  puts(Exception.message(e))       # => Infinity
end
begin
  Float.to_r(Float.NAN)
rescue FloatDomainError => e
  puts(Exception.message(e))       # => NaN
end
begin
  Integer(Float.NAN)
rescue FloatDomainError => e
  puts(Exception.message(e))       # => NaN
end
```

## IOError

`IOError.new(message)`

A failure of input or output: the File and Dir operations (`File.read`, `File.write`, `File.open`, `Dir.mkdir`, `Dir.children`, ...; Ruby's `Errno::ENOENT` and company become this one type, with Ruby's message), an operation on a closed IO, `IO.size` of a stream that is not a file (`not a file`), the terminal operations (`IO.winsize`, ...) on an IO that is not a terminal (`not a terminal`), socket errors (connection refused, reset, a name that does not resolve, timeout), `Queue.push` to a closed Queue. A Ruby `EOFError` inside an operation becomes this type too, not Sake's `EOFError`.

```ruby
begin
  File.read("no-such-file")
rescue IOError => e
  puts(Exception.message(e))       # => No such file or directory @ rb_sysopen - no-such-file
end
begin
  IO.size(IO.stdin)
rescue IOError => e
  puts(Exception.message(e))       # => not a file
end
q = Queue.new
Queue.close(q)
begin
  Queue.push(q, 1)
rescue IOError => e
  puts(Exception.message(e))       # => push to a closed Queue
end
```

## EOFError

`EOFError.new(message)`

A read at the end of the input. Ruby's `IO#readline` and the like raise it, but Sake's reading operations (`IO.read(io, n)`, `IO.gets`, `IO.getc`, `gets`) return nil at the end, so no built-in operation raises it (a Ruby `EOFError` inside an operation becomes `IOError`). A program can use it for its own "no more input". It is a type of its own, which `rescue IOError` does not catch.

```ruby
def next_token(xs) = Array.shift(xs) || raise(EOFError, "no more tokens")
begin
  next_token(Any[])
rescue EOFError => e
  puts(Exception.message(e))       # => no more tokens
end
```

## EncodingError

`EncodingError.new(message)`

A mistake in a String's encoding: a byte that `String.encode` cannot convert, concatenating or comparing Strings of incompatible encodings, and whatever else makes Ruby raise an `EncodingError` (`Encoding::UndefinedConversionError`, ...) inside an operation.

```ruby
begin
  String.encode("\xff", "UTF-16")
rescue EncodingError => e
  puts(Exception.message(e))       # => "\xFF" on UTF-8
end
```

## RegexpError

`RegexpError.new(message)`

An invalid regular expression: a literal with interpolation `/#{s}/` whose pattern is invalid, `Regexp.new(s)` with an invalid pattern, a String operation given an invalid pattern String (`String.match?("a+c", "+")`). A pattern written directly in a literal is a syntax error, stopped before running.

```ruby
s = "("
begin
  r = /#{s}/
rescue RegexpError => e
  puts(Exception.message(e))       # => end pattern with unmatched parenthesis: /(/
end
begin
  Regexp.new("(")
rescue RegexpError => e
  puts(Exception.message(e))       # => end pattern with unmatched parenthesis: /(/
end
begin
  String.match?("a+c", "+")
rescue RegexpError => e
  puts(Exception.message(e))       # => target of repeat operator is not specified: /+/
end
```

## NoMatchingPatternError

`NoMatchingPatternError.new(message)`

No branch of a `case`/`in` matches the value and there is no `else`, or `x => pattern` does not match. The checker finds from the value's type that no branch can match and stops the program as a `type` problem (the example below), so it is raised at run time only where the checker let the code through, for instance in a program run with `--strict=0`. The message describes the value that did not match.

```ruby error
def kind(x)
  case x
  in Integer then "int"
  end
end
kind("s")                          # !> case/in: no `in` branch matches String
```

## ThreadError

`ThreadError.new(message)`

A mistake in the use of threads: `Mutex.lock` or `Mutex.synchronize` on a Mutex the thread already holds, `Mutex.unlock` on a Mutex it does not hold, `Thread.join` of the current thread. A deadlock, every thread waiting, also ends the program with Sake's `ThreadError` ([Exceptions and errors](../08-exceptions.md)).

```ruby
m = Mutex.new
Mutex.lock(m)
begin
  Mutex.lock(m)
rescue ThreadError => e
  puts(Exception.message(e))       # => deadlock; recursive locking
end
begin
  Mutex.unlock(Mutex.new)
rescue ThreadError => e
  puts(Exception.message(e))       # => Attempt to unlock a mutex which is not locked
end
begin
  Thread.join(Thread.current)
rescue ThreadError => e
  puts(Exception.message(e))       # => Target thread must not be current thread
end
```

# Integer

An Integer is a whole number of any size, as Ruby's: `42`, `-7`, `1_000_000`, `0x1f`, `0b101`, `0o17`, `2 ** 100` ([Values and types](../03-values.md)). The conversion `Integer("42")` and `Integer(3.9)` is an operation of Kernel, not of this namespace (`Kernel.Integer`; an `ArgumentError` on bad input). Integers are values: no operation here changes its subject, and every result is a new value.

The operators on Integers come from the modules Integer includes ([Operators and indexing](../05-operators.md)): `Arithmetic` gives `+ - * / % **` and unary `-x`, `Comparable` gives `<=> < <= > >=`, `Bitwise` gives `& | ^ << >>` and unary `~x`, and `==` and `!=` are Kernel's. `a + b` dispatches on the type of `a`, so with an Integer on the left it runs `Integer.+(a, b)`; the entries `Integer.+(x, y)` and so on below are those function forms. The right operand's type comes from a **closed table**: Integer with Integer gives an Integer, with a Float a Float, with a Rational a Rational (except `**`, whose result with a Rational exponent is `Rational | Float`), with a Complex a Complex; `<` and the like compare with Integer, Float, and Rational; the bit operators take an Integer only. A right operand of another type (a String, nil, a Symbol) is a `type` problem statically (`the operands are (Integer, String), which the left operand's type does not support`) and a `TypeError` at run time. The function form also requires its first argument to be an Integer (`Integer.+(1.0, 2)` is a `type` problem: use `Float.+`, or the operator).

Two things differ from Ruby: `Integer ** negative Integer` raises `ArgumentError` instead of giving a Rational, so that the type of `a ** b` never depends on the value of `b`, and no method is called on a value (`1.+(2)` is rejected; write `Integer.+(1, 2)` or `1 + 2`). `/` and `%` floor as Ruby's do (`-7 / 2` is -4), and dividing by zero raises `ZeroDivisionError`.

The rounding family `round`, `floor`, `ceil`, `truncate`, and `abs`, `to_f`, `to_i`, `zero?` also exist in the module `Arithmetic` ([Arithmetic](Arithmetic.md)), where they take any real number (`Arithmetic.round(x)` for an Integer, Float, or Rational `x`, as Ruby's `x.round`); the entries below are the Integer-only forms, which reject a Float subject with a hint to use `Arithmetic`. Operations whose result may be nil do not exist in this namespace: every result is a definite Integer, Float, Rational, String, Boolean, Tuple, or Array.

## Integer[]

`Integer[*Any]`

Makes an **Array of Integers**: a typed Array whose every element must be an Integer, checked on construction and on every write (`Array.push`, `a[i] = v`, ...). A literal element of another type is rejected before running (`element 2 must be Integer, got String`); a write of another type is a `type` problem statically and a `TypeError` at run time. The empty Array of Integers is `Integer[]`. Compare `Any[1, 2]`, whose element type is inferred and widens with what is written into it.

```ruby
a = Integer[1, 2, 3]
Array.push(a, 4)
p(a)                         # => [1, 2, 3, 4]
p(Array.size(Integer[]))     # => 0
```

```ruby error
Array.push(Integer[], 1.5)   # !> Array.push: an element must be Integer, but is Float
```

## +, -, *

`Integer.+(x, Any)`

`Integer.-(x, Any)`

`Integer.*(x, Any)`

Addition, subtraction, and multiplication, exact at any size. The result's type follows the right operand: Integer with an Integer, Float with a Float, Rational with a Rational, Complex with a Complex. Any other right operand is a `type` problem. The unary minus `-x` is `Integer.-@`, written as the operator only.

```ruby
p(Integer.+(1, 2))           # => 3
p(Integer.-(1, 2))           # => -1
p(3 * 4)                     # => 12
p(2 + 1.5)                   # => 3.5
p(1 + 1r)                    # => (2/1)
p(2 ** 64 + 1)               # => 18446744073709551617
```

```ruby error
p(1 + "a")                   # !> the operands are (Integer, String), which the left operand's type does not support
```

## /, %

`Integer./(x, Any)`

`Integer.%(x, Any)`

Division and remainder. With two Integers both **floor**, as Ruby's: the quotient rounds toward minus infinity and the remainder takes the sign of the divisor (`-7 / 2` is -4, `-7 % 3` is 2, `7 % -3` is -2). An Integer divisor of 0 raises `ZeroDivisionError`. With a Float on the right the result is a Float and division by `0.0` gives `Infinity` (IEEE); with a Rational, a Rational. See also `div`, `modulo`, `remainder`, `divmod`, `fdiv`, and `ceildiv`.

```ruby
p(7 / 2)                     # => 3
p(-7 / 2)                    # => -4
p(7 % 3)                     # => 1
p(-7 % 3)                    # => 2
p(Integer./(7, 2.0))         # => 3.5
p(1 / 0.0)                   # => Infinity
```

```ruby error
p(Integer./(1, 0))           # !> ZeroDivisionError: Integer./: divided by 0
```

## **

`Integer.**(x, Any)`

Exponentiation. Integer with a non-negative Integer exponent is an exact Integer; a Float exponent gives a Float. A **Rational exponent** gives a Rational when it is integral (`2 ** 2r` is `(4/1)`) and a Float otherwise (`2 ** (1r/2)` is `1.4142135623730951`), so the checker types that result `Rational | Float`: narrow it with `case`/`in` before using it as one of the two. Unlike Ruby, a **negative Integer exponent is an `ArgumentError`** (Ruby gives a Rational); the message tells how to ask for a Rational (`2r ** -1`). A negative base with a fractional exponent (`(-8) ** 0.5`) is a `Math::DomainError`, where Ruby gives a Complex. `**` binds tighter than unary minus, so `-2 ** 2` is -4, as in Ruby; it is right-associative (`2 ** 3 ** 2` is 512). See also `pow`.

```ruby
p(2 ** 10)                   # => 1024
p(Integer.**(2, 0))          # => 1
p(2 ** 0.5)                  # => 1.4142135623730951
p(2 ** 2r)                   # => (4/1)
p(2 ** (1r/2))               # => 1.4142135623730951
p(-2 ** 2)                   # => -4
x = 2 ** 2r
case x
in Rational then p(Rational.numerator(x))   # => 4
in Float then p(x)
end
```

```ruby error
p(2 ** -1)                   # !> ArgumentError: Arithmetic.**: Integer ** negative Integer (2 ** -1) is an error; for a Rational, write 2r ** -1
```

```ruby error
p((-8) ** 0.5)               # !> Math::DomainError: Arithmetic.**: -8 ** 0.5 is not a real number (a negative base with a fractional exponent)
```

```ruby error
p(Rational.numerator(2 ** 2r))   # !> Rational.numerator: argument 1 must be Rational, but can be Float
```

## ==, !=

`Integer.==(x, Any)`

`Integer.!=(x, Any)`

Equality (`!=` is its negation). Numbers compare across types: `1 == 1.0`, `1 == 1r`, and `1 == Complex(1, 0)` are true. A value of another type is never equal (`1 == "1"` is false, `1 == nil` is false), as in Ruby; the function form `Integer.==(x, y)` gives the same false (and `!=` true) for a right operand of any other type, without an error.

```ruby
p(1 == 1)                    # => true
p(1 == 1.0)                  # => true
p(1 != 1)                    # => false
p(1 == "1")                  # => false
p(1 == nil)                  # => false
p(Integer.==(1, nil))        # => false
p(Integer.==(1, "a"))        # => false
p(Integer.!=(1, "a"))        # => true
```

## <, <=, >, >=

`Integer.<(x, Any)`

`Integer.<=(x, Any)`

`Integer.>(x, Any)`

`Integer.>=(x, Any)`

Ordering. The right operand is an Integer, Float, or Rational; a Complex has no order, and a String or nil is a `type` problem (`Comparable.<: the operands are (Integer, String) ...`). The result is true or false.

```ruby
p(1 < 2)                     # => true
p(1 <= 1)                    # => true
p(2 >= 3)                    # => false
p(1 < 1.5)                   # => true
p(Integer.<(1, 2r))          # => true
```

```ruby error
p(1 < "a")                   # !> Comparable.<: the operands are (Integer, String), which the left operand's type does not support
```

## <=>

`Integer.<=>(x, Any)`

-1, 0, or 1 as the left operand is smaller than, equal to, or larger than the right (an Integer, Float, or Rational). Between numbers the result is never nil, so the checker types it as an Integer. Another right operand is a `type` problem.

```ruby
p(1 <=> 2)                   # => -1
p(2 <=> 1)                   # => 1
p(1 <=> 1)                   # => 0
p(Integer.<=>(1, 1.5))       # => -1
```

## &, |, ^

`Integer.&(x, Any)`

`Integer.|(x, Any)`

`Integer.^(x, Any)`

Bitwise and, or, and exclusive or on the two's-complement bits, as Ruby's; negative numbers behave as if they had infinitely many 1 bits above. Both operands must be Integers: a Float on the right is a `type` problem (`Bitwise.&: the operands are (Integer, Float) ...`). The unary complement `~x` is `Integer.~`, written as the operator only.

```ruby
p(6 & 3)                     # => 2
p(6 | 3)                     # => 7
p(6 ^ 3)                     # => 5
p(~5)                        # => -6
p(Integer.&(6, 3))           # => 2
```

```ruby error
p(6 & 1.5)                   # !> Bitwise.&: the operands are (Integer, Float), which the left operand's type does not support
```

## <<, >>

`Integer.<<(x, Any)`

`Integer.>>(x, Any)`

Shift left and shift right by an Integer number of bits, as Ruby's: `x << n` is `x * 2**n` (exact, growing as needed), `x >> n` floors (`-1 >> 1` is -1). A negative count shifts the other way (`1 << -1` is 0). The right operand must be an Integer.

```ruby
p(1 << 4)                    # => 16
p(256 >> 4)                  # => 16
p(-1 >> 1)                   # => -1
p(1 << 100 == 2 ** 100)      # => true
```

## bit_length

`Integer.bit_length(x)`

The number of bits needed to represent the number in two's complement, without the sign bit, as Ruby's: 255 needs 8, 256 needs 9, 0 and -1 need 0, -256 needs 8.

```ruby
p(Integer.bit_length(255))   # => 8
p(Integer.bit_length(256))   # => 9
p(Integer.bit_length(0))     # => 0
p(Integer.bit_length(-1))    # => 0
```

## allbits?, anybits?, nobits?

`Integer.allbits?(x, Integer)`

`Integer.anybits?(x, Integer)`

`Integer.nobits?(x, Integer)`

Whether all, any, or none of the bits set in the mask are set in the number: `x & mask == mask`, `x & mask != 0`, `x & mask == 0`.

```ruby
p(Integer.allbits?(6, 2))    # => true
p(Integer.allbits?(6, 3))    # => false
p(Integer.anybits?(6, 3))    # => true
p(Integer.nobits?(6, 1))     # => true
```

## div, modulo, remainder

`Integer.div(x, Integer|Float|Rational)`

`Integer.modulo(x, Integer)`

`Integer.remainder(x, Integer)`

`div` is the floored quotient as an **Integer** even for a Float or Rational divisor (`Integer.div(7, 2.0)` is 3, where `7 / 2.0` is 3.5). `modulo` is `%`: the remainder with the sign of the divisor. `remainder` is the remainder with the sign of the **dividend**, as Ruby's (`Integer.remainder(-7, 3)` is -1, where `-7 % 3` is 2). A divisor of 0 (or `0.0`) raises `ZeroDivisionError`.

```ruby
p(Integer.div(-7, 2))        # => -4
p(Integer.div(7, 2.0))       # => 3
p(Integer.modulo(-7, 3))     # => 2
p(Integer.remainder(-7, 3))  # => -1
p(Integer.remainder(7, -3))  # => 1
```

```ruby error
p(Integer.modulo(7, 0))      # !> ZeroDivisionError: Integer.modulo: divided by 0
```

## divmod

`Integer.divmod(x, Integer)`

The Tuple `[quotient, remainder]` of the floored division, so that `q * b + r == a`, with `r` taking the sign of the divisor. A divisor of 0 raises `ZeroDivisionError`. Unlike Ruby's, the divisor must be an Integer (`Float.divmod` takes a Float subject).

```ruby
p(Integer.divmod(7, 2))      # => [3, 1]
p(Integer.divmod(-7, 2))     # => [-4, 1]
q, r = Integer.divmod(17, 5)
p([q, r])                    # => [3, 2]
```

```ruby error
p(Integer.divmod(7, 0))      # !> ZeroDivisionError: Integer.divmod: divided by 0
```

## ceildiv

`Integer.ceildiv(x, Integer)`

The quotient rounded toward plus infinity (Ruby's `ceildiv`): `Integer.ceildiv(7, 2)` is 4, `Integer.ceildiv(-7, 2)` is -3. A divisor of 0 raises `ZeroDivisionError`.

```ruby
p(Integer.ceildiv(7, 2))     # => 4
p(Integer.ceildiv(-7, 2))    # => -3
p(Integer.ceildiv(6, 2))     # => 3
```

## fdiv

`Integer.fdiv(x, Integer)`

The quotient as a Float, `Integer.to_f(a) / b`. A divisor of 0 does not raise: the result is `Infinity`, `-Infinity`, or `NaN` (for `0 / 0`), as Ruby's. Unlike Ruby's, the divisor must be an Integer.

```ruby
p(Integer.fdiv(7, 2))        # => 3.5
p(Integer.fdiv(1, 3))        # => 0.3333333333333333
p(Integer.fdiv(1, 0))        # => Infinity
```

## pow

`Integer.pow(x, Integer, [Integer])`

`Integer.pow(a, b)` is `a ** b` for an Integer exponent, with the same `ArgumentError` for a negative one. `Integer.pow(a, b, m)` is `a ** b` modulo `m`, computed without building the large power, as Ruby's; the result takes the sign of `m`. With a modulus, a negative exponent is a `RangeError` (Ruby's message, which counts the exponent as the 1st argument) and a modulus of 0 a `ZeroDivisionError`; both can be rescued.

```ruby
p(Integer.pow(2, 10))        # => 1024
p(Integer.pow(2, 10, 1000))  # => 24
p(Integer.pow(3, 100, 7))    # => 4
p(Integer.pow(2, 10, -7))    # => -5
begin
  Integer.pow(2, 3, 0)
rescue ZeroDivisionError => e
  p(Exception.message(e))    # => "divided by 0"
end
```

```ruby error
p(Integer.pow(2, -1))        # !> ArgumentError: Integer.pow: Integer ** negative Integer (2 ** -1) is an error; for a Rational, write 2r ** -1
```

```ruby error
p(Integer.pow(2, -1, 7))     # !> RangeError: Integer.pow: Integer#pow() 1st argument cannot be negative when 2nd argument specified
```

## gcd, lcm

`Integer.gcd(x, Integer)`

`Integer.lcm(x, Integer)`

The greatest common divisor and the least common multiple, always non-negative, as Ruby's. `gcd(0, n)` is `|n|` and `gcd(0, 0)` is 0; `lcm(0, n)` is 0.

```ruby
p(Integer.gcd(12, 18))       # => 6
p(Integer.gcd(-12, 18))      # => 6
p(Integer.gcd(0, 0))         # => 0
p(Integer.lcm(4, 6))         # => 12
p(Integer.lcm(0, 6))         # => 0
```

## gcdlcm

`Integer.gcdlcm(x, Integer)`

The Tuple `[gcd, lcm]`.

```ruby
g, l = Integer.gcdlcm(4, 6)
p([g, l])                    # => [2, 12]
```

## sqrt

`Integer.sqrt(x)`

The integer square root: the largest Integer whose square does not exceed the number (Ruby's `Integer.sqrt`), exact at any size, where `Math.sqrt` would give a Float. A negative number raises `Math::DomainError` (Ruby's message), which can be rescued.

```ruby
p(Integer.sqrt(16))          # => 4
p(Integer.sqrt(17))          # => 4
p(Integer.sqrt(10 ** 20))    # => 10000000000
```

```ruby error
p(Integer.sqrt(-1))          # !> Math::DomainError: Integer.sqrt: Numerical argument is out of domain - "isqrt"
```

## abs, magnitude

`Integer.abs(x)`

`Integer.magnitude(x)`

The absolute value, an Integer. `Arithmetic.abs(x)` does the same for any real number.

```ruby
p(Integer.abs(-5))           # => 5
p(Integer.magnitude(5))      # => 5
```

## even?, odd?

`Integer.even?(x)`

`Integer.odd?(x)`

Whether the number is even or odd; 0 is even, -3 is odd.

```ruby
p(Integer.even?(4))          # => true
p(Integer.even?(-3))         # => false
p(Integer.odd?(-3))          # => true
```

## zero?, positive?, negative?

`Integer.zero?(x)`

`Integer.positive?(x)`

`Integer.negative?(x)`

Whether the number is 0, above 0, or below 0. 0 is neither positive nor negative. `Arithmetic.zero?(x)` takes any real number.

```ruby
p(Integer.zero?(0))          # => true
p(Integer.positive?(0))      # => false
p(Integer.negative?(-1))     # => true
```

## succ, next

`Integer.succ(x)`

`Integer.next(x)`

The number plus one.

```ruby
p(Integer.succ(5))           # => 6
p(Integer.next(-1))          # => 0
```

## pred

`Integer.pred(x)`

The number minus one.

```ruby
p(Integer.pred(5))           # => 4
p(Integer.pred(0))           # => -1
```

## times

`Integer.times(x) { }`

Calls the block with 0, 1, ..., n-1 and returns n (the subject), as Ruby's. For n of 0 or less the block is not called. The block is **required** (Ruby's `times` without a block gives an Enumerator); it takes one parameter. `break v` ends the loop and makes `v` the result; `next` goes to the next count; `return` inside a function returns from the function.

```ruby
n = Integer.times(3) { |i| puts(i) }   # => 0
                                       # => 1
                                       # => 2
p(n)                                   # => 3
sum = 0
Integer.times(4) { |i| sum += i }
p(sum)                                 # => 6
p(Integer.times(0) { |i| puts(i) })    # => 0
p(Integer.times(3) { |i| break i * 10 if i == 1 })   # => 10
```

```ruby error
Integer.times(3)                       # !> Integer.times requires a block
```

## upto

`Integer.upto(x, Integer) { }`

Calls the block with a, a+1, ..., b and returns a (the subject), as Ruby's `a.upto(b)`. When a is above b the block is not called. The block is required.

```ruby
p(Integer.upto(1, 3) { |i| puts(i) })  # => 1
                                       # => 2
                                       # => 3
                                       # => 1
p(Integer.upto(3, 1) { |i| puts(i) })  # => 3
```

## downto

`Integer.downto(x, Integer) { }`

Calls the block with a, a-1, ..., b and returns a, as Ruby's `a.downto(b)`. When a is below b the block is not called. The block is required.

```ruby
p(Integer.downto(3, 1) { |i| puts(i) })  # => 3
                                         # => 2
                                         # => 1
                                         # => 3
```

## step

`Integer.step(x, Integer, Integer) { }`

`Integer.step(a, limit, step)` calls the block with a, a+step, a+2·step, ... while the value does not pass the limit (in the direction of the step), and returns a, as Ruby's `a.step(limit, step)`. Both the limit and the step are required Integers (Ruby's Float steps and keyword forms do not exist); a step of 0 is an `ArgumentError`. A negative step counts down.

```ruby
p(Integer.step(1, 10, 3) { |i| puts(i) })   # => 1
                                            # => 4
                                            # => 7
                                            # => 10
                                            # => 1
p(Integer.step(10, 1, -3) { |i| print(i, " ") })   # => 10 7 4 1 10
```

```ruby error
Integer.step(1, 10, 0) { |i| puts(i) }      # !> ArgumentError: Integer.step: step can't be 0
```

## clamp

`Integer.clamp(x, Integer, Integer)`

The number limited to the interval: `lo` when below it, `hi` when above it, otherwise itself. Unlike Ruby's, there is no Range form (`clamp(1..10)`). A `lo` above `hi` is an `ArgumentError` (`min argument must be less than or equal to max argument`).

```ruby
p(Integer.clamp(5, 1, 10))   # => 5
p(Integer.clamp(-5, 1, 10))  # => 1
p(Integer.clamp(50, 1, 10))  # => 10
```

```ruby error
p(Integer.clamp(5, 10, 1))   # !> ArgumentError: Integer.clamp: min argument must be less than or equal to max argument
```

## between?

`Integer.between?(x, Integer, Integer)`

True when `lo <= x <= hi`. Both bounds are included; with `lo` above `hi` the result is false.

```ruby
p(Integer.between?(5, 1, 10))   # => true
p(Integer.between?(1, 1, 10))   # => true
p(Integer.between?(0, 1, 10))   # => false
```

## round

`Integer.round(x, [Integer])`

Without digits, the number itself. With a negative digit count -d, the number rounded to a multiple of 10^d, halves away from zero, as Ruby's (`Integer.round(1250, -2)` is 1300, `Integer.round(-1250, -2)` is -1300); a positive count leaves the number unchanged. The result is always an Integer. Only for an Integer subject: a Float is a `type` problem whose hint names `Arithmetic.round(x)`, which rounds any real number.

```ruby
p(Integer.round(1234))       # => 1234
p(Integer.round(1234, -2))   # => 1200
p(Integer.round(1250, -2))   # => 1300
p(Integer.round(-1250, -2))  # => -1300
p(Integer.round(1234, 2))    # => 1234
```

```ruby error
p(Integer.round(2.5))        # !> Integer.round: argument 1 must be Integer, but is Float
```

## floor, ceil

`Integer.floor(x, [Integer])`

`Integer.ceil(x, [Integer])`

Without digits, the number itself. With a negative digit count -d, the number rounded down (`floor`, toward minus infinity) or up (`ceil`, toward plus infinity) to a multiple of 10^d, as Ruby's. Always an Integer. For a Float, use `Arithmetic.floor` or `Float.floor`.

```ruby
p(Integer.floor(1234, -2))   # => 1200
p(Integer.floor(-1234, -2))  # => -1300
p(Integer.ceil(1234, -2))    # => 1300
p(Integer.ceil(-1234, -2))   # => -1200
p(Integer.floor(5))          # => 5
```

## truncate

`Integer.truncate(x, [Integer])`

Without digits, the number itself. With a negative digit count -d, the number rounded toward zero to a multiple of 10^d. Always an Integer.

```ruby
p(Integer.truncate(1234, -2))    # => 1200
p(Integer.truncate(-1234, -2))   # => -1200
```

## digits

`Integer.digits(x)`

A new Array of the decimal digits, least significant first, as Ruby's `digits` without a base (`Integer.digits(1234)` is `[4, 3, 2, 1]`; 0 gives `[0]`). It is a typed `Integer[]`: pushing a value of another type is a `type` problem. A negative number raises `Math::DomainError` (`out of domain`). Unlike Ruby's, there is no base argument: use `to_s(n, base)` and `String.chars`.

```ruby
p(Integer.digits(1234))      # => [4, 3, 2, 1]
p(Integer.digits(0))         # => [0]
p(Array.sum(Integer.digits(999)))   # => 27
d = Integer.digits(12)
Array.push(d, 9)
p(d)                         # => [2, 1, 9]
```

```ruby error
Array.push(Integer.digits(12), "x")   # !> Array.push: an element must be Integer, but is String
```

```ruby error
p(Integer.digits(-1))        # !> Math::DomainError: Integer.digits: out of domain
```

## to_s

`Integer.to_s(x, [Integer])`

The number written in the given base (2 to 36; default 10), as a String; lowercase letters stand for digits above 9, and a negative number gets a leading `-`. A base outside 2..36 is an `ArgumentError` (`invalid radix 1`). `String.to_i(s, base)` reads it back.

```ruby
p(Integer.to_s(255))         # => "255"
p(Integer.to_s(255, 2))      # => "11111111"
p(Integer.to_s(-255, 16))    # => "-ff"
p(Integer.to_s(35, 36))      # => "z"
```

```ruby error
p(Integer.to_s(255, 37))     # !> ArgumentError: Integer.to_s: invalid radix 37
```

## to_f

`Integer.to_f(x)`

The number as a Float (the nearest one for a number too large to be exact). `Arithmetic.to_f(x)` takes any real number.

```ruby
p(Integer.to_f(3))           # => 3.0
```

## to_i, to_int

`Integer.to_i(x)`

`Integer.to_int(x)`

The number itself. They exist for code written for any real number; `Arithmetic.to_i(x)` truncates a Float or Rational to an Integer. Note that `Integer(s)`, the conversion of a String, is Kernel's, not this operation.

```ruby
p(Integer.to_i(3))           # => 3
p(Integer.to_int(-3))        # => -3
```

## to_r

`Integer.to_r(x)`

The number as a Rational with denominator 1.

```ruby
p(Integer.to_r(3))           # => (3/1)
```

## rationalize

`Integer.rationalize(x, [Integer|Float|Rational])`

The number as a Rational, as `to_r`; the optional tolerance is accepted for symmetry with `Float.rationalize` and makes no difference for an Integer.

```ruby
p(Integer.rationalize(3))        # => (3/1)
p(Integer.rationalize(3, 0.5))   # => (3/1)
```

## numerator, denominator

`Integer.numerator(x)`

`Integer.denominator(x)`

Seen as a fraction, the number is its own numerator over 1.

```ruby
p(Integer.numerator(6))      # => 6
p(Integer.denominator(6))    # => 1
```

## integer?

`Integer.integer?(x)`

Always true for an Integer (Ruby's `Numeric#integer?`). Use `x in Integer` to test the type of a value that may be something else.

```ruby
p(Integer.integer?(6))       # => true
```

## size

`Integer.size(x)`

The number of bytes in the machine representation, as Ruby's: 8 for a number that fits a machine word, more for larger ones. For the number of significant bits use `bit_length`.

```ruby
p(Integer.size(1))           # => 8
p(Integer.size(2 ** 100))    # => 13
```

## chr

`Integer.chr(x)`

A one-character String whose byte is the number, which must be 0 to 255; outside that it raises `RangeError` (`256 out of char range`), which can be rescued. As Ruby's `chr` without an encoding, 0 to 127 give an ASCII String and 128 to 255 a one-byte binary String, which raises `EncodingError` when joined with UTF-8 text. `String.ord` is the reverse. For a code point above 255 build the String another way (`format("%c", n)`).

```ruby
p(Integer.chr(65))           # => "A"
puts(Integer.chr(97))        # => a
p(Integer.chr(10))           # => "\n"
p(String.ord(Integer.chr(200)))   # => 200
begin
  Integer.chr(200) + "é"
rescue EncodingError => e
  p(Exception.message(e))    # => "incompatible character encodings: BINARY (ASCII-8BIT) and UTF-8"
end
```

```ruby error
p(Integer.chr(256))          # !> RangeError: Integer.chr: 256 out of char range
```

## ord

`Integer.ord(x)`

The number itself (Ruby's `Integer#ord`), the counterpart of `String.ord`, which gives the code point of a String's first character.

```ruby
p(Integer.ord(65))           # => 65
p(String.ord("A"))           # => 65
```

# Float

A Float is an IEEE 754 double-precision floating-point number. The literals are `1.5`, `2.0`, `1e-3` ([Values and types](../03-values.md)). The conversion from a String, `Float("1.5")`, is a Kernel operation, and from an Integer it is `Integer.to_f(n)`. There is no implicit conversion of an Integer where a Float is expected: `Float[1]` and `Float.clamp(x, 1, 2)` are `type` problems statically.

The operators on Floats are `+`, `-`, `*`, `/`, `%`, `**` and `==`, `!=`, `<`, `<=`, `>`, `>=`, `<=>`. The right operand may be an Integer, a Float, a Rational or a Complex, and the result type follows the closed table: with an Integer or a Rational the result is a Float, with a Complex it is a Complex ([Operators and indexing](../05-operators.md)). The entries `Float.+(x, y)` and so on below are the function forms of those operators; since the left operand is known to be a Float, the checker is stricter about the right one.

As in Ruby, dividing by zero gives `Infinity`, `-Infinity` or `NaN` rather than an exception (`%`, `modulo` and `divmod` are the exceptions: `ZeroDivisionError`), and the operations that turn a NaN or an Infinity into an Integer (`to_i`, `floor`, `ceil`, `round`, `truncate`, `to_r`, `numerator`, `denominator`) raise `FloatDomainError`. Ruby's constants `Float::INFINITY` and so on are read in Sake as operations without arguments, `Float.INFINITY` (Sake has no value constants). The main differences from Ruby: `round(f, 2)` with a digit count always returns a Float, a negative base with a fractional exponent (`(-8.0) ** 0.5`) is a `Math::DomainError` instead of a Complex, `divmod` returns a Tuple, and the checker follows the nil of `<=>` and `infinite?`.

## Float[]

`Float[*Any]`

Makes an **Array of Floats** (not a Float). It is a typed Array like `Integer[]`: every element must be a Float, and every write (`Array.push`, ...) is checked. An Integer is not converted: `Float[1.5, 2]` is a `type` problem statically and a `TypeError` at run time. The empty Array of Floats is `Float[]`.

```ruby
fs = Float[1.5, 2.0]
Array.push(fs, 3.5)
p(fs)                          # => [1.5, 2.0, 3.5]
p(Array.size(Float[]))         # => 0
p(Array.sum(fs))               # => 7.0
```

```ruby error
Array.push(Float[], 1)         # !> Array.push: an element must be Float, but is Integer
```

## INFINITY, NAN, EPSILON, MAX, MIN

`Float.INFINITY()`

`Float.NAN()`

`Float.EPSILON()`

`Float.MAX()`

`Float.MIN()`

Operations without arguments that return the values of Ruby's `Float::INFINITY` and so on (Sake has no value constants, so they are written `Float.INFINITY` or `Float.INFINITY()`). `INFINITY` is positive infinity (the negative one is `-Float.INFINITY`), `NAN` is not-a-number, `EPSILON` is the difference between 1.0 and the smallest Float above it, `MAX` is the largest finite value, and `MIN` is the smallest positive normalized value. All are Floats. A NaN is `==` to nothing, not even itself.

```ruby
p(Float.INFINITY)              # => Infinity
p(-Float.INFINITY)             # => -Infinity
p(Float.NAN == Float.NAN)      # => false
p(Float.EPSILON)               # => 2.220446049250313e-16
p(1.0 + Float.EPSILON > 1.0)   # => true
p(Float.MAX)                   # => 1.7976931348623157e+308
p(Float.MAX * 2)               # => Infinity
p(Float.MIN)                   # => 2.2250738585072014e-308
```

## +, -, *, /, %, **

`Float.+(x, Any)`

`Float.-(x, Any)`

`Float.*(x, Any)`

`Float./(x, Any)`

`Float.%(x, Any)`

`Float.**(x, Any)`

The function forms of `x + y` and the others with a Float on the left. The right operand `y` is an Integer, a Float, a Rational or a Complex. With an Integer or a Rational the result is a Float, with a Complex a Complex. Any other type (a String, nil) is a `type` problem statically (`the operands are (Float, String), which the left operand's type does not support`) and a `TypeError` at run time.

Division follows IEEE: `1.0 / 0` is `Infinity` and `0.0 / 0` is `NaN`, without an exception. `%` is Ruby's `Float#%` (the result takes the sign of the right operand), but dividing by zero with it, the Integer `0` or the Float `0.0`, is a `ZeroDivisionError` (rescuable; Ruby 4 raises it too). `**` with an Integer, Float or Rational exponent is a Float; unlike Ruby, a negative base with a fractional exponent (`(-8.0) ** 0.5`) is a `Math::DomainError` rather than a Complex, so the result is always a Float.

```ruby
p(1.5 + 2)                     # => 3.5
p(Float.+(1.5, 2r))            # => 3.5
p(Float.+(1.5, Complex(1, 2))) # => (2.5+2i)
p(Float.*(1.5, 2))             # => 3.0
p(Float./(1.0, 0))             # => Infinity
p(Float./(0.0, 0))             # => NaN
p(Float.%(7.5, 2))             # => 1.5
p(-7.5 % 2)                    # => 0.5
p(Float.**(2.0, 3))            # => 8.0
p(Float.**(2.0, 0.5))          # => 1.4142135623730951
p(Float.**(2.0, 2r))           # => 4.0
p((-8.0) ** 2)                 # => 64.0
begin
  7.5 % 0.0
rescue ZeroDivisionError => e
  p(Exception.message(e))      # => "divided by 0"
end
```

```ruby error
p(Float.+(1.5, "a"))           # !> the operands are (Float, String), which the left operand's type does not support
```

```ruby error
p(Float.%(7.5, 0))             # !> ZeroDivisionError: Float.%: divided by 0
```

```ruby error
p(Float.**(-8.0, 0.5))         # !> Math::DomainError: Float.**: -8.0 ** 0.5 is not a real number (a negative base with a fractional exponent)
```

## ==, !=

`Float.==(x, Any)`

`Float.!=(x, Any)`

The function form of `x == y`. A Float compares by value with an Integer, a Rational and a Complex: `1.0 == 1` is true. A NaN equals nothing, itself included. A value of another type is never equal (`1.0 == "1"` is false), and the function form `Float.==(x, y)` gives the same false (and `!=` true) for any right operand of another type, as the operator does, without an error.

```ruby
p(Float.==(1.0, 1))            # => true
p(Float.==(1.0, 1r))           # => true
p(1.5 != 1.5)                  # => false
p(Float.!=(1.0, Float.NAN))    # => true
p(1.0 == "1")                  # => false
p(Float.==(1.0, "1"))          # => false
p(Float.!=(1.0, nil))          # => true
```

## <, <=, >, >=

`Float.<(x, Any)`

`Float.<=(x, Any)`

`Float.>(x, Any)`

`Float.>=(x, Any)`

The function forms of `x < y` and the others. The right operand is an Integer, a Float or a Rational (a Complex has no order; it is a `type` problem statically). Every comparison with a NaN is false.

```ruby
p(1.5 < 2)                     # => true
p(Float.<=(1.5, 2r))           # => true
p(Float.>(1.5, 2))             # => false
p(Float.>=(1.5, 1.5))          # => true
p(Float.NAN < 1.0)             # => false
```

```ruby error
p(Float.<(1.0, Complex(1, 0))) # !> the operands are (Float, Complex), which the left operand's type does not support
```

## <=>

`Float.<=>(x, Any)`

The function form of `x <=> y`: -1, 0 or 1. The right operand is an Integer, a Float or a Rational. When either side is a NaN the result is **nil**, so its type is `Integer | nil`, and `--strict` (the `nil` item of level 2) reports using it in arithmetic unchecked as `the operands may be nil`. Test it first with `if c`.

```ruby
p(Float.<=>(1.5, 2))           # => -1
p(1.5 <=> 1.5)                 # => 0
p(Float.<=>(Float.NAN, 1.0))   # => nil
c = 2.0 <=> 1
if c
  p(c + 1)                     # => 2
end
```

```ruby error
c = Float.<=>(1.5, 2.0)
p(c + 1)                       # !> the operands may be nil
```

## abs, magnitude

`Float.abs(x)`

`Float.magnitude(x)`

The absolute value (a Float). `-0.0` gives `0.0` and `-Infinity` gives `Infinity`.

```ruby
p(Float.abs(-1.5))             # => 1.5
p(Float.magnitude(-1.5))       # => 1.5
p(Float.abs(-0.0))             # => 0.0
p(Float.abs(-Float.INFINITY))  # => Infinity
```

## negative?, positive?, zero?

`Float.negative?(x)`

`Float.positive?(x)`

`Float.zero?(x)`

`x < 0`, `x > 0` and `x == 0` as true/false. For `-0.0`, `zero?` is true and both `negative?` and `positive?` are false. All three are false for a NaN.

```ruby
p(Float.negative?(-1.5))       # => true
p(Float.negative?(-0.0))       # => false
p(Float.positive?(0.0))        # => false
p(Float.zero?(-0.0))           # => true
p(Float.zero?(1.5))            # => false
```

## nan?, finite?, infinite?

`Float.nan?(x)`

`Float.finite?(x)`

`Float.infinite?(x)`

`nan?` is true for a NaN. `finite?` is true when x is neither a NaN nor infinite. `infinite?` returns, as Ruby's does, `1` for positive infinity, `-1` for negative infinity and **nil** otherwise (a NaN included), so its type is `Integer | nil`. It is usually used as a condition, `if Float.infinite?(x)`; to use the value in arithmetic, check it for nil first (`--strict`'s `nil` item reports the unchecked use).

```ruby
p(Float.nan?(0.0 / 0))               # => true
p(Float.nan?(1.5))                   # => false
p(Float.finite?(1.5))                # => true
p(Float.finite?(Float.INFINITY))     # => false
p(Float.infinite?(1.5))              # => nil
p(Float.infinite?(Float.INFINITY))   # => 1
p(Float.infinite?(-Float.INFINITY))  # => -1
```

## to_i, to_int, truncate

`Float.to_i(x)`

`Float.to_int(x)`

`Float.truncate(x)`

Drop the fraction, giving the Integer nearest to zero (Ruby's `Float#to_i`). The three give the same result. A NaN or an Infinity cannot become an Integer: `FloatDomainError`, whose message is the value (`NaN`, `Infinity`). Ruby's digit-count argument of `truncate(digits)` does not exist on Float; use `Arithmetic.truncate(x, n)` ([Arithmetic](Arithmetic.md)).

```ruby
p(Float.to_i(1.9))             # => 1
p(Float.to_int(-1.9))          # => -1
p(Float.truncate(-1.8))        # => -1
begin
  Float.to_i(Float.NAN)
rescue FloatDomainError => e
  p(Exception.message(e))      # => "NaN"
end
```

```ruby error
p(Float.truncate(Float.INFINITY))  # !> FloatDomainError: Float.truncate: Infinity
```

## floor, ceil

`Float.floor(x)`

`Float.ceil(x)`

`floor` is the largest Integer not above x, `ceil` the smallest Integer not below it. A NaN or an Infinity is a `FloatDomainError`. There is no digit-count argument; `Arithmetic.floor(x, n)` and `Arithmetic.ceil(x, n)` take one.

```ruby
p(Float.floor(1.8))            # => 1
p(Float.floor(-1.2))           # => -2
p(Float.ceil(1.2))             # => 2
p(Float.ceil(-1.2))            # => -1
```

```ruby error
p(Float.floor(Float.INFINITY)) # !> FloatDomainError: Float.floor: Infinity
```

## round

`Float.round(x, [Integer], [half: Symbol])`

Rounds. Without a digit count it returns the nearest Integer, rounding halves away from zero by default (`Float.round(2.5)` is 3 and `-2.5` gives -3, as Ruby's `round`). With a digit count `n` it returns a **Float** rounded to n decimal places. A negative count rounds to a power of ten, but the result is still a Float (Ruby's `1234.5.round(-2)` is the Integer 1200; Sake gives `1200.0`). The result type depends on whether a count is given because the checker looks at the number of arguments. The keyword `half:` chooses how an exact half is rounded, as Ruby's: `:up` (the default, away from zero), `:even` (to the even neighbour, banker's rounding), `:down` (toward zero); another Symbol is an `ArgumentError` (`invalid rounding mode: foo`), and a non-Symbol is a `type` problem. A NaN or an Infinity without a count is a `FloatDomainError`; with a count it is returned unchanged.

```ruby
p(Float.round(1.5))            # => 2
p(Float.round(2.5))            # => 3
p(Float.round(-2.5))           # => -3
p(Float.round(1.2345, 2))      # => 1.23
p(Float.round(3.14159, 3))     # => 3.142
p(Float.round(1234.5, -2))     # => 1200.0
p(Float.round(1.5, 0))         # => 2.0
p(Float.round(Float.NAN, 2))   # => NaN
p(Float.round(2.5, half: :even))      # => 2
p(Float.round(3.5, half: :even))      # => 4
p(Float.round(2.5, half: :down))      # => 2
p(Float.round(2.5, 0, half: :even))   # => 2.0
p(Float.round(1.25, 1, half: :even))  # => 1.2
p(Float.round(1250.0, -2, half: :even))  # => 1200.0
```

```ruby error
p(Float.round(-Float.INFINITY))  # !> FloatDomainError: Float.round: -Infinity
```

```ruby error
p(Float.round(2.5, half: :foo))  # !> ArgumentError: Float.round: invalid rounding mode: foo
```

## divmod

`Float.divmod(x, Any)`

The Tuple `[q, r]` of quotient and remainder: `q` is `x / y` floored, an **Integer**, and `r` is `x - q * y`, a Float with the sign of `y` (as Ruby's `7.5.divmod(2)` is `[3, 1.5]`). `y` is a Float or an Integer; any other type, a Rational included, is a `type` problem statically. Dividing by zero, the Integer `0` or the Float `0.0`, is a `ZeroDivisionError`; an `x` that is a NaN or an Infinity is a `FloatDomainError` (its message is the value). Both can be rescued.

```ruby
p(Float.divmod(7.5, 2))        # => [3, 1.5]
p(Float.divmod(-7.5, 2.0))     # => [-4, 0.5]
q, r = Float.divmod(7.5, 2)
p(q + 1)                       # => 4
p(r + 0.5)                     # => 2.0
begin
  Float.divmod(Float.NAN, 2)
rescue FloatDomainError => e
  p(Exception.message(e))      # => "NaN"
end
```

```ruby error
p(Float.divmod(7.5, 0))        # !> ZeroDivisionError: Float.divmod: divided by 0
```

```ruby error
p(Float.divmod(7.5, 0.0))      # !> ZeroDivisionError: Float.divmod: divided by 0
```

## fdiv, quo

`Float.fdiv(x, Integer|Float|Rational)`

`Float.quo(x, Integer|Float|Rational)`

Floating-point division `x / y` (a Float). The result is that of `/`: dividing by zero gives `Infinity` or `NaN`, without an exception. The two are the same operation.

```ruby
p(Float.fdiv(7.5, 2))          # => 3.75
p(Float.quo(1.0, 4r))          # => 0.25
p(Float.fdiv(1.0, 0))          # => Infinity
```

## modulo

`Float.modulo(x, Integer|Float|Rational)`

The remainder of `x % y` (a Float with the sign of `y`), the same operation as `%`. Dividing by zero (the Integer 0 or `0.0`) raises `ZeroDivisionError`.

```ruby
p(Float.modulo(7.5, 2))        # => 1.5
p(Float.modulo(-7.5, 2))       # => 0.5
p(Float.modulo(5.5, 2.5))      # => 0.5
begin
  Float.modulo(7.5, 0)
rescue ZeroDivisionError => e
  p(Exception.message(e))      # => "divided by 0"
end
```

## between?

`Float.between?(x, Float, Float)`

True when `lo <= x <= hi`. `lo` and `hi` must be Floats; an Integer is a `type` problem statically (`argument 2 must be Float, but is Integer`).

```ruby
p(Float.between?(1.5, 1.0, 2.0))   # => true
p(Float.between?(2.5, 1.0, 2.0))   # => false
```

```ruby error
p(Float.between?(1.5, 1, 2))       # !> Float.between?: argument 2 must be Float, but is Integer
```

## clamp

`Float.clamp(x, Float, Float)`

The Float `x` confined to `lo..hi` (`lo` when x is below it, `hi` when above). `lo` and `hi` are Floats only. `lo > hi` is an `ArgumentError` (`min argument must be less than or equal to max argument`), which can be rescued.

```ruby
p(Float.clamp(2.5, 1.0, 2.0))  # => 2.0
p(Float.clamp(0.5, 1.0, 2.0))  # => 1.0
p(Float.clamp(1.5, 1.0, 2.0))  # => 1.5
```

```ruby error
p(Float.clamp(1.5, 1, 2))      # !> Float.clamp: argument 2 must be Float, but is Integer
```

```ruby error
p(Float.clamp(1.5, 2.0, 1.0))  # !> ArgumentError: Float.clamp: min argument must be less than or equal to max argument
```

## next_float, prev_float

`Float.next_float(x)`

`Float.prev_float(x)`

The next representable Float above or below x. `next_float(1.0) - 1.0` is `Float.EPSILON`. The Float after `Float.MAX` is `Infinity`; the one after a NaN is a NaN.

```ruby
p(Float.next_float(1.0))                       # => 1.0000000000000002
p(Float.prev_float(1.0))                       # => 0.9999999999999999
p(Float.next_float(0.0))                       # => 5.0e-324
p(Float.next_float(1.0) - 1.0 == Float.EPSILON)  # => true
p(Float.next_float(Float.MAX))                 # => Infinity
```

## to_r, rationalize

`Float.to_r(x)`

`Float.rationalize(x)`

Convert to a Rational. `to_r` takes the binary value as it is, so `0.1` becomes `3602879701896397/36028797018963968`. `rationalize` picks the simplest fraction that rounds back to the same Float, so `0.1` becomes `1/10`. A NaN or an Infinity is a `FloatDomainError`. Ruby's tolerance argument `rationalize(eps)` does not exist on Float; `Rational.rationalize(r, eps)` takes one ([Rational](Rational.md)).

```ruby
p(Float.to_r(0.75))            # => (3/4)
p(Float.to_r(0.1))             # => (3602879701896397/36028797018963968)
p(Float.rationalize(0.1))      # => (1/10)
p(Float.rationalize(0.333))    # => (333/1000)
p(Float.to_r(-1.5))            # => (-3/2)
```

```ruby error
p(Float.to_r(Float.NAN))       # !> FloatDomainError: Float.to_r: NaN
```

## numerator, denominator

`Float.numerator(x)`

`Float.denominator(x)`

The numerator and denominator of `Float.to_r(x)` (Integers). The denominator of `0.1` is `36028797018963968`, the value of the binary representation. Unlike Ruby's, where `numerator` of a NaN is the NaN itself and `denominator` is 1, a NaN or an Infinity is a `FloatDomainError`, so the result is always an Integer.

```ruby
p(Float.numerator(0.75))       # => 3
p(Float.denominator(0.75))     # => 4
p(Float.numerator(1.5))        # => 3
p(Float.denominator(0.1))      # => 36028797018963968
```

```ruby error
p(Float.numerator(Float.NAN))  # !> FloatDomainError: Float.numerator: NaN
```

## angle, arg, phase

`Float.angle(x)`

`Float.arg(x)`

`Float.phase(x)`

The argument in the complex plane: the Integer `0` for a positive number (and `0.0`), the Float `π` for a negative one (and `-0.0`), so the type is `Integer | Float`. A NaN gives a NaN. The three are the same operation.

```ruby
p(Float.angle(1.5))            # => 0
p(Float.arg(-1.5))             # => 3.141592653589793
p(Float.phase(-0.0))           # => 3.141592653589793
p(Float.angle(Float.NAN))      # => NaN
```

## to_f, to_s

`Float.to_f(x)`

`Float.to_s(x)`

`to_f` returns x itself (to turn a value of type `Integer | Float` into a Float, use `Arithmetic.to_f(x)`). `to_s` is Ruby's notation: an integral value keeps its `.0`, and values from about 1e16 up or below 1e-4 use the exponent form `1.0e+20`. `Infinity`, `-Infinity` and `NaN` are spelled out. `puts(1.5)` prints the same notation.

```ruby
p(Float.to_f(1.5))             # => 1.5
p(Float.to_s(1.0))             # => "1.0"
p(Float.to_s(0.1 + 0.2))       # => "0.30000000000000004"
p(Float.to_s(1e20))            # => "1.0e+20"
p(Float.to_s(1e-5))            # => "1.0e-05"
p(Float.to_s(Float.INFINITY))  # => "Infinity"
p(Float.to_s(-0.0))            # => "-0.0"
```

# Rational

A Rational is a rational number with an Integer numerator and denominator, always kept in lowest terms. The literals are Ruby's: `2r`, `1/3r` (the division `1 / 3r`), `0.5r` (`1/2`); `Rational(1, 3)`, `Rational("1/3")` and `Rational(0.5)` (which is `(1/2)`, as in Ruby) are Kernel operations; `Float.to_r` and `Float.rationalize` also convert a Float (see [Values and types](../03-values.md)). `p` shows a Rational as `(1/3)`.

The operators on Rationals are `+`, `-`, `*`, `/`, `%`, `**` and `==`, `!=`, `<`, `<=`, `>`, `>=`, `<=>`. The right operand may be an Integer, a Float, a Rational or a Complex, and the result type follows the closed table: with an Integer the result is a Rational, with a Float a Float, with a Complex a Complex ([Operators and indexing](../05-operators.md)). `1 + 1r/2`, with the Integer on the left, is a Rational too. An Integer raised to a negative Integer, `2 ** -1`, is a Rational in Ruby but an `ArgumentError` in Sake: write `2r ** -1`. The entries `Rational.+(x, y)` and so on below are the function forms of those operators.

As in Ruby, dividing a Rational by zero is a `ZeroDivisionError` (unlike a Float, it does not become infinite). The differences from Ruby: `round`, `floor`, `ceil` and `truncate` take no digit count (use `Arithmetic.round(r, n)`, see [Arithmetic](Arithmetic.md)), and the arguments of `quo` and `rationalize` do not accept a Float.

## Rational[]

`Rational[*Any]`

Makes an **Array of Rationals** (not a Rational). Every element must be a Rational, and every write (`Array.push`, ...) is checked. An Integer is not converted: it is a `type` problem statically and a `TypeError` at run time. The empty Array of Rationals is `Rational[]`.

```ruby
rs = Rational[1r/2, 2r]
Array.push(rs, Rational(1, 3))
p(rs)                            # => [(1/2), (2/1), (1/3)]
p(Array.size(Rational[]))        # => 0
```

```ruby error
Array.push(Rational[], 1)        # !> Array.push: an element must be Rational, but is Integer
```

## +, -, *, /, %, **

`Rational.+(x, Any)`

`Rational.-(x, Any)`

`Rational.*(x, Any)`

`Rational./(x, Any)`

`Rational.%(x, Any)`

`Rational.**(x, Any)`

The function forms of `x + y` and the others with a Rational on the left. The right operand `y` is an Integer, a Float, a Rational or a Complex. With an Integer or a Rational the result is a Rational, with a Float a Float, with a Complex a Complex. Any other type is a `type` problem statically and a `TypeError` at run time. A right operand of zero (`0`, `0r`) in `/` or `%` is a `ZeroDivisionError` (`0.0` follows the Float rules and gives `Infinity`). `%` is Ruby's `Rational#%`: the result takes the sign of the right operand. `**` with an Integer exponent is a Rational (a negative one too: `2r ** -1` is `(1/2)`) and with a Float exponent a Float. With a **Rational exponent** the result is a Rational when the exponent is integral (`4r ** 2r` is `(16/1)`) and a Float otherwise (`4r ** (1r/2)` is `2.0`), so the checker types it `Rational | Float`: narrow it with `case`/`in` before using it as one of the two (`Rational.numerator(4r ** 2r)` is a `type` problem). A negative base with a fractional exponent is a `Math::DomainError` (Ruby gives a Complex).

```ruby
p(1r/3 + 1r/6)                   # => (1/2)
p(Rational.+(1r/3, 1))           # => (4/3)
p(Rational.+(1r/3, 0.5))         # => 0.8333333333333333
p(Rational.+(1r/3, Complex(1, 1)))  # => ((4/3)+1i)
p(Rational.-(1r, 1r/3))          # => (2/3)
p(Rational.*(2r/3, 3r/4))        # => (1/2)
p(Rational./(1r/3, 2))           # => (1/6)
p(Rational.%(7r/3, 1r/2))        # => (1/3)
p(Rational.**(2r, -1))           # => (1/2)
p(Rational.**(1r/2, 3))          # => (1/8)
p(Rational.**(4r, 2r))           # => (16/1)
p(Rational.**(2r, 1r/2))         # => 1.4142135623730951
x = 4r ** (1r/2)
case x
in Rational then p(Rational.numerator(x))
in Float then p(x)               # => 2.0
end
```

```ruby error
p(Rational./(1r, 0))             # !> ZeroDivisionError: Rational./: divided by 0
```

```ruby error
p(Rational.numerator(4r ** 2r))  # !> Rational.numerator: argument 1 must be Rational, but can be Float
```

```ruby error
p((-8r) ** (1r/2))               # !> Math::DomainError: Arithmetic.**: -8/1 ** 1/2 is not a real number (a negative base with a fractional exponent)
```

## ==, !=

`Rational.==(x, Any)`

`Rational.!=(x, Any)`

The function form of `x == y`. A Rational compares by value with an Integer, a Float and a Complex: `1r/2 == 0.5` and `2r == 2` are true. A value of another type is never equal, and the function form `Rational.==(x, y)` gives the same false (and `!=` true) for any right operand of another type, as the operator does, without an error.

```ruby
p(Rational.==(1r/2, 0.5))        # => true
p(Rational.==(2r, 2))            # => true
p(Rational.!=(1r/2, 1r/3))       # => true
p(1r == nil)                     # => false
p(Rational.==(1r/2, "x"))        # => false
p(Rational.!=(1r/2, "x"))        # => true
```

## <, <=, >, >=

`Rational.<(x, Any)`

`Rational.<=(x, Any)`

`Rational.>(x, Any)`

`Rational.>=(x, Any)`

The function forms of `x < y` and the others. The right operand is an Integer, a Float or a Rational (a Complex has no order; it is a `type` problem statically). Comparisons with a Rational or an Integer are exact; with a Float the Float's value is used.

```ruby
p(1r/3 < 1r/2)                   # => true
p(Rational.<(1r/3, 0.5))         # => true
p(Rational.<=(1r/2, 0.5))        # => true
p(Rational.>(1r/3, 0.3))         # => true
p(Rational.>=(1r, 1))            # => true
```

```ruby error
p(Rational.<(1r, Complex(1, 0))) # !> the operands are (Rational, Complex), which the left operand's type does not support
```

## <=>

`Rational.<=>(x, Any)`

The function form of `x <=> y`: -1, 0 or 1. With an Integer or a Rational on the right the result is always an Integer. With a Float on the right it is **nil** for a NaN, so the type is `Integer | nil`, and `--strict` (the `nil` item of level 2) reports using it in arithmetic unchecked.

```ruby
p(Rational.<=>(1r/3, 1r/2))      # => -1
p(Rational.<=>(1r/2, 1r/2))      # => 0
p(Rational.<=>(1r, 1r/2))        # => 1
p(Rational.<=>(1r/2, Float.NAN)) # => nil
c = Rational.<=>(1r/3, 1r/2)
p(c + 1)                         # => 0
```

```ruby error
c = Rational.<=>(1r/3, 0.5)
p(c + 1)                         # !> the operands may be nil
```

## abs, magnitude

`Rational.abs(x)`

`Rational.magnitude(x)`

The absolute value (a Rational). The two are the same operation.

```ruby
p(Rational.abs(-1r/3))           # => (1/3)
p(Rational.magnitude(-1r/3))     # => (1/3)
p(Rational.abs(-5r/2))           # => (5/2)
```

## negative?, positive?, zero?

`Rational.negative?(x)`

`Rational.positive?(x)`

`Rational.zero?(x)`

`x < 0`, `x > 0` and `x == 0` as true/false.

```ruby
p(Rational.positive?(1r/3))      # => true
p(Rational.positive?(0r))        # => false
p(Rational.negative?(-1r/3))     # => true
p(Rational.zero?(0r))            # => true
p(Rational.zero?(1r/3))          # => false
```

## numerator, denominator

`Rational.numerator(x)`

`Rational.denominator(x)`

The numerator and denominator in lowest terms (Integers). The numerator carries the sign; the denominator is always positive, and 1 for an integral value.

```ruby
p(Rational.numerator(-6r/4))     # => -3
p(Rational.denominator(-6r/4))   # => 2
p(Rational.numerator(1r/2 + 1r/2))    # => 1
p(Rational.denominator(1r/2 + 1r/2))  # => 1
```

## to_i, truncate, floor, ceil, round

`Rational.to_i(x)`

`Rational.truncate(x)`

`Rational.floor(x)`

`Rational.ceil(x)`

`Rational.round(x)`

Convert to an Integer: `to_i` and `truncate` toward zero, `floor` downward, `ceil` upward, `round` to the nearest Integer (an exact half goes away from zero: `Rational.round(5r/2)` is 3 and `-5r/2` gives -3). Ruby's digit count, `round(digits)` and the like, does not exist on these; to round to a number of digits use `Arithmetic.round(x, n)`, whose result is a Rational.

```ruby
p(Rational.to_i(-7r/2))          # => -3
p(Rational.truncate(7r/3))       # => 2
p(Rational.floor(-7r/2))         # => -4
p(Rational.ceil(7r/2))           # => 4
p(Rational.round(7r/2))          # => 4
p(Rational.round(5r/2))          # => 3
p(Rational.round(-1r/2))         # => -1
```

```ruby error
p(Rational.round(7r/3, 1))       # !> wrong number of arguments for Rational.round (given 2, expected 1)
```

## fdiv

`Rational.fdiv(x, Integer|Float|Rational)`

The floating-point division `x / y` as a **Float**. Dividing by zero gives `Infinity`, without an exception.

```ruby
p(Rational.fdiv(1r/3, 2))        # => 0.16666666666666666
p(Rational.fdiv(2r/3, 1r/3))     # => 2.0
p(Rational.fdiv(1r, 0))          # => Infinity
```

## quo

`Rational.quo(x, Integer|Rational)`

The exact division `x / y` (a Rational). `y` is an Integer or a Rational; a Float is a `type` problem statically (to divide by a Float use `/`). Dividing by zero is a `ZeroDivisionError`.

```ruby
p(Rational.quo(1r/3, 2))         # => (1/6)
p(Rational.quo(1r/3, 2r/3))      # => (1/2)
```

```ruby error
p(Rational.quo(1r/3, 0))         # !> ZeroDivisionError: Rational.quo: divided by 0
```

## rationalize

`Rational.rationalize(x, [Rational])`

The simplest fraction within the tolerance `eps` (a Rational), that is, in `x - eps .. x + eps` (Ruby's `Rational#rationalize`). Without `eps` it returns x itself. `eps` must be a Rational; a Float is a `type` problem statically.

```ruby
pi = 3141592653589793r/1000000000000000
p(Rational.rationalize(pi, 1r/100))   # => (22/7)
p(Rational.rationalize(pi, 1r/1000000))  # => (355/113)
p(Rational.rationalize(1r/3))         # => (1/3)
```

```ruby error
p(Rational.rationalize(1r/3, 0.1))    # !> Rational.rationalize: argument 2 must be Rational, but is Float
```

## to_f

`Rational.to_f(x)`

The nearest Float.

```ruby
p(Rational.to_f(1r/4))           # => 0.25
p(Rational.to_f(2r/3))           # => 0.6666666666666666
```

## to_r

`Rational.to_r(x)`

Returns x itself (for uniform handling of a value of type `Integer | Rational`).

```ruby
p(Rational.to_r(1r/3))           # => (1/3)
```

## to_s

`Rational.to_s(x)`

The String `"numerator/denominator"` (as Ruby's). An integral value keeps its denominator: `"2/1"`. It differs from `p`'s `(1/3)` only by the parentheses.

```ruby
p(Rational.to_s(1r/3))           # => "1/3"
p(Rational.to_s(2r))             # => "2/1"
p(Rational.to_s(-6r/4))          # => "-3/2"
```

# Complex

A Complex is a complex number with a real and an imaginary part, each an Integer, a Float or a Rational. The literals are Ruby's imaginary units `2i`, `3.5i` and expressions with them such as `1 + 2i`; `Complex(1, 2)` (and `Complex(1.5)` without an imaginary part) is a Kernel operation whose arguments are real numbers only ([Values and types](../03-values.md)). `p` shows a Complex as `(1+2i)`.

The operators on Complexes are `+`, `-`, `*`, `/`, `**` and `==`, `!=`. A Complex has no order, so there is no `<` and the like, and no `%` (both are `type` problems statically). The right operand may be an Integer, a Float, a Rational or a Complex, and the result is always a Complex; with an Integer, a Float or a Rational on the left and a Complex on the right the result is a Complex as well ([Operators and indexing](../05-operators.md)). The entries `Complex.+(x, y)` and so on below are the function forms of those operators.

The parts keep their types inside the value: `Complex(1, 2) / 2` is `((1/2)+1i)`, with a Rational real part (as in Ruby). That is why `real` and `imag` have the result type `Integer | Float | Rational`; to use a part as an Integer, narrow it with `case`/`in`. The difference from Ruby: a part cannot be a Complex.

## Complex[]

`Complex[*Any]`

Makes an **Array of Complexes** (not a Complex). Every element must be a Complex, and every write (`Array.push`, ...) is checked. A real number is not converted: it is a `type` problem statically and a `TypeError` at run time. The empty Array of Complexes is `Complex[]`.

```ruby
cs = Complex[Complex(1, 2), 3i]
Array.push(cs, Complex(0, 0))
p(cs)                              # => [(1+2i), (0+3i), (0+0i)]
p(Array.size(Complex[]))           # => 0
```

```ruby error
Array.push(Complex[], 1)           # !> Array.push: an element must be Complex, but is Integer
```

## +, -, *, /, **

`Complex.+(x, Any)`

`Complex.-(x, Any)`

`Complex.*(x, Any)`

`Complex./(x, Any)`

`Complex.**(x, Any)`

The function forms of `x + y` and the others with a Complex on the left. The right operand `y` is an Integer, a Float, a Rational or a Complex, and the result is a Complex. Any other type is a `type` problem statically and a `TypeError` at run time. `/` computes exactly according to the parts' types, so Integer parts become Rationals. Dividing a Complex whose parts are all exact (Integer, Rational) by 0 or by `Complex(0, 0)` is a `ZeroDivisionError`; so is dividing one with Float parts by the Integer 0, while dividing by `0.0` follows the Float rules and gives `Infinity` parts (as in Ruby). `**` is exact for an integral power and gives Float parts otherwise. There is no `%` on Complex.

```ruby
c = Complex(1, 2)
p(c + Complex(3, 4))               # => (4+6i)
p(Complex.+(c, 1.5))               # => (2.5+2i)
p(Complex.-(Complex(5, 3), 2))     # => (3+3i)
p(Complex.*(c, c))                 # => (-3+4i)
p(Complex.*(Complex(1, 1), Complex(1, -1)))  # => (2+0i)
p(Complex./(c, 2))                 # => ((1/2)+1i)
p(Complex./(Complex(1.0, 2), 2))   # => (0.5+1i)
p(Complex./(Complex(1, 2), 0.0))   # => (Infinity+Infinity*i)
p(Complex.**(Complex(0, 1), 2))    # => (-1+0i)
p(Complex.**(c, -1))               # => ((1/5)-(2/5)*i)
p(1.5 * c)                         # => (1.5+3.0i)
```

```ruby error
p(Complex./(Complex(1, 2), 0))     # !> ZeroDivisionError: Complex./: divided by 0
```

## ==, !=

`Complex.==(x, Any)`

`Complex.!=(x, Any)`

The function form of `x == y`. True when the real parts and the imaginary parts are each `==`; `Complex(1, 0) == 1` and `Complex(1.0, 0) == 1` are true. A value of another type is never equal (`Complex(1, 2) == "x"` is false), and the function form `Complex.==(x, y)` gives the same false (and `!=` true) for any right operand of another type, as the operator does, without an error.

```ruby
p(Complex.==(Complex(1, 2), Complex(1, 2)))    # => true
p(Complex.==(Complex(1, 0), 1))                # => true
p(Complex.==(Complex(2, 0), 2.0))              # => true
p(Complex.!=(Complex(1, 2), Complex(2, 1)))    # => true
p(Complex(1, 2) == "x")                        # => false
p(Complex.==(Complex(1, 2), "x"))              # => false
p(Complex.==(Complex(1, 2), nil))              # => false
```

## real, imag, imaginary

`Complex.real(x)`

`Complex.imag(x)`

`Complex.imaginary(x)`

The real and the imaginary part. The type is that of the part as constructed (`Integer | Float | Rational`). `imag` and `imaginary` are the same operation.

```ruby
c = Complex(1, 2.5)
p(Complex.real(c))                 # => 1
p(Complex.imag(c))                 # => 2.5
p(Complex.imaginary(Complex(3, 4))) # => 4
p(Complex.real(Complex(1r/2, 1)))  # => (1/2)
r = Complex.real(Complex(1, 2))
p(r + 1)                           # => 2
```

## rect, rectangular

`Complex.rect(x)`

`Complex.rectangular(x)`

The Tuple `[re, im]` of the two parts (each position `Integer | Float | Rational`). Multiple assignment takes both. The two are the same operation.

```ruby
p(Complex.rect(Complex(1, 2)))             # => [1, 2]
p(Complex.rectangular(Complex(1.5, 2r)))   # => [1.5, (2/1)]
re, im = Complex.rectangular(Complex(1, 2))
p(re + im)                                 # => 3
```

## polar

`Complex.polar(x)`

The polar form as the Tuple `[r, θ]`: `r` is the absolute value exactly as `abs` gives it (`Integer | Float`; a Rational magnitude becomes a Float, `Complex.polar(Complex(3r, 0))` is `[3.0, 0.0]`) and `θ` the argument (as `arg`, a Float; `0.0` or `π` on the real axis).

```ruby
p(Complex.polar(Complex(3, 4)))    # => [5.0, 0.9272952180016122]
p(Complex.polar(Complex(0, 2)))    # => [2, 1.5707963267948966]
p(Complex.polar(Complex(-2, 0)))   # => [2, 3.141592653589793]
p(Complex.polar(Complex(3r, 0)))   # => [3.0, 0.0]
r, th = Complex.polar(Complex(3, 4))
p(r)                               # => 5.0
```

## abs, magnitude

`Complex.abs(x)`

`Complex.magnitude(x)`

The absolute value `sqrt(re² + im²)`. Usually a Float, but as in Ruby, when one part is an exact 0 the absolute value of the other part is returned as it is, so `Complex(3, 0)` gives the Integer `3`. Unlike Ruby's, a Rational magnitude becomes a Float (`Complex.abs(Complex(3r, 0))` is `3.0`), so the type is exactly `Integer | Float`. The two are the same operation.

```ruby
p(Complex.abs(Complex(3, 4)))      # => 5.0
p(Complex.abs(Complex(3, 0)))      # => 3
p(Complex.abs(Complex(0, -2)))     # => 2
p(Complex.abs(Complex(3r, 0)))     # => 3.0
p(Complex.magnitude(Complex(1, 1))) # => 1.4142135623730951
a = Complex.abs(Complex(3, 4))
p(a + 1)                           # => 6.0
```

## abs2

`Complex.abs2(x)`

The square of the absolute value, `re² + im²`. No square root is taken, so it is exact, and the type follows the parts: `Integer | Float | Rational`.

```ruby
p(Complex.abs2(Complex(3, 4)))         # => 25
p(Complex.abs2(Complex(1.5, 0)))       # => 2.25
p(Complex.abs2(Complex(1r/2, 1r/2)))   # => (1/2)
```

## arg, angle, phase

`Complex.arg(x)`

`Complex.angle(x)`

`Complex.phase(x)`

The argument (a Float from `-π` to `π`), that is, `Math.atan2(im, re)`: `0.0` for `Complex(0, 0)`, `π` on the negative real axis. The three are the same operation.

```ruby
p(Complex.arg(Complex(1, 1)))      # => 0.7853981633974483
p(Complex.angle(Complex(0, 1)))    # => 1.5707963267948966
p(Complex.phase(Complex(-1, 0)))   # => 3.141592653589793
p(Complex.arg(Complex(0, -1)))     # => -1.5707963267948966
p(Complex.arg(Complex(0, 0)))      # => 0.0
```

## conj, conjugate

`Complex.conj(x)`

`Complex.conjugate(x)`

The complex conjugate (the Complex with the sign of the imaginary part flipped). The two are the same operation.

```ruby
p(Complex.conj(Complex(1, 2)))         # => (1-2i)
p(Complex.conjugate(Complex(1.5, -2))) # => (1.5+2i)
p(Complex.conjugate(Complex(3, 0)))    # => (3+0i)
```

## real?

`Complex.real?(x)`

Always **false** (as Ruby's `Complex#real?`: a value of type Complex is not a real number even when its imaginary part is 0). To ask whether the value is real, use `Complex.imag(x) == 0`.

```ruby
p(Complex.real?(Complex(1, 2)))    # => false
p(Complex.real?(Complex(1, 0)))    # => false
p(Complex.imag(Complex(1, 0)) == 0)  # => true
```

## finite?, infinite?

`Complex.finite?(x)`

`Complex.infinite?(x)`

`finite?` is true when both parts are finite (neither a NaN nor infinite). `infinite?` returns, as Ruby's does, `1` when either part is infinite and **nil** otherwise (a NaN included), so its type is `Integer | nil`. It is usually used as a condition; to use the value in arithmetic, check it for nil first (`--strict`'s `nil` item reports the unchecked use).

```ruby
p(Complex.finite?(Complex(1, 2)))                 # => true
p(Complex.finite?(Complex(Float.INFINITY, 2)))    # => false
p(Complex.finite?(Complex(Float.NAN, 2)))         # => false
p(Complex.infinite?(Complex(1, 2)))               # => nil
p(Complex.infinite?(Complex(1, -Float.INFINITY))) # => 1
p(Complex.infinite?(Complex(Float.NAN, 2)))       # => nil
```

```ruby error
x = Complex.infinite?(Complex(1, 2))
p(x + 1)                           # !> the operands may be nil
```

## fdiv

`Complex.fdiv(x, Integer|Float|Rational)`

Divides by the real number `y` after turning the parts into Floats: a Complex with Float parts. `y` cannot be a Complex (a `type` problem statically; to divide by a Complex use `/` or `quo`). Dividing by zero gives `Infinity` parts, without an exception.

```ruby
p(Complex.fdiv(Complex(3, 4), 2))    # => (1.5+2.0i)
p(Complex.fdiv(Complex(1, 2), 2r))   # => (0.5+1.0i)
p(Complex.fdiv(Complex(1, 2), 0))    # => (Infinity+Infinity*i)
```

```ruby error
p(Complex.fdiv(Complex(1, 2), Complex(1, 1)))  # !> Complex.fdiv: argument 2 must be Integer|Float|Rational, but is Complex
```

## quo

`Complex.quo(x, Integer|Float|Rational|Complex)`

The division `x / y` (a Complex), the same as `/`: Integer parts become Rationals. Dividing by an exact zero is a `ZeroDivisionError`.

```ruby
p(Complex.quo(Complex(3, 4), 2))              # => ((3/2)+2i)
p(Complex.quo(Complex(3, 4), 2.0))            # => (1.5+2.0i)
p(Complex.quo(Complex(3, 4), Complex(1, 1)))  # => ((7/2)+(1/2)*i)
```

```ruby error
p(Complex.quo(Complex(1, 2), 0))   # !> ZeroDivisionError: Complex.quo: divided by 0
```

## numerator, denominator

`Complex.numerator(x)`

`Complex.denominator(x)`

`denominator` is the least common multiple of the parts' denominators (an Integer); `numerator` is `x * denominator`, a Complex with integral parts. An Integer part has denominator 1.

```ruby
c = Complex(1r/2, 1r/4)
p(Complex.denominator(c))          # => 4
p(Complex.numerator(c))            # => (2+1i)
p(Complex.denominator(Complex(3, 4)))  # => 1
p(Complex.numerator(Complex(3, 4)))    # => (3+4i)
```

## to_i, to_f, to_r, rationalize

`Complex.to_i(x)`

`Complex.to_f(x)`

`Complex.to_r(x)`

`Complex.rationalize(x, [Rational])`

Convert the real part to an Integer, a Float or a Rational (`rationalize` is the real part's `rationalize`, with a Rational tolerance `eps`), but only when the imaginary part is an exact 0 (the Integer `0` or `0r`). When it is not 0, or is `0.0` (the Float zero), a `RangeError` is raised (Ruby's message: `can't convert 1+2i into Float`), which can be rescued.

```ruby
p(Complex.to_i(Complex(1.9, 0)))           # => 1
p(Complex.to_f(Complex(3, 0)))             # => 3.0
p(Complex.to_r(Complex(1.5, 0)))           # => (3/2)
p(Complex.rationalize(Complex(0.333, 0)))  # => (333/1000)
p(Complex.rationalize(Complex(0.333, 0), 1r/100))  # => (1/3)
begin
  Complex.to_f(Complex(1, 2))
rescue RangeError => e
  p(Exception.message(e))                  # => "can't convert 1+2i into Float"
end
```

```ruby error
p(Complex.to_f(Complex(1, 0.0)))   # !> RangeError: Complex.to_f: can't convert 1+0.0i into Float
```

## to_c

`Complex.to_c(x)`

Returns x itself (a real number becomes a Complex through Kernel's `Complex(x)`).

```ruby
p(Complex.to_c(Complex(3, 4)))     # => (3+4i)
```

## to_s

`Complex.to_s(x)`

The String `"re+imi"` (as Ruby's): `p`'s `(3+4i)` without the parentheses, and a Rational part is written without its parentheses too, `1/2+3i`.

```ruby
p(Complex.to_s(Complex(3, 4)))         # => "3+4i"
p(Complex.to_s(Complex(-1, -1)))       # => "-1-1i"
p(Complex.to_s(Complex(0.5, 2)))       # => "0.5+2i"
p(Complex.to_s(Complex(1r/2, 3)))      # => "1/2+3i"
```

# Arithmetic

Arithmetic is the module of the arithmetic operators `+`, `-`, `*`, `/`, `%`, `**` and the unary `-x`, `+x`. `a + b` is shorthand for `Arithmetic.+(a, b)`, which dispatches to the `+` of the left operand's type (Integer, Float, Rational, Complex, String, Time, Set, or a class that does `include Arithmetic`), see [Operators and indexing](../05-operators.md) and [Program structure](../02-program.md).

The eight operations in this chapter are not operators: they are **rounding and conversion for any real number** (Integer, Float, Rational). A Sake operation normally names its type, `Float.round(f)`; these are for values whose type is not settled, such as `Integer | Float` (the result of `Array.sum`, a number read from JSON), and they dispatch on the value's type as Ruby's `x.round` does. The result type follows the argument's: `round`, `floor`, `ceil`, `truncate` without a digit count and `to_i` give an Integer; a rounding with a digit count and `abs` give the argument's type (a union when the argument's type is one); `to_f` gives a Float; `zero?` gives true/false. The checker applies this rule to each type of the argument.

A Complex or a String argument is a `type` problem statically (`argument 1 must be Integer|Float|Rational, but is Complex`). Turning a NaN or an Infinity into an Integer raises `FloatDomainError`, which `rescue FloatDomainError => e` catches.

## round, floor, ceil, truncate

`Arithmetic.round(Integer|Float|Rational, [Integer], [half: Symbol])`

`Arithmetic.floor(Integer|Float|Rational, [Integer])`

`Arithmetic.ceil(Integer|Float|Rational, [Integer])`

`Arithmetic.truncate(Integer|Float|Rational, [Integer])`

Ruby's `x.round(n)`, `x.floor(n)`, `x.ceil(n)` and `x.truncate(n)`, with one difference in the result type. `round` goes to the nearest value (a half away from zero by default), `floor` downward, `ceil` upward, `truncate` toward zero.

- Without the digit count `n` the result is an **Integer** (an Integer argument stays one).
- With a count the result has **the argument's type**: a Float for a Float, a Rational for a Rational, an Integer for an Integer (a positive count leaves the value as it is). A negative count rounds to a power of ten. Unlike Ruby, this holds for a count of 0 or less too: `Arithmetic.round(1234.5, -2)` is `1200.0` where Ruby's `1234.5.round(-2)` is the Integer 1200, and `Arithmetic.round(5r/2, -1)` is `(0/1)`. So the result's type depends only on the argument's type and on whether a count is given.
- `round` takes the keyword `half:`, as `Float.round` does, for any argument type: `:up` (the default, away from zero), `:even` (to the even neighbour), `:down` (toward zero); another Symbol is an `ArgumentError` (`invalid rounding mode: foo`). `floor`, `ceil` and `truncate` do not take it (a static error).
- A NaN or an Infinity without a count is a `FloatDomainError` (rescuable); with a count it is returned unchanged.

```ruby
p(Arithmetic.round(1.5))           # => 2
p(Arithmetic.round(7r/2))          # => 4
p(Arithmetic.round(3))             # => 3
p(Arithmetic.round(1.2345, 2))     # => 1.23
p(Arithmetic.round(7r/3, 2))       # => (233/100)
p(Arithmetic.round(1234, -2))      # => 1200
p(Arithmetic.round(1234.5, -2))    # => 1200.0
p(Arithmetic.round(5r/2, -1))      # => (0/1)
p(Arithmetic.round(2.5, half: :even))      # => 2
p(Arithmetic.round(5r/2, half: :even))     # => 2
p(Arithmetic.round(25, -1, half: :even))   # => 20
p(Arithmetic.round(1.25, 1, half: :even))  # => 1.2
p(Arithmetic.floor(-1.5))          # => -2
p(Arithmetic.floor(1.567, 1))      # => 1.5
p(Arithmetic.floor(1234.5, -2))    # => 1200.0
p(Arithmetic.ceil(7r/2))           # => 4
p(Arithmetic.ceil(1234, -2))       # => 1300
p(Arithmetic.truncate(-7r/2))      # => -3
p(Arithmetic.truncate(1.999, 2))   # => 1.99
p(Arithmetic.round(Float.NAN, 2))  # => NaN
begin
  Arithmetic.round(Float.NAN)
rescue FloatDomainError => e
  p(Exception.message(e))          # => "NaN"
end
```

One operation serves a value whose type is not settled:

```ruby
def tenth(x)
  Arithmetic.round(x, 1)
end
p(tenth(1.25))                     # => 1.3
p(tenth(3))                        # => 3
p(tenth(1r/3))                     # => (3/10)
```

```ruby error
p(Arithmetic.round("1.5"))         # !> Arithmetic.round: argument 1 must be Integer|Float|Rational, but is String
```

```ruby error
p(Arithmetic.round(2.5, half: :foo))   # !> ArgumentError: Arithmetic.round: invalid rounding mode: foo
```

```ruby error
p(Arithmetic.round(Float.INFINITY))    # !> FloatDomainError: Arithmetic.round: Infinity
```

## abs

`Arithmetic.abs(Integer|Float|Rational)`

The absolute value, of the argument's type (Ruby's `x.abs`).

```ruby
p(Arithmetic.abs(-3))              # => 3
p(Arithmetic.abs(-1.5))            # => 1.5
p(Arithmetic.abs(-1r/3))           # => (1/3)
```

```ruby error
p(Arithmetic.abs(Complex(3, 4)))   # !> Arithmetic.abs: argument 1 must be Integer|Float|Rational, but is Complex
```

## to_i, to_f

`Arithmetic.to_i(Integer|Float|Rational)`

`Arithmetic.to_f(Integer|Float|Rational)`

`to_i` is the Integer truncated toward zero; `to_f` the nearest Float. The typical use is `to_f` to settle an `Integer | Float` value on Float (when `Float.floor(x)` is given an `Integer | Float`, the checker suggests `Arithmetic.floor(x)`). `to_i` of a NaN or an Infinity is a `FloatDomainError`, which can be rescued.

```ruby
p(Arithmetic.to_i(1.9))            # => 1
p(Arithmetic.to_i(7r/2))           # => 3
p(Arithmetic.to_i(7))              # => 7
p(Arithmetic.to_f(3))              # => 3.0
p(Arithmetic.to_f(1r/4))           # => 0.25
p(Arithmetic.to_f(1.5))            # => 1.5
```

```ruby error
p(Arithmetic.to_i(Float.NAN))      # !> FloatDomainError: Arithmetic.to_i: NaN
```

## zero?

`Arithmetic.zero?(Integer|Float|Rational)`

True when `x == 0` (`0.0` and `-0.0` included).

```ruby
p(Arithmetic.zero?(0))             # => true
p(Arithmetic.zero?(0.0))           # => true
p(Arithmetic.zero?(0r))            # => true
p(Arithmetic.zero?(1))             # => false
```

# Math

Math is the collection of elementary functions of Ruby's `Math` module. It is a namespace, not a type: there are no Math values. Every function takes a real number (Integer, Float, Rational) and the result is always a **Float** (`Math.sqrt(4)` is `2.0`; a Complex is not accepted). Only the two-argument functions `atan2` and `hypot`, and the exponent of `ldexp`, do not take a Rational: they take an Integer or a Float.

Ruby's `Math::PI` and `Math::E` are the operations without arguments `Math.PI` and `Math.E` in Sake (Sake has no value constants); `Math::PI` is also read in Ruby's form, as the same operation. `Math.log(x, base)` takes a base as Ruby's does (an Integer or a Float).

An argument outside the domain (`sqrt(-1)`, `log(-1)`, `acos(2)`, ...) raises `Math::DomainError`, as in Ruby, which `rescue Math::DomainError => e` catches ([Exceptions and errors](../08-exceptions.md)). A NaN argument gives a NaN, and infinities follow IEEE (`log(0)` is `-Infinity`, `exp(1000)` is `Infinity`; no exception).

## PI, E

`Math.PI()`

`Math.E()`

The circle constant π and the base of natural logarithms e (Floats). Written `Math.PI` or `Math.PI()`.

```ruby
p(Math.PI)                         # => 3.141592653589793
p(Math.E)                          # => 2.718281828459045
p(Math.cos(Math.PI))               # => -1.0
p(Math.exp(1) == Math.E)           # => true
```

## sqrt, cbrt

`Math.sqrt(Integer|Float|Rational)`

`Math.cbrt(Integer|Float|Rational)`

The square root and the cube root (Floats). A negative argument to `sqrt` is a `Math::DomainError`. `cbrt` accepts negative numbers (`cbrt(-8)` is `-2.0`). For the exact integer square root use `Integer.sqrt(n)`.

```ruby
p(Math.sqrt(4))                    # => 2.0
p(Math.sqrt(2.0))                  # => 1.4142135623730951
p(Math.sqrt(9r/4))                 # => 1.5
p(Math.cbrt(-8))                   # => -2.0
p(Math.sqrt(Float.INFINITY))       # => Infinity
begin
  Math.sqrt(-1)
rescue Math::DomainError => e
  p(Exception.message(e))          # => "Numerical argument is out of domain - sqrt"
end
```

```ruby error
p(Math.sqrt(-1))                   # !> Math::DomainError: Math.sqrt: Numerical argument is out of domain - sqrt
```

## sin, cos, tan

`Math.sin(Integer|Float|Rational)`

`Math.cos(Integer|Float|Rational)`

`Math.tan(Integer|Float|Rational)`

The trigonometric functions; the argument is in radians. The result is a Float, so a value such as `sin(π)` is not an exact 0 but carries rounding error.

```ruby
p(Math.sin(0))                     # => 0.0
p(Math.cos(0))                     # => 1.0
p(Math.sin(Math.PI / 2))           # => 1.0
p(Math.cos(Math.PI / 3))           # => 0.5000000000000001
p(Math.tan(Math.PI / 4))           # => 0.9999999999999999
```

## asin, acos, atan

`Math.asin(Integer|Float|Rational)`

`Math.acos(Integer|Float|Rational)`

`Math.atan(Integer|Float|Rational)`

The inverse trigonometric functions (Floats in radians). The argument of `asin` and `acos` must lie between -1 and 1; outside it is a `Math::DomainError`. `atan` takes any real number and returns a value from `-π/2` to `π/2`.

```ruby
p(Math.asin(1))                    # => 1.5707963267948966
p(Math.acos(1))                    # => 0.0
p(Math.asin(0.5))                  # => 0.5235987755982989
p(Math.atan(1) * 4 == Math.PI)     # => true
p(Math.atan(Float.INFINITY))       # => 1.5707963267948966
```

```ruby error
p(Math.acos(2))                    # !> Math::DomainError: Math.acos: Numerical argument is out of domain - acos
```

## atan2

`Math.atan2(Integer|Float, Integer|Float)`

`atan2(y, x)`: the angle of the point `(x, y)` in radians from `-π` to `π` (a Float). Unlike `atan(y / x)` it tells the quadrants apart and works when `x` is 0. The arguments are Integers or Floats; a Rational is a `type` problem statically.

```ruby
p(Math.atan2(1, 1))                # => 0.7853981633974483
p(Math.atan2(1.0, -1.0))           # => 2.356194490192345
p(Math.atan2(1, 0))                # => 1.5707963267948966
p(Math.atan2(0, -1))               # => 3.141592653589793
p(Math.atan2(-1, -1))              # => -2.356194490192345
```

```ruby error
p(Math.atan2(1r, 1))               # !> Math.atan2: argument 1 must be Integer|Float, but is Rational
```

## hypot

`Math.hypot(Integer|Float, Integer|Float)`

`sqrt(x² + y²)` (a Float), computed without overflowing in the intermediate squares. The arguments are Integers or Floats; a Rational is a `type` problem statically.

```ruby
p(Math.hypot(3, 4))                # => 5.0
p(Math.hypot(5, 12))               # => 13.0
p(Math.hypot(1.0, 1.0))            # => 1.4142135623730951
```

```ruby error
p(Math.hypot(1r, 1))               # !> Math.hypot: argument 1 must be Integer|Float, but is Rational
```

## sinh, cosh, tanh

`Math.sinh(Integer|Float|Rational)`

`Math.cosh(Integer|Float|Rational)`

`Math.tanh(Integer|Float|Rational)`

The hyperbolic functions (Floats).

```ruby
p(Math.sinh(0))                    # => 0.0
p(Math.cosh(0))                    # => 1.0
p(Math.sinh(1))                    # => 1.1752011936438014
p(Math.cosh(1))                    # => 1.5430806348152437
p(Math.tanh(Float.INFINITY))       # => 1.0
```

## asinh, acosh, atanh

`Math.asinh(Integer|Float|Rational)`

`Math.acosh(Integer|Float|Rational)`

`Math.atanh(Integer|Float|Rational)`

The inverse hyperbolic functions (Floats). The argument of `acosh` must be at least 1 and that of `atanh` between -1 and 1; outside it is a `Math::DomainError`. `atanh(1)` is `Infinity` and `atanh(-1)` is `-Infinity`.

```ruby
p(Math.asinh(1))                   # => 0.881373587019543
p(Math.acosh(2))                   # => 1.3169578969248168
p(Math.atanh(0.5))                 # => 0.5493061443340549
p(Math.atanh(1))                   # => Infinity
```

```ruby error
p(Math.acosh(0))                   # !> Math::DomainError: Math.acosh: Numerical argument is out of domain - acosh
```

## exp

`Math.exp(Integer|Float|Rational)`

`e` to the power x (a Float). A large argument gives `Infinity`, without an exception.

```ruby
p(Math.exp(0))                     # => 1.0
p(Math.exp(2))                     # => 7.38905609893065
p(Math.exp(0.5r))                  # => 1.6487212707001282
p(Math.exp(1000))                  # => Infinity
p(Math.exp(-Float.INFINITY))       # => 0.0
```

## log, log2, log10

`Math.log(Integer|Float|Rational, [Integer|Float])`

`Math.log2(Integer|Float|Rational)`

`Math.log10(Integer|Float|Rational)`

The natural logarithm and the logarithms to base 2 and base 10 (Floats). `log(0)` is `-Infinity`; a negative argument is a `Math::DomainError`. `Math.log(x, base)` is the logarithm to the given base, as Ruby's: `Math.log(8, 2)` is `3.0`. The base is an Integer or a Float (a Rational is a `type` problem statically); a negative base is a `Math::DomainError`, and as in Ruby a base of 0 gives `-0.0` and a base of 1 `NaN`. A large Integer, even one too large for a Float, has a logarithm (as in Ruby).

```ruby
p(Math.log(Math.E))                # => 1.0
p(Math.log(1))                     # => 0.0
p(Math.log(0))                     # => -Infinity
p(Math.log2(8))                    # => 3.0
p(Math.log10(0.001))               # => -3.0
p(Math.log(8r))                    # => 2.0794415416798357
p(Math.log(8, 2))                  # => 3.0
p(Math.log(100, 10))               # => 2.0
p(Math.log(8r, 2.0))               # => 3.0
p(Math.log(10 ** 400))             # => 921.0340371976183
p(Math.log(10 ** 400, 10))         # => 400.0
```

```ruby error
p(Math.log(8, 2r))                 # !> Math.log: argument 2 must be Integer|Float, but is Rational
```

```ruby error
p(Math.log(8, -2))                 # !> Math::DomainError: Math.log: Numerical argument is out of domain - log
```

## erf, erfc

`Math.erf(Integer|Float|Rational)`

`Math.erfc(Integer|Float|Rational)`

The error function and the complementary error function `1 - erf(x)` (Floats).

```ruby
p(Math.erf(0))                     # => 0.0
p(Math.erf(1))                     # => 0.8427007929497149
p(Math.erfc(1))                    # => 0.15729920705028513
p(Math.erf(Float.INFINITY))        # => 1.0
```

## gamma, lgamma

`Math.gamma(Integer|Float|Rational)`

`Math.lgamma(Integer|Float|Rational)`

`gamma` is the gamma function Γ(x) (a Float; `(n-1)!` for a positive integer n). `gamma(0)` is `Infinity`, a negative integer and `-Infinity` are a `Math::DomainError`, and 172 and above give `Infinity`. `lgamma` returns the Tuple `[log|Γ(x)|, sign]`: the natural logarithm of the absolute value (a Float) and the sign of Γ(x) (the Integer `1` or `-1`). It works for large x where `gamma` overflows.

```ruby
p(Math.gamma(5))                   # => 24.0
p(Math.gamma(0.5))                 # => 1.772453850905516
p(Math.gamma(0))                   # => Infinity
p(Math.gamma(172))                 # => Infinity
p(Math.lgamma(6))                  # => [4.787491742782046, 1]
p(Math.lgamma(-0.5))               # => [1.2655121234846454, -1]
v, sign = Math.lgamma(-0.5)
p(sign)                            # => -1
```

```ruby error
p(Math.gamma(-1))                  # !> Math::DomainError: Math.gamma: Numerical argument is out of domain - gamma
```

## frexp, ldexp

`Math.frexp(Integer|Float|Rational)`

`Math.ldexp(Integer|Float|Rational, Integer)`

`frexp(x)` splits x into the Tuple `[fraction, exponent]` (`[Float, Integer]`), where the absolute value of `fraction` is at least 0.5 and below 1 and `x == fraction * 2 ** exponent`; 0 gives `[0.0, 0]`. `ldexp(fraction, exponent)` is the inverse: `fraction * 2 ** exponent` as a Float. The exponent must be an Integer; a Float is a `type` problem statically.

```ruby
p(Math.frexp(8.0))                 # => [0.5, 4]
p(Math.frexp(0.75))                # => [0.75, 0]
p(Math.frexp(10))                  # => [0.625, 4]
p(Math.frexp(0))                   # => [0.0, 0]
f, e = Math.frexp(8.0)
p(Math.ldexp(f, e))                # => 8.0
p(Math.ldexp(1.0, -2))             # => 0.25
p(Math.ldexp(3, 2))                # => 12.0
```

```ruby error
p(Math.ldexp(0.5, 4.0))            # !> Math.ldexp: argument 2 must be Integer, but is Float
```

# String

A String is a sequence of characters with an encoding, as Ruby's String. The literals are `"abc"`, `'abc'` and `"a#{x}"` (interpolation shows the value with `to_s`, see [Values and types](../03-values.md)); a literal is UTF-8, and every evaluation of a literal makes a new String. Strings are **mutable**: the operations whose names end in `!`, and `concat`, `append_as_bytes`, `prepend`, `insert`, `replace`, `clear`, `setbyte`, `bytesplice`, `force_encoding`, change their subject in place; every other operation leaves the subject alone and returns a new String. The Strings a program cannot change are Hash keys, Set elements, the names of Symbols (`Symbol.name`), the String a MatchData holds (`MatchData.string`) and the program's arguments (`ARGV`): changing one of them in place is a `TypeError` at run time (`cannot change this String in place: it is a Hash key, a Set element, a Symbol's name, or a program argument`).

Every operation is written with its type: `String.upcase(s)`, never `s.upcase`. The operators on Strings are `+` (String, String), `*` (String, Integer), `%` (the format operator), `==`, `!=`, `<`, `<=`, `>`, `>=`, `<=>` (String, String), `=~`, `!~` (String, Regexp), and the index `s[i]`, `s[i, n]`, `s[range]` ([Operators and indexing](../05-operators.md)). The entries `String.+(x, y)` and so on below are the function forms of those operators. There is no `s[i] = v`: a String cannot be indexed for writing (a `type` problem statically); change it with `String.sub!`, `String.insert`, `String.bytesplice` or `String.replace`.

Many operations give **nil** for a miss: `index`, `rindex`, `byteindex`, `byterindex`, `match`, `getbyte`, `casecmp`, `unpack1`, `slice!`, `sub!`, `gsub!`, and the in-place forms that give nil when nothing changed (`upcase!`, `strip!`, `chomp!`, `tr!`, ...). Using such a result unchecked is a `nil` problem at `--strict` level 2. Only the index forms `s[i]`, `s[i, n]`, `s[range]`, `String.slice` and `String.byteslice` give the nil of a miss (`index-nil`), which is reported only at level 3 ([Overview](../01-overview.md)).

Where Sake differs from Ruby: `sub` and `gsub` need a replacement or a block; a `MatchData` is read with `m[i]` (there are no `$~`, `$1`); `scan`, `partition` and `rpartition` give Tuples where Ruby gives Arrays; `count`, `start_with?`, `end_with?`, `include?` take one argument each (`chomp`, `delete`, `squeeze` take what their `!` forms take: a separator, several sets); iteration (`each_char`, ...) needs a block. The table in [Built-in operations](../09-builtins.md) lists the operations in short.

## String[]

`String[*Any]`

Makes an **Array of Strings** (not a String). It is a typed Array like `Integer[]` and `Tuple[]`: every element must be a String, and every write (`Array.push`, ...) is checked. An element that is not a String is a `type` problem statically and a `TypeError` at run time. The empty Array of Strings is `String[]`.

```ruby
names = String["a", "b"]
Array.push(names, "c")
p(names)                     # => ["a", "b", "c"]
p(Array.size(String[]))      # => 0
```

```ruby error
Array.push(String[], 1)      # !> Array.push: an element must be String, but is Integer
```

## length, size

`String.length(x)`

`String.size(x)`

The number of characters (an Integer), not of bytes: `"héllo"` has 5 characters and 6 bytes (`bytesize`).

```ruby
p(String.length("hello"))    # => 5
p(String.size("héllo"))      # => 5
p(String.size(""))           # => 0
```

## bytesize

`String.bytesize(x)`

The number of bytes (an Integer) in the String's encoding.

```ruby
p(String.bytesize("héllo"))  # => 6
p(String.bytesize(""))       # => 0
```

## empty?

`String.empty?(x)`

True when the String has no characters.

```ruby
p(String.empty?(""))         # => true
p(String.empty?(" "))        # => false
```

## +

`String.+(x, Any)`

The function form of `a + b`: a new String with the characters of `a` followed by those of `b`. Both operands must be Strings; `"a" + 1` is a `type` problem statically (Ruby raises `TypeError`). Neither operand changes.

```ruby
s = "abc"
p(s + "d")                   # => "abcd"
p(String.+("a", "b"))        # => "ab"
p(s)                         # => "abc"
```

```ruby error
p("a" + 1)                   # !> the operands are (String, Integer)
```

## *

`String.*(x, Integer)`

The function form of `s * n`: a new String with `s` repeated `n` times (`""` for 0). A negative `n` is an `ArgumentError` (`negative argument`), from the operator as from the function form; it can be rescued.

```ruby
p("ab" * 3)                  # => "ababab"
p(String.*("-", 0))          # => ""
begin
  p("ab" * -1)
rescue ArgumentError => e
  puts(Exception.message(e))     # => negative argument
end
```

```ruby error
p(String.*("ab", -1))        # !> ArgumentError: String.*: negative argument -1
```

## %

`String.%(x, Any)`

The function form of `fmt % value`, Ruby's `format` with `fmt` as the format: `"%d items" % 3`. The right operand is one value (Integer, Float, String, Symbol, nil, true or false), a Tuple of values for several directives (`"%s-%s" % [a, b]`), or a Record or a Hash with Symbol keys for the named directives `%<name>d` and `%{name}` (`"%<a>05d" % {a: 42}`, `"%<a>05d" % Hash.of(a: 42)`); an Array is a `type` problem statically. The directives are Ruby's (`%d`, `%s`, `%f`, `%x`, `%05d`, `%-4s`, `%.2f`, `%p`, ...). A value that does not fit its directive (`"%d" % "x"`) or too few values is an `ArgumentError`; a name the Record or Hash does not have is a `KeyError` (`key<b> not found`); extra values are ignored.

```ruby
p("%05d|%-4s|%.2f|%x" % [42, "ab", 3.14159, 255])   # => "00042|ab  |3.14|ff"
p("%d items" % 3)            # => "3 items"
p("%s" % :sym)               # => "sym"
p("%p" % nil)                # => "nil"
p(String.%("%03d", 7))       # => "007"
p("%<a>05d" % {a: 42})       # => "00042"
p("%{a}-%<b>s" % {a: 1, b: "x"})     # => "1-x"
p("%<a>05d" % Hash.of(a: 42))   # => "00042"
```

```ruby error
p("%<b>d" % {a: 42})         # !> KeyError: Arithmetic.%: key<b> not found
```

```ruby error
p("%d %d" % 1)               # !> ArgumentError: Arithmetic.%: too few arguments
```

## ==, !=

`String.==(x, Any)`

`String.!=(x, Any)`

True when both are Strings with the same characters (`!=` is the negation); comparison is by content, not identity. A right operand of another type (an Integer, a Symbol, nil) is simply not equal, with the function forms as with the operators: `String.==("a", 1)` is false and `String.!=("a", 1)` is true (no error).

```ruby
p("a" == "a")                # => true
p("a" != "b")                # => true
p("1" == 1)                  # => false
p("a" == nil)                # => false
p(String.==("a", nil))       # => false
p(String.==("a", 1))         # => false
p(String.!=("a", 1))         # => true
```

## <, <=, >, >=

`String.<(x, Any)`

`String.<=(x, Any)`

`String.>(x, Any)`

`String.>=(x, Any)`

Compare two Strings byte by byte, as Ruby's: `"B" < "a"` because uppercase letters come first, `"10" < "9"`, and a prefix is smaller than the longer String. Both operands must be Strings; a String compared with another type is a `type` problem statically.

```ruby
p("a" < "b")                 # => true
p("B" >= "a")                # => false
p("10" < "9")                # => true
p("ab" < "abc")              # => true
p(String.<=("a", "a"))       # => true
```

```ruby error
p("a" < 1)                   # !> the operands are (String, Integer)
```

## <=>

`String.<=>(x, Any)`

The result of that comparison as -1, 0, or 1. Both operands must be Strings, so the result is never nil (Ruby gives nil for a non-String).

```ruby
p("a" <=> "b")               # => -1
p("a" <=> "a")               # => 0
p(String.<=>("b", "a"))      # => 1
```

## between?

`String.between?(x, String, String)`

True when `lo <= x <= hi` in the order of `<`.

```ruby
p(String.between?("b", "a", "c"))   # => true
p(String.between?("z", "a", "c"))   # => false
```

## clamp

`String.clamp(x, String, String)`

`x` when it lies between `lo` and `hi`, otherwise the nearer bound. `lo > hi` is an `ArgumentError`.

```ruby
p(String.clamp("b", "a", "c"))      # => "b"
p(String.clamp("z", "a", "c"))      # => "c"
```

```ruby error
p(String.clamp("b", "c", "a"))      # !> ArgumentError: String.clamp: min argument must be less than or equal to max argument
```

## casecmp

`String.casecmp(x, String)`

Compares the two Strings ignoring the case of ASCII letters: -1, 0, or 1 (an Integer). It is nil when the encodings of the two Strings are incompatible; using the result unchecked is a `nil` problem at `--strict` level 2.

```ruby
p(String.casecmp("a", "A"))         # => 0
p(String.casecmp("a", "B"))         # => -1
p(String.casecmp("a", String.encode("a", "UTF-16LE")))   # => nil
```

```ruby error
p(String.casecmp("a", "b") + 1)     # !> the operands may be nil
```

## casecmp?

`String.casecmp?(x, String)`

True when the two Strings are equal after Unicode case folding. The result is a Boolean, never nil: with incompatible encodings (where Ruby's method returns nil and `casecmp` gives nil) it is **false**.

```ruby
p(String.casecmp?("a", "A"))        # => true
p(String.casecmp?("a", "b"))        # => false
p(String.casecmp?("a", String.encode("a", "UTF-16LE")))   # => false
```

## =~

`String.=~(x, Any)`

The function form of `s =~ re`: the position (an Integer) of the first match of the Regexp `re` in `s`, or nil when there is none. The right operand must be a Regexp; a String is a `type` problem statically. The nil is a `nil` problem at `--strict` level 2 when used unchecked. There is no `$~`: use `String.match` to read the groups.

```ruby
p("hello" =~ /l+/)           # => 2
p("hello" =~ /z/)            # => nil
p(String.=~("ab", /b/))      # => 1
```

```ruby error
p(("abc" =~ /b/) + 1)        # !> the operands may be nil
```

## !~

`String.!~(x, Any)`

The function form of `s !~ re`: true when the Regexp does not match.

```ruby
p("hello" !~ /z/)            # => true
p(String.!~("ab", /b/))      # => false
```

## []

`String.[](x, Any, [Integer])`

The function form of `s[i]`, `s[i, n]` and `s[range]`. `s[i]` is the one-character String at position `i` (an Integer; a negative one counts from the end), or nil outside the String. `s[i, n]` is the substring of up to `n` characters starting at `i`: nil when `i` is past the end or `n` is negative, `""` when `i` equals the length. `s[range]` is the characters in the Range, nil when its start is past the end. As in Ruby, a String or a Regexp is an index too: `s["b"]` is its first occurrence (a new String) or nil, `s[/re/]` the first match, and `s[/re/, 1]` that group (the same as `String.slice`; since 2026-10-11). Another index type such as a Float is a `type` problem statically, and a Range with a count is a `TypeError`. The result is `String | nil`; the nil is the nil of a miss (`index-nil`), reported only at `--strict` level 3.

```ruby
s = "hello"
p(s[1])                      # => "e"
p(s[-1])                     # => "o"
p(s[10])                     # => nil
p(s[1, 3])                   # => "ell"
p(s[5, 1])                   # => ""
p(s[6, 1])                   # => nil
p(s[1..2])                   # => "el"
p(s[2..])                    # => "llo"
p(String.[](s, 0))           # => "h"
```

```ruby
s = "hello world"
p(s["wor"])                  # => "wor"
p(s["xyz"])                  # => nil
p(s[/o w/])                  # => "o w"
p(s[/(\w+) (\w+)/, 2])        # => "world"
```

```ruby error
p("abc"[1.5])                # !> the index must be Integer, but is Float
```

## slice

`String.slice(x, Integer|Range|String|Regexp, [Integer])`

`s[i]`, `s[i, n]` or `s[range]` written as an operation: the same results and the same nil (`index-nil`, level 3). It also takes what `slice!` takes, as Ruby's `slice`: a String gives its first occurrence (a new String) or nil, and a Regexp gives the first match, or, with a group number as the second argument, that group's String (nil when the group did not take part). A count after a String or a Range is a `TypeError`.

```ruby
s = "hello"
p(String.slice(s, 1))        # => "e"
p(String.slice(s, 1, 2))     # => "el"
p(String.slice(s, 1..2))     # => "el"
p(String.slice(s, 9))        # => nil
p(String.slice(s, "ll"))     # => "ll"
p(String.slice(s, "zz"))     # => nil
p(String.slice(s, /l+/))     # => "ll"
p(String.slice(s, /(l)(o)/, 2))      # => "o"
```

```ruby error
p(String.slice("hello", "ll", 1))    # !> TypeError: String.slice: no implicit conversion of String into Integer
```

## slice!

`String.slice!(x, Integer|Range|String|Regexp, [Integer])`

Removes the part `s[i]`, `s[i, n]` or `s[range]` from the subject **in place** and returns it. A String argument removes its first occurrence, a Regexp its first match. The result is nil when there is nothing to remove (an index past the end, a String or Regexp that does not occur), and the subject is then unchanged; using the result unchecked is a `nil` problem at `--strict` level 2.

```ruby
s = "hello"
p(String.slice!(s, 0))       # => "h"
p(String.slice!(s, /l+/))    # => "ll"
p(String.slice!(s, "zz"))    # => nil
p(s)                         # => "eo"
```

```ruby error
s = "hello"
c = String.slice!(s, 1)
p(String.upcase(c))          # !> argument 1 may be nil
```

## byteslice

`String.byteslice(x, Integer|Range, [Integer])`

The byte at byte offset `i` (one-byte String), the `n` bytes from `i`, or the bytes in a Range of byte offsets, in the subject's encoding; the result may be an invalid String when it cuts a multibyte character. nil when `i` (or the Range's start) is past the end or `n` is negative (`index-nil`, level 3). As Ruby's.

```ruby
p(String.byteslice("héllo", 1))      # => "\xC3"
p(String.byteslice("héllo", 1, 2))   # => "é"
p(String.byteslice("héllo", 1..2))   # => "é"
p(String.byteslice("hello", 9..10))  # => nil
p(String.byteslice("héllo", 9))      # => nil
```

## getbyte

`String.getbyte(x, Integer)`

The byte (an Integer 0..255) at byte offset `i`, negative from the end, or nil outside the String. Using the result unchecked is a `nil` problem at `--strict` level 2.

```ruby
s = "héllo"
p(String.getbyte(s, 0))      # => 104
p(String.getbyte(s, 1))      # => 195
p(String.getbyte(s, -1))     # => 111
p(String.getbyte(s, 99))     # => nil
```

```ruby error
p(String.getbyte("a", 5) + 1)        # !> the operands may be nil
```

## setbyte

`String.setbyte(x, Integer, Integer)`

Stores the byte `b` at byte offset `i` **in place** (the low 8 bits of `b`, as Ruby) and returns `b`. An offset outside the String is an `IndexError`.

```ruby
s = "abc"
p(String.setbyte(s, 0, 65))  # => 65
p(s)                         # => "Abc"
```

```ruby error
p(String.setbyte("abc", 10, 1))      # !> IndexError: String.setbyte: index 10 out of string
```

## bytesplice

`String.bytesplice(x, Integer, Integer, String)`

Replaces the `n` bytes from byte offset `i` with the String `str`, **in place**, and returns the subject. An offset outside the String is an `IndexError`; the offsets must fall on character boundaries.

```ruby
s = "hello"
p(String.bytesplice(s, 0, 2, "J"))   # => "Jllo"
p(s)                                 # => "Jllo"
```

```ruby error
p(String.bytesplice("abc", 10, 1, "x"))   # !> IndexError: String.bytesplice: index 10 out of string
```

## upcase, downcase, capitalize, swapcase

`String.upcase(x)`

`String.downcase(x)`

`String.capitalize(x)`

`String.swapcase(x)`

A new String with the letters in upper case, in lower case, with the first character in upper case and the rest in lower case, or with each letter's case swapped. Case mapping is Unicode's, as Ruby's (`"straße"` upcases to `"STRASSE"`).

```ruby
p(String.upcase("abc"))          # => "ABC"
p(String.downcase("ABC"))        # => "abc"
p(String.capitalize("hELLO"))    # => "Hello"
p(String.swapcase("Ab"))         # => "aB"
p(String.upcase("straße"))       # => "STRASSE"
```

## upcase!, downcase!, capitalize!, swapcase!

`String.upcase!(x)`

`String.downcase!(x)`

`String.capitalize!(x)`

`String.swapcase!(x)`

The same conversions **in place**. They return the subject when it changed, and nil when it was already in that form; using the result unchecked is a `nil` problem at `--strict` level 2.

```ruby
s = "Hello"
p(String.upcase!(s))         # => "HELLO"
p(s)                         # => "HELLO"
p(String.upcase!(s))         # => nil
p(String.capitalize!("Abc")) # => nil
```

```ruby error
s = "Hello"
r = String.upcase!(s)
p(String.size(r))            # !> argument 1 may be nil
```

## strip, lstrip, rstrip

`String.strip(x)`

`String.lstrip(x)`

`String.rstrip(x)`

A new String without the leading and trailing whitespace (`strip`), the leading only (`lstrip`), or the trailing only (`rstrip`). Whitespace is Ruby's: spaces, tabs, newlines, and NUL.

```ruby
p(String.strip("  a  "))     # => "a"
p(String.lstrip("  a  "))    # => "a  "
p(String.rstrip("  a  "))    # => "  a"
```

## strip!, lstrip!, rstrip!

`String.strip!(x)`

`String.lstrip!(x)`

`String.rstrip!(x)`

The same **in place**: the subject when something was removed, nil when there was nothing to remove (a `nil` problem at `--strict` level 2 when used unchecked).

```ruby
s = "  a  "
p(String.strip!(s))          # => "a"
p(String.strip!(s))          # => nil
p(String.lstrip!("a"))       # => nil
```

## chomp

`String.chomp(x, [String])`

A new String without one trailing line end (`"\n"`, `"\r\n"` or `"\r"`); a String without one is returned unchanged. With `suffix`, that suffix is removed instead (as Ruby's: `""` removes every trailing newline).

```ruby
p(String.chomp("a\n"))       # => "a"
p(String.chomp("a\r\n"))     # => "a"
p(String.chomp("a"))         # => "a"
p(String.chomp("abc!", "!"))         # => "abc"
p(String.chomp("abc", "x"))          # => "abc"
p(String.chomp("abc\n\n", ""))       # => "abc"
```

## chomp!

`String.chomp!(x, [String])`

Removes one trailing line end **in place**, or, with `suffix`, removes that suffix. Returns the subject when something was removed and nil otherwise (a `nil` problem at `--strict` level 2 when used unchecked).

```ruby
s = "a\n"
p(String.chomp!(s))          # => "a"
p(String.chomp!(s))          # => nil
p(String.chomp!("abc", "bc"))        # => "a"
p(String.chomp!("abc", "x"))         # => nil
```

```ruby error
p(String.chomp!("abc") + "!")        # !> the operands may be nil
```

## chop

`String.chop(x)`

A new String without its last character (`"\r\n"` counts as one). `""` gives `""`.

```ruby
p(String.chop("abc"))        # => "ab"
p(String.chop("a\r\n"))      # => "a"
p(String.chop(""))           # => ""
```

## chop!

`String.chop!(x)`

Removes the last character **in place**: the subject, or nil when it was empty (a `nil` problem at `--strict` level 2 when used unchecked).

```ruby
s = "abc"
p(String.chop!(s))           # => "ab"
p(String.chop!(""))          # => nil
```

## chr

`String.chr(x)`

The first character as a String, `""` for an empty String (Ruby's `chr`).

```ruby
p(String.chr("abc"))         # => "a"
p(String.chr(""))            # => ""
```

## delete_prefix, delete_suffix

`String.delete_prefix(x, String)`

`String.delete_suffix(x, String)`

A new String without the leading `prefix` or the trailing `suffix`; when the String does not start (end) with it, an unchanged copy.

```ruby
p(String.delete_prefix("foobar", "foo"))   # => "bar"
p(String.delete_suffix("foobar", "bar"))   # => "foo"
p(String.delete_prefix("foobar", "x"))     # => "foobar"
```

## delete_prefix!, delete_suffix!

`String.delete_prefix!(x, String)`

`String.delete_suffix!(x, String)`

The same **in place**: the subject when the prefix (suffix) was there and removed, nil otherwise (a `nil` problem at `--strict` level 2 when used unchecked).

```ruby
s = "foobar"
p(String.delete_prefix!(s, "foo"))   # => "bar"
p(String.delete_prefix!(s, "foo"))   # => nil
p(String.delete_suffix!(s, "ar"))    # => "b"
p(s)                                 # => "b"
```

## include?, start_with?, end_with?

`String.include?(x, String)`

`String.start_with?(x, String)`

`String.end_with?(x, String)`

True when `t` occurs in the String, when the String starts with `t`, or when it ends with `t`. Each takes exactly one String (Ruby's `start_with?` also takes several arguments and Regexps).

```ruby
s = "hello world"
p(String.include?(s, "wor"))     # => true
p(String.start_with?(s, "he"))   # => true
p(String.end_with?(s, "x"))      # => false
```

## index

`String.index(x, String|Regexp, [Integer])`

The character position (an Integer) of the first occurrence of the String `t`, or of the first match of the Regexp, at or after position `pos` (default 0; a negative `pos` counts from the end). nil when there is none; using it unchecked is a `nil` problem at `--strict` level 2. `""` is found at `pos`.

```ruby
s = "hello world"
p(String.index(s, "o"))          # => 4
p(String.index(s, "o", 5))       # => 7
p(String.index(s, /w./))         # => 6
p(String.index(s, "l", -3))      # => 9
p(String.index(s, "z"))          # => nil
```

```ruby error
s = "hello"
i = String.index(s, "o")
p(i + 1)                         # !> the operands may be nil
```

## rindex

`String.rindex(x, String|Regexp, [Integer])`

The position of the last occurrence of `t` (or last match of the Regexp) that starts at or before `pos` (default: the end). nil when there is none (a `nil` problem at `--strict` level 2 when used unchecked).

```ruby
s = "hello world"
p(String.rindex(s, "o"))         # => 7
p(String.rindex(s, "o", 5))      # => 4
p(String.rindex(s, "z"))         # => nil
```

```ruby error
p(String.rindex("abc", "b") + 1) # !> the operands may be nil
```

## byteindex

`String.byteindex(x, String|Regexp, [Integer])`

As `index`, but the result and `pos` are **byte** offsets. nil when there is no occurrence (a `nil` problem at `--strict` level 2 when used unchecked). `pos` must fall on a character boundary: an offset inside a multibyte character is an `IndexError` (`offset 2 does not land on character boundary`), which can be rescued.

```ruby
p(String.index("héllo", "l"))        # => 2
p(String.byteindex("héllo", "l"))    # => 3
p(String.byteindex("héllo", "z"))    # => nil
p(String.byteindex("héllo", "l", 3)) # => 3
```

```ruby error
p(String.byteindex("héllo", "l", 2))   # !> IndexError: String.byteindex: offset 2 does not land on character boundary
```

## byterindex

`String.byterindex(x, String|Regexp, [Integer])`

As `rindex`, with byte offsets: the last occurrence starting at or before byte `pos`. nil when there is none (a `nil` problem at `--strict` level 2 when used unchecked).

```ruby
p(String.byterindex("héllo", "l"))      # => 4
p(String.byterindex("héllo", "l", 3))   # => 3
```

```ruby error
p(String.byterindex("abc", "b") + 1)    # !> the operands may be nil
```

## match

`String.match(x, String|Regexp, [Integer])`

The `MatchData` of the first match of the Regexp at or after position `pos` (default 0), or nil when it does not match; using the result unchecked (`m[1]` without an `if m`) is a `nil` problem at `--strict` level 2. A String pattern is compiled as a Regexp, as Ruby's (`"."` matches any character; an invalid pattern such as `"+"` is a `RegexpError`, which can be rescued). There are no `$~` and `$1`: read the groups from the MatchData with `m[0]`, `m[1]`, `m["name"]`.

```ruby
m = String.match("hello world", /(w)(o)/)
if m
  p(m[0])                      # => "wo"
  p(m[2])                      # => "o"
end
p(String.match("hello", "zzz"))      # => nil
```

```ruby error
p(String.match("a+c", "+"))  # !> RegexpError: String.match: target of repeat operator is not specified: /+/
```

```ruby error
m = String.match("abc", /b/)
p(m[0])                      # !> the operands may be nil (MatchData | nil)
```

## match?

`String.match?(x, String|Regexp, [Integer])`

True when the pattern matches at or after position `pos` (default 0), without building a MatchData. A String pattern is compiled as a Regexp; an invalid one is a `RegexpError`, as with `match`.

```ruby
p(String.match?("hello", /l+/))      # => true
p(String.match?("hello", "o", 8))    # => false
p(String.match?("abc", "b."))        # => true
begin
  p(String.match?("a+c", "+"))
rescue RegexpError => e
  puts(Exception.message(e))         # => target of repeat operator is not specified: /+/
end
```

## scan

`String.scan(x, String|Regexp)`

Every non-overlapping match, from left to right, as an Array. Without groups, an Array of the matched Strings. With groups, an Array of **Tuples** with one String per group (Ruby gives Arrays); a group that did not take part is nil in its position, the nil of a miss (`index-nil`, level 3).

```ruby
p(String.scan("a1b22", /\d+/))       # => ["1", "22"]
p(String.scan("abab", "ab"))         # => ["ab", "ab"]
p(String.scan("a1b22", /(\w)(\d)/))  # => [["a", "1"], ["b", "2"]]
p(String.scan("a1 b", /(\w)(\d)?/))  # => [["a", "1"], ["b", nil]]
```

## count

`String.count(x, String)`

The number of characters (an Integer) that belong to the set `chars`, written as in Ruby's `tr`: `"lo"` lists characters, `"a-z"` is a range, a leading `^` negates. Only one set is accepted (Ruby intersects several). A reversed range such as `"z-a"` is an `ArgumentError` (`invalid range "z-a" in string transliteration`), here as in `tr`, `delete` and `squeeze`.

```ruby
s = "hello world"
p(String.count(s, "lo"))     # => 5
p(String.count(s, "a-z"))    # => 10
p(String.count(s, "^l"))     # => 8
```

```ruby error
p(String.count("abc", "z-a"))    # !> ArgumentError: String.count: invalid range "z-a" in string transliteration
```

## partition, rpartition

`String.partition(x, String|Regexp)`

`String.rpartition(x, String|Regexp)`

Splits around the first (`partition`) or last (`rpartition`) occurrence of `sep`, a String or a Regexp, into the **Tuple** `[before, separator, after]` (Ruby gives an Array). When `sep` does not occur, `partition` gives `[s, "", ""]` and `rpartition` gives `["", "", s]`.

```ruby
p(String.partition("a=b=c", "="))    # => ["a", "=", "b=c"]
p(String.rpartition("a=b=c", "="))   # => ["a=b", "=", "c"]
p(String.partition("hello", /l+/))   # => ["he", "ll", "o"]
p(String.partition("abc", "x"))      # => ["abc", "", ""]
p(String.rpartition("abc", "x"))     # => ["", "", "abc"]
```

## sub, gsub

`String.sub(x, String|Regexp, [String|Hash]) [{ }]`

`String.gsub(x, String|Regexp, [String|Hash]) [{ }]`

A new String with the first match (`sub`) or every match (`gsub`) of `pattern` replaced. A String pattern is matched literally (`"."` is a period, not any character). The replacement is one of three: a **String**, in which `\0` is the whole match, `\1` the first group and `\k<name>` a named group; a **Hash** whose keys are matched Strings and whose values (shown with `to_s`) replace them, a match that is not a key being removed; or a **block** that receives the matched String (not a MatchData) and whose result, shown with `to_s`, is the replacement. Giving neither a replacement nor a block is an `ArgumentError` at run time (Ruby's enumerator form does not exist).

```ruby
s = "hello"
p(String.sub(s, "l", "L"))                 # => "heLlo"
p(String.gsub(s, "l", "L"))                # => "heLLo"
p(String.gsub(s, /l+/, "<\\0>"))           # => "he<ll>o"
p(String.gsub(s, /(l)(o)/, "\\2\\1"))      # => "helol"
p(String.gsub(s, /[el]/, Hash.of("e" => "3", "l" => 1)))   # => "h311o"
p(String.gsub(s, /l/) { |m| String.upcase(m) })         # => "heLLo"
p(String.sub(s, /l/) { |m| 7 })            # => "he7lo"
p(s)                                       # => "hello"
```

```ruby error
p(String.sub("abc", "b"))    # !> ArgumentError: String.sub: sub needs a replacement or a block
```

## sub!, gsub!

`String.sub!(x, String|Regexp, [String|Hash]) [{ }]`

`String.gsub!(x, String|Regexp, [String|Hash]) [{ }]`

The same replacement **in place**. They return the subject when something was replaced and nil when the pattern did not match (the subject unchanged); using the result unchecked is a `nil` problem at `--strict` level 2. The replacement forms are those of `sub` and `gsub`.

```ruby
s = "hello"
p(String.sub!(s, "l", "L"))      # => "heLlo"
p(s)                             # => "heLlo"
p(String.gsub!(s, "zz", "y"))    # => nil
String.gsub!(s, /l/i) { |m| "_" }
p(s)                             # => "he__o"
```

```ruby error
s = "hello"
p(String.sub!(s, "x", "y") + "!")    # !> the operands may be nil
```

## tr

`String.tr(x, String, String)`

A new String in which each character of the set `from` is replaced by the character at the same position in `to` (Ruby's `tr`): `"a-y"` is a range, a leading `^` in `from` negates, a `to` shorter than `from` repeats its last character, and an empty `to` deletes. A reversed range is an `ArgumentError` (`invalid range "z-a" in string transliteration`).

```ruby
p(String.tr("hello", "el", "ip"))    # => "hippo"
p(String.tr("hello", "a-y", "b-z"))  # => "ifmmp"
p(String.tr("hello", "^l", "*"))     # => "**ll*"
p(String.tr("hello", "lo", ""))      # => "he"
```

```ruby error
p(String.tr("abc", "z-a", "x"))      # !> ArgumentError: String.tr: invalid range "z-a" in string transliteration
```

## tr!

`String.tr!(x, String, String)`

`tr` **in place**: the subject when something changed, nil otherwise (a `nil` problem at `--strict` level 2 when used unchecked).

```ruby
s = "hello"
p(String.tr!(s, "l", "L"))   # => "heLLo"
p(String.tr!(s, "z", "Z"))   # => nil
```

## tr_s

`String.tr_s(x, String, String)`

As `tr`, and then squeezes runs of the replaced characters into one (Ruby's `tr_s`).

```ruby
p(String.tr_s("hello", "l", "r"))      # => "hero"
p(String.tr_s("aabbcc", "a-c", "x"))   # => "x"
```

## tr_s!

`String.tr_s!(x, String, String)`

`tr_s` **in place**: the subject when something changed, nil otherwise (a `nil` problem at `--strict` level 2 when used unchecked).

```ruby
s = "aabb"
p(String.tr_s!(s, "a", "x"))     # => "xbb"
p(String.tr_s!(s, "z", "x"))     # => nil
```

## delete

`String.delete(x, String, *String)`

A new String without the characters that belong to every set given (`tr` syntax: `"a-k"`, `"^l"`). Several sets intersect, as Ruby's: `delete("hello", "l", "o")` deletes nothing, since no character is in both sets. A reversed range is an `ArgumentError`.

```ruby
p(String.delete("hello", "l"))     # => "heo"
p(String.delete("hello", "a-k"))   # => "llo"
p(String.delete("hello", "^l"))    # => "ll"
p(String.delete("hello", "l", "o"))        # => "hello"
p(String.delete("hello", "a-z", "^l"))     # => "ll"
```

## delete!

`String.delete!(x, String, *String)`

Deletes **in place** the characters that belong to every set given (several sets intersect, as Ruby's). The subject when something was deleted, nil otherwise (a `nil` problem at `--strict` level 2 when used unchecked).

```ruby
s = "hello"
p(String.delete!(s, "l"))              # => "heo"
p(String.delete!(s, "z"))              # => nil
p(String.delete!("hello", "l", "^l"))  # => nil
```

```ruby error
p(String.delete!("a", "z-a"))          # !> ArgumentError: String.delete!: invalid range "z-a"
```

## squeeze

`String.squeeze(x, *String)`

A new String in which runs of the same character are reduced to one; with sets, only runs of characters that belong to every set (several sets intersect, as Ruby's).

```ruby
p(String.squeeze("aaabbb  c"))         # => "ab c"
p(String.squeeze("aaabbb  c", "a"))    # => "abbb  c"
p(String.squeeze("aaabbb  c", "a-b"))  # => "ab  c"
p(String.squeeze("aaabbb  c", "a-b", "b"))     # => "aaab  c"
```

## squeeze!

`String.squeeze!(x, *String)`

`squeeze` **in place**, with any number of sets (their intersection, as Ruby's). The subject when something changed, nil otherwise (a `nil` problem at `--strict` level 2 when used unchecked).

```ruby
s = "aaabbb"
p(String.squeeze!(s))                # => "ab"
p(String.squeeze!(s))                # => nil
p(String.squeeze!("aabb", "a"))      # => "abb"
```

## split

`String.split(x, [String|Regexp], [Integer])`

Splits into an Array of Strings. Without `sep` or with `" "`, it splits on runs of whitespace and ignores leading whitespace (awk style); with another String, on each occurrence of it; with `""`, into characters; with a Regexp, on each match, and the Regexp's groups are included in the result (Ruby's rule). `limit` (default 0) bounds the number of pieces: a positive `limit` gives at most that many, the last holding the rest; 0 drops trailing empty Strings; a negative `limit` keeps them. There is no block form.

```ruby
p(String.split("a b  c"))              # => ["a", "b", "c"]
p(String.split("a,b,,c", ","))         # => ["a", "b", "", "c"]
p(String.split("a,b,,c", ",", 2))      # => ["a", "b,,c"]
p(String.split("a,b,,", ","))          # => ["a", "b"]
p(String.split("a,b,,", ",", -1))      # => ["a", "b", "", ""]
p(String.split("a1b2c", /\d/))         # => ["a", "b", "c"]
p(String.split("a1b", /(\d)/))         # => ["a", "1", "b"]
p(String.split("abc", ""))             # => ["a", "b", "c"]
p(String.split("", ","))               # => []
```

## chars

`String.chars(x)`

An Array of the characters, each a one-character String.

```ruby
p(String.chars("aé"))        # => ["a", "é"]
p(String.chars(""))          # => []
```

## lines

`String.lines(x)`

An Array of the lines, each keeping its `"\n"` (the last one may lack it).

```ruby
p(String.lines("a\nb\n"))    # => ["a\n", "b\n"]
p(String.lines("a\nb"))      # => ["a\n", "b"]
p(String.lines(""))          # => []
```

## bytes

`String.bytes(x)`

An Array of the bytes as Integers (0..255).

```ruby
p(String.bytes("aé"))        # => [97, 195, 169]
```

## codepoints

`String.codepoints(x)`

An Array of the Unicode code points as Integers.

```ruby
p(String.codepoints("aé"))   # => [97, 233]
```

## grapheme_clusters

`String.grapheme_clusters(x)`

An Array of the grapheme clusters (what a reader sees as one character: a base character with its combining marks), each a String.

```ruby
s = "éa"
p(String.length(s))                  # => 3
p(Array.size(String.grapheme_clusters(s)))                       # => 2
p(Array.map(String.grapheme_clusters(s)) { |g| String.length(g) })   # => [2, 1]
```

## each_char

`String.each_char(x) { }`

Calls the block with each character (a one-character String) in order and returns the subject. The block is required: a call without one is a static error (Ruby's enumerator form does not exist).

```ruby
r = String.each_char("ab") { |c| p(c) }
# => "a"
# => "b"
p(r)                         # => "ab"
```

```ruby error
String.each_char("ab")       # !> String.each_char requires a block
```

## each_line

`String.each_line(x) { }`

Calls the block with each line, keeping its `"\n"`, and returns the subject. The block is required; there is no separator argument (Ruby's `each_line(sep)`).

```ruby
String.each_line("a\nb") { |l| p(l) }
# => "a\n"
# => "b"
```

## each_byte

`String.each_byte(x) { }`

Calls the block with each byte (an Integer) and returns the subject. The block is required.

```ruby
String.each_byte("aé") { |b| p(b) }
# => 97
# => 195
# => 169
```

## each_codepoint

`String.each_codepoint(x) { }`

Calls the block with each code point (an Integer) and returns the subject. The block is required.

```ruby
String.each_codepoint("aé") { |c| p(c) }
# => 97
# => 233
```

## each_grapheme_cluster

`String.each_grapheme_cluster(x) { }`

Calls the block with each grapheme cluster (a String) and returns the subject. The block is required.

```ruby
String.each_grapheme_cluster("éa") { |g| p(String.length(g)) }
# => 2
# => 1
```

## upto

`String.upto(x, String) { }`

Calls the block with `x`, then `String.succ` of it, and so on up to and including `last`, as Ruby's `upto` (`"a".upto("e")`); the walk stops when a value gets longer than `last`, so nothing is yielded when `x` is already past it. Strings of digits count as numbers (`"9"` up to `"11"`). Returns the subject.

```ruby
String.upto("a", "c") { |s| p(s) }
# => "a"
# => "b"
# => "c"
String.upto("az", "bb") { |s| p(s) }
# => "az"
# => "ba"
# => "bb"
String.upto("9", "11") { |s| p(s) }
# => "9"
# => "10"
# => "11"
String.upto("b", "a") { |s| p(s) }
```

## succ, next

`String.succ(x)`

`String.next(x)`

A new String that is the successor of `x` in Ruby's sense: the rightmost letter or digit is incremented, with a carry to the left (`"az"` to `"ba"`, `"a9"` to `"b0"`, `"Zz"` to `"AAa"`). `""` gives `""`.

```ruby
p(String.succ("az"))         # => "ba"
p(String.next("Zz"))         # => "AAa"
p(String.succ("a9"))         # => "b0"
p(String.succ(""))           # => ""
```

## succ!, next!

`String.succ!(x)`

`String.next!(x)`

Replace the subject by its successor **in place** and return the subject (never nil).

```ruby
s = "az"
p(String.succ!(s))           # => "ba"
p(String.next!(s))           # => "bb"
p(s)                         # => "bb"
```

## reverse

`String.reverse(x)`

A new String with the characters in reverse order.

```ruby
p(String.reverse("abc"))     # => "cba"
```

## reverse!

`String.reverse!(x)`

Reverses the subject **in place** and returns it (never nil).

```ruby
s = "abc"
p(String.reverse!(s))        # => "cba"
p(s)                         # => "cba"
```

## ljust, rjust, center

`String.ljust(x, Integer, [String])`

`String.rjust(x, Integer, [String])`

`String.center(x, Integer, [String])`

A new String of `width` characters: the subject padded on the right (`ljust`), on the left (`rjust`), or on both sides (`center`, the extra padding going right) with repetitions of `pad` (default `" "`), cut to fit. A `width` not greater than the length gives an unchanged copy. An empty `pad` is an `ArgumentError` (`zero width padding`).

```ruby
p(String.ljust("ab", 5))         # => "ab   "
p(String.rjust("ab", 5, "0"))    # => "000ab"
p(String.center("ab", 6, "*"))   # => "**ab**"
p(String.ljust("ab", 5, "xy"))   # => "abxyx"
p(String.center("ab", 1))        # => "ab"
```

```ruby error
p(String.ljust("a", 3, ""))      # !> ArgumentError: String.ljust: zero width padding
```

## to_s

`String.to_s(x)`

The String itself. It exists so that `T.to_s(x)` can be written for every type.

```ruby
p(String.to_s("x"))          # => "x"
```

## to_i

`String.to_i(x, [Integer])`

The Integer written at the start of the String in base `base` (2..36, default 10), as Ruby's `to_i`: leading whitespace, a sign and underscores between digits are allowed, reading stops at the first other character, and a String with no digits gives 0 (never an error; `Integer(s)` in [Built-in operations](../09-builtins.md) raises instead). Base 0 reads a prefix: `0x` (16), `0b` (2), `0o` or a leading `0` (8). With base 16, a `0x` prefix is accepted. A base outside 2..36 (other than 0) is an `ArgumentError`.

```ruby
p(String.to_i("42"))         # => 42
p(String.to_i("  -12abc"))   # => -12
p(String.to_i("abc"))        # => 0
p(String.to_i("ff", 16))     # => 255
p(String.to_i("0x1f", 16))   # => 31
p(String.to_i("0b101", 0))   # => 5
p(String.to_i("012", 0))     # => 10
p(String.to_i("z", 36))      # => 35
```

```ruby error
p(String.to_i("1", 37))      # !> ArgumentError: String.to_i: invalid radix 37
```

## to_f

`String.to_f(x)`

The Float written at the start of the String (`"3.5e2"`, underscores allowed), 0.0 when there is none (never an error; `Float(s)` raises). A value too large for a Float is `Infinity`.

```ruby
p(String.to_f("3.5e2"))      # => 350.0
p(String.to_f("1_000.5"))    # => 1000.5
p(String.to_f("abc"))        # => 0.0
```

## to_r

`String.to_r(x)`

The Rational written at the start of the String: `"3/4"`, `"0.75"`, `"1e2"`. A String with no number gives `(0/1)`.

```ruby
p(String.to_r("3/4"))        # => (3/4)
p(String.to_r("0.75"))       # => (3/4)
p(String.to_r("abc"))        # => (0/1)
```

## to_c

`String.to_c(x)`

The Complex written at the start of the String (`"1+2i"`); `(0+0i)` when there is none.

```ruby
p(String.to_c("1+2i"))       # => (1+2i)
p(String.to_c("x"))          # => (0+0i)
```

## to_sym, intern

`String.to_sym(x)`

`String.intern(x)`

The Symbol with this name. Any String can become a Symbol; one that is not an identifier is shown quoted.

```ruby
p(String.to_sym("abc"))      # => :abc
p(String.intern("a b"))      # => :"a b"
```

## hex

`String.hex(x)`

The String read as a hexadecimal Integer, as Ruby's `hex`: an optional sign and `0x` prefix, reading stops at the first non-hex character, and 0 when there are no digits.

```ruby
p(String.hex("ff"))          # => 255
p(String.hex("0x1F"))        # => 31
p(String.hex("-a"))          # => -10
p(String.hex("zz"))          # => 0
```

## oct

`String.oct(x)`

The String read as an octal Integer, as Ruby's `oct`; a prefix `0x`, `0b` or `0o` switches the base, as Ruby's. 0 when there are no digits.

```ruby
p(String.oct("17"))          # => 15
p(String.oct("0x1f"))        # => 31
p(String.oct("0b11"))        # => 3
p(String.oct("9"))           # => 0
```

## ord

`String.ord(x)`

The code point (an Integer) of the first character. An empty String is an `ArgumentError`.

```ruby
p(String.ord("a"))           # => 97
p(String.ord("é"))           # => 233
```

```ruby error
p(String.ord(""))            # !> ArgumentError: String.ord: empty string
```

## sum

`String.sum(x, [Integer])`

The sum of the bytes modulo 2 to the power `bits` (default 16), Ruby's simple checksum.

```ruby
p(String.sum("abc"))         # => 294
p(String.sum("abc", 8))      # => 38
```

## crypt

`String.crypt(x, String)`

The one-way hash of the String with `salt` by the system's `crypt(3)`, as Ruby's. A salt shorter than two bytes is an `ArgumentError`. The result depends on the platform's libc.

```ruby
p(String.size(String.crypt("abc", "ab")) > 2)   # => true
```

```ruby error
p(String.crypt("abc", "a"))  # !> ArgumentError: String.crypt: salt too short (need >=2 bytes)
```

## concat

`String.concat(x, *String|Integer)`

Appends each argument to the subject **in place** and returns the subject. An Integer is appended as the character with that code point.

```ruby
s = "a"
p(String.concat(s, "b", 99, "d"))    # => "abcd"
p(s)                                 # => "abcd"
```

## append_as_bytes

`String.append_as_bytes(x, *String|Integer)`

Appends the **bytes** of each String argument, and each Integer as one byte (its low 8 bits), **in place** without converting encodings or checking validity, as Ruby's; the result may be an invalid String. Returns the subject.

```ruby
s = "a"
p(String.append_as_bytes(s, "é", 255))   # => "aé\xFF"
p(String.bytesize(s))                    # => 4
p(String.valid_encoding?(s))             # => false
```

## prepend

`String.prepend(x, *String)`

Inserts the arguments, in order, at the front of the subject **in place** and returns the subject.

```ruby
s = "c"
p(String.prepend(s, "a", "b"))   # => "abc"
p(s)                             # => "abc"
```

## insert

`String.insert(x, Integer, String)`

Inserts `str` **in place** before the character at position `i`; a negative `i` inserts after the character counted from the end, so -1 appends. Returns the subject. A position outside the String is an `IndexError`.

```ruby
s = "ac"
p(String.insert(s, 1, "b"))      # => "abc"
p(String.insert(s, -1, "d"))     # => "abcd"
p(String.insert(s, 0, "_"))      # => "_abcd"
```

```ruby error
p(String.insert("abc", 10, "x")) # !> IndexError: String.insert: index 10 out of string
```

## replace

`String.replace(x, String)`

Replaces the contents of the subject with those of `str`, **in place**, and returns the subject. Every variable holding the subject sees the new contents.

```ruby
s = "old"
t = s
String.replace(s, "new")
p(t)                         # => "new"
```

## clear

`String.clear(x)`

Removes every character **in place** and returns the subject (now `""`).

```ruby
s = "hello"
p(String.clear(s))           # => ""
p(String.empty?(s))          # => true
```

## encoding

`String.encoding(x)`

The name of the String's encoding as a String (`"UTF-8"`, `"ASCII-8BIT"`, ...). Ruby gives an Encoding object; Sake has no such type.

```ruby
p(String.encoding("é"))              # => "UTF-8"
p(String.encoding(String.b("é")))    # => "ASCII-8BIT"
```

## force_encoding

`String.force_encoding(x, String)`

Relabels the subject's bytes as encoding `enc` **in place** and returns the subject, as Ruby's `force_encoding`. The bytes are not converted; `valid_encoding?` tells whether they are valid in the new encoding. An unknown encoding name is an `ArgumentError`; a String that cannot be changed (a Hash key, a Symbol's name, ...) is a `TypeError`.

```ruby
s = String.b("\xC3\xA9")
p(String.encoding(s))                # => "ASCII-8BIT"
t = String.force_encoding(s, "UTF-8")
p(String.encoding(s))                # => "UTF-8"
p(Kernel.equal?(s, t))               # => true
p(s)                                 # => "é"
p(String.valid_encoding?(String.force_encoding("\xff", "UTF-8")))   # => false
```

```ruby error
p(String.force_encoding("a", "bogus"))   # !> ArgumentError: String.force_encoding: unknown encoding name - bogus
```

## encode

`String.encode(x, String, [String])`

A new String with the characters converted to the encoding `to`; `from` (default: the subject's encoding) says how to read the bytes. A character that `to` cannot represent, an invalid byte in the subject, or an unknown encoding name is an `EncodingError`.

```ruby
u = String.encode("é", "UTF-16LE")
p(String.encoding(u))                            # => "UTF-16LE"
p(String.bytesize(u))                            # => 2
p(String.encode("\xE9", "UTF-8", "ISO-8859-1"))  # => "é"
begin
  String.encode("é", "US-ASCII")
rescue EncodingError => e
  p(Exception.message(e))                        # => "U+00E9 from UTF-8 to US-ASCII"
end
```

## valid_encoding?

`String.valid_encoding?(x)`

True when the bytes are valid in the String's encoding.

```ruby
p(String.valid_encoding?("é"))       # => true
p(String.valid_encoding?("\xff"))    # => false
```

## ascii_only?

`String.ascii_only?(x)`

True when every character is ASCII (an empty String is).

```ruby
p(String.ascii_only?("abc"))         # => true
p(String.ascii_only?("é"))           # => false
```

## b

`String.b(x)`

A copy of the String with the same bytes labelled `ASCII-8BIT` (binary), so that its length is its byte count.

```ruby
b = String.b("é")
p(b)                         # => "\xC3\xA9"
p(String.length(b))          # => 2
```

## scrub

`String.scrub(x, [String])`

A new String in which every invalid byte sequence is replaced by `repl` (default: the replacement character `"�"`).

```ruby
p(String.scrub("a\xffb", "?"))       # => "a?b"
p(String.valid_encoding?(String.scrub("a\xffb")))   # => true
```

## scrub!

`String.scrub!(x, [String])`

`scrub` **in place**. Returns the subject, also when there was nothing to replace (unlike the other `!` forms it never gives nil, as Ruby's).

```ruby
s = "a\xffb"
p(String.scrub!(s, "!"))     # => "a!b"
p(String.scrub!("ok"))       # => "ok"
```

## unicode_normalize

`String.unicode_normalize(x, [Symbol])`

A new String in Unicode normalization form `form`: `:nfc` (default), `:nfd`, `:nfkc` or `:nfkd`. Another Symbol is an `ArgumentError`; a String in a non-Unicode encoding (a binary String) is an `EncodingError`.

```ruby
d = String.unicode_normalize("é", :nfd)
p(String.length(d))                          # => 2
p(String.length(String.unicode_normalize(d)))   # => 1
```

```ruby error
p(String.unicode_normalize("a", :bogus))     # !> ArgumentError: String.unicode_normalize: Invalid normalization form bogus
```

## unicode_normalize!

`String.unicode_normalize!(x, [Symbol])`

`unicode_normalize` **in place**; returns the subject (never nil).

```ruby
s = "é"
p(String.unicode_normalize!(s))      # => "é"
p(String.length(s))                  # => 1
```

## unicode_normalized?

`String.unicode_normalized?(x, [Symbol])`

True when the String is already in the form `form` (default `:nfc`).

```ruby
p(String.unicode_normalized?("é"))           # => true
p(String.unicode_normalized?("é", :nfd))     # => false
```

## dump

`String.dump(x)`

A new String that is a quoted, ASCII-only source form of the subject: non-printing and non-ASCII characters become escapes such as `\n` and `é`, and `"` and `\` are escaped. `undump` reverses it.

```ruby
p(String.dump("a\né"))  # => "\"a\\n\\u00E9\""
```

## undump

`String.undump(x)`

The String that `dump` produced the subject from: the quotes are removed and the escapes read. A String that is not in `dump`'s form is an `ArgumentError` with Ruby's message (Ruby's method raises RuntimeError; in Sake a RuntimeError comes only from `raise "msg"` and `Thread.raise`).

```ruby
p(String.undump("\"a\\n\""))         # => "a\n"
p(String.undump(String.dump("é")))   # => "é"
begin
  p(String.undump("abc"))
rescue ArgumentError => e
  puts(Exception.message(e))         # => invalid dumped string; not wrapped with '"' nor '"...".force_encoding("...")' form
end
```

```ruby error
p(String.undump("abc"))      # !> ArgumentError: String.undump: invalid dumped string
```

## unpack

`String.unpack(x, String)`

Decodes the bytes according to the `Array.pack` format `fmt` into an Array, as Ruby's `unpack`: `C` an unsigned byte, `v` a little-endian 16-bit Integer, `e` a little-endian Float, `a`/`A`/`Z` Strings, `*` repeating. The element type follows the directives (Integer, String, Float); a directive with no bytes left gives nil. An unknown directive is an `ArgumentError`.

```ruby
p(String.unpack("AB", "C*"))             # => [65, 66]
p(String.unpack("\x01\x00", "v"))        # => [1]
p(String.unpack("ab", "a1a1"))           # => ["a", "b"]
p(String.unpack("\x00\x00\x80\x3f", "e"))   # => [1.0]
p(String.unpack("", "C"))                # => [nil]
```

```ruby error
p(String.unpack("abc", "Z%"))            # !> ArgumentError: String.unpack: % is not supported
```

## unpack1

`String.unpack1(x, String)`

The first element of `unpack(x, fmt)`, or nil when there are no bytes for it; using the result unchecked is a `nil` problem at `--strict` level 2.

```ruby
p(String.unpack1("\x01\x00", "v"))   # => 1
p(String.unpack1("abc", "a*"))       # => "abc"
p(String.unpack1("", "C"))           # => nil
```

```ruby error
p(String.unpack1("ab", "C") + 1)     # !> the operands may be nil
```

# Symbol

A Symbol is a name as a value, made by the literal `:name` (a name with spaces or other characters is `:"hello world"`; see [Values and types](../03-values.md)). Symbols of the same name are always one and the same value; they serve as Hash keys (the labels of `Hash.of(name: 1)` are Symbol keys) and as the marks of `case`/`in` branches. `String.to_sym(s)` (`String.intern`) makes one from a String, and `Symbol.to_s` turns it back. Ruby's `%i[a b]` is not available yet (rejected statically); an Array of Symbols is written `Symbol[:a, :b]`.

There is no `Symbol#to_proc` (`&:name`). Blocks are not values, so `Array.map(xs, &:to_s)` is rejected statically; write `{ |x| Integer.to_s(x) }` ([Functions and blocks](../04-functions.md)).

When branching on literals, `case s in :a ... in :b ... end`, and `s` is a Symbol that did not come from a literal (the result of `String.to_sym`, say), `--strict=3` reports that some values fall through, as `exhaustive` (add an `else`; see [Control](../06-control.md)).

The operators on Symbols are `==`, `!=`, `<`, `<=`, `>`, `>=` and `<=>`, and they compare Symbols with Symbols only (with a String or an Integer on the right, `<` and the others are a `type` problem statically). `:a == "a"` is false, and so is the function form `Symbol.==(:a, "a")`. `Symbol.<(x, y)` and so on are the function forms of those operators ([Operators and indexing](../05-operators.md)).

The string-like operations (`upcase`, `slice`, `match`, `start_with?`, ...) give the result of treating the name as a String. Some return a Symbol (`upcase` and friends), some a String (`slice`). None changes the Symbol itself (a Symbol is immutable).

## Symbol[]

`Symbol[*Any]`

Makes an **Array of Symbols**. It is a typed Array like `Integer[]`: every element must be a Symbol, and every write (`Array.push`, ...) is checked. Anything but a Symbol is a `type` problem statically and a `TypeError` at run time. The empty Array of Symbols is `Symbol[]`.

```ruby
syms = Symbol[:a, :b]
Array.push(syms, :c)
p(syms)                            # => [:a, :b, :c]
p(Array.size(Symbol[]))            # => 0
```

```ruby error
Array.push(Symbol[:a], "x")        # !> Array.push: an element must be Symbol, but is String
```

## to_s, id2name, name

`Symbol.to_s(x)`

`Symbol.id2name(x)`

`Symbol.name(x)`

The name as a String. `to_s` and `id2name` return a new (writable) String each time; `name` returns the name the Symbol holds, which is frozen as in Ruby, so changing it with `String.upcase!` or the like is a `TypeError`.

```ruby
p(Symbol.to_s(:hello))             # => "hello"
p(Symbol.id2name(:"with space"))   # => "with space"
s = Symbol.to_s(:abc)
String.upcase!(s)
p(s)                               # => "ABC"
p(Symbol.name(:abc))               # => "abc"
```

```ruby error
s = Symbol.name(:abc)
String.upcase!(s)                  # !> TypeError: String.upcase!: cannot change this String in place
```

## to_sym, intern

`Symbol.to_sym(x)`

`Symbol.intern(x)`

The Symbol itself (as Ruby's). A Symbol is made from a String by `String.to_sym` and `String.intern`.

```ruby
p(Symbol.to_sym(:a))               # => :a
p(Symbol.intern(:a) == :a)         # => true
p(String.to_sym("a") == :a)        # => true
```

## length, size

`Symbol.length(x)`

`Symbol.size(x)`

The number of characters in the name (an Integer; not bytes).

```ruby
p(Symbol.length(:hello))           # => 5
p(Symbol.size(:日本))              # => 2
```

## empty?

`Symbol.empty?(x)`

True when the name is empty (`:""`).

```ruby
p(Symbol.empty?(:""))              # => true
p(Symbol.empty?(:a))               # => false
```

## encoding

`Symbol.encoding(x)`

The name of the name's encoding, as a String (Ruby returns an Encoding object). An ASCII-only name is `"US-ASCII"`, any other `"UTF-8"`.

```ruby
p(Symbol.encoding(:hello))         # => "US-ASCII"
p(Symbol.encoding(:日本))          # => "UTF-8"
```

## upcase, downcase, capitalize, swapcase

`Symbol.upcase(x)`

`Symbol.downcase(x)`

`Symbol.capitalize(x)`

`Symbol.swapcase(x)`

A new Symbol with the name in upper case, in lower case, with only the first character upper case (the rest lower), or with the cases swapped. As Ruby's `String` operations of the same names; Unicode characters are converted too. The option arguments (`:ascii`, ...) do not exist.

```ruby
p(Symbol.upcase(:hello))           # => :HELLO
p(Symbol.downcase(:HeLLo))         # => :hello
p(Symbol.capitalize(:hello_world)) # => :Hello_world
p(Symbol.swapcase(:HeLLo))         # => :hEllO
p(Symbol.downcase(:ÀB))            # => :àb
```

## succ, next

`Symbol.succ(x)`

`Symbol.next(x)`

A new Symbol with the name advanced by Ruby's `String#succ` (`:a` → `:b`, `:az` → `:ba`, `:zz` → `:aaa`, `:a9` → `:b0`).

```ruby
p(Symbol.succ(:a))                 # => :b
p(Symbol.next(:az))                # => :ba
p(Symbol.succ(:zz))                # => :aaa
p(Symbol.succ(:a9))                # => :b0
```

## start_with?, end_with?

`Symbol.start_with?(x, String)`

`Symbol.end_with?(x, String)`

True when the name starts or ends with the String `s`. The argument is a String; passing a Symbol is a `type` problem statically (Ruby's forms with several arguments or a Regexp do not exist).

```ruby
p(Symbol.start_with?(:hello, "he"))   # => true
p(Symbol.end_with?(:hello, "lo"))     # => true
p(Symbol.start_with?(:hello, "x"))    # => false
```

```ruby error
p(Symbol.start_with?(:abc, :a))    # !> Symbol.start_with?: argument 2 must be String, but is :a
```

## slice

`Symbol.slice(x, Integer, [Integer])`

A part of the name as a String. `slice(s, i)` is the one character at position `i` (a negative one counts from the end); `slice(s, i, n)` is `n` characters from position `i`. Outside the name it is nil (as Ruby's `String#slice`; `slice(s, i, n)` with `i` equal to the length is `""`). Because the result may be nil, using it unchecked is reported at level 2. There is no Range or String index (a `type` problem statically).

```ruby
p(Symbol.slice(:hello, 1))         # => "e"
p(Symbol.slice(:hello, -1))        # => "o"
p(Symbol.slice(:hello, 1, 3))      # => "ell"
p(Symbol.slice(:hello, 10))        # => nil
p(Symbol.slice(:abc, 3, 1))        # => ""
```

```ruby error
p(Symbol.slice(:abc, 0..1))        # !> Symbol.slice: argument 2 must be Integer, but is Range[Integer]
```

## match

`Symbol.match(x, Regexp|String)`

Matches the pattern against the name and returns the MatchData of the first match, or nil when there is none (reported at level 2). A String pattern is taken as the source of a regular expression (as in Ruby).

```ruby
m = Symbol.match(:hello, /l+/)
p(m)                               # => #<MatchData "ll">
if m
  p(MatchData.to_s(m))             # => "ll"
end
p(Symbol.match(:hello, "l+"))      # => #<MatchData "ll">
p(Symbol.match(:hello, /z/))       # => nil
```

## match?

`Symbol.match?(x, Regexp|String)`

True when the name matches the pattern. It makes no MatchData, so use it when only whether it matched matters.

```ruby
p(Symbol.match?(:hello, /ell/))    # => true
p(Symbol.match?(:hello, "x"))      # => false
```

## casecmp

`Symbol.casecmp(x, Symbol)`

Compares the names of two Symbols ignoring case (in the ASCII range) and returns -1, 0, or 1. The argument must be a Symbol (a String is a `type` problem statically). Because Ruby's `casecmp` returns nil when it cannot compare, the result type is `Integer | nil`, and using it in arithmetic unchecked is reported at level 2.

```ruby
p(Symbol.casecmp(:a, :A))          # => 0
p(Symbol.casecmp(:a, :B))          # => -1
p(Symbol.casecmp(:b, :A))          # => 1
```

```ruby error
p(Symbol.casecmp(:a, "b"))         # !> Symbol.casecmp: argument 2 must be Symbol, but is String
```

## casecmp?

`Symbol.casecmp?(x, Symbol)`

True when the names of two Symbols are equal ignoring case (by Unicode case folding). Unlike `casecmp`, the result is a Boolean and never nil: where Ruby's method gives nil (incompatible encodings) it is false.

```ruby
p(Symbol.casecmp?(:a, :A))         # => true
p(Symbol.casecmp?(:a, :b))         # => false
p(Symbol.casecmp?(:Straße, :STRASSE))   # => true
```

## ==, !=

`Symbol.==(x, Any)`

`Symbol.!=(x, Any)`

True when the two are Symbols of the same name (`!=` is the negation). A right operand that is not a Symbol (a String, an Integer, nil) is simply not equal, with the function form as with the operator: `Symbol.==(:a, "a")` is false (no error).

```ruby
p(:a == :a)                        # => true
p(:a != :b)                        # => true
p(:a == "a")                       # => false
p(Symbol.==(:a, :a))               # => true
p(Symbol.==(:a, "a"))              # => false
p(Symbol.!=(:a, "a"))              # => true
```

## <, <=, >, >=

`Symbol.<(x, Any)`

`Symbol.<=(x, Any)`

`Symbol.>(x, Any)`

`Symbol.>=(x, Any)`

Compare the names of two Symbols as Strings (byte order, as in Ruby). The right operand must be a Symbol; a String or an Integer is a `type` problem statically.

```ruby
p(:a < :b)                         # => true
p(:b >= :c)                        # => false
p(Symbol.<=(:a, :a))               # => true
```

```ruby error
p(:a < "a")                        # !> which the left operand's type does not support
```

## <=>

`Symbol.<=>(x, Any)`

The result of comparing the names as -1, 0, or 1. The rules are those of `<`, and the right operand must be a Symbol. It serves as the key of `Array.sort_by`.

```ruby
p(:a <=> :b)                       # => -1
p(:aa <=> :b)                      # => -1
p(Symbol.<=>(:a, :a))              # => 0
```

# Regexp

A Regexp is a regular expression. The literal `/\d+/` takes Ruby's flags: `i` (ignore case), `m` (`.` also matches a newline), `x` (ignore whitespace and comments), `n` (match bytes; `/a/n` has the encoding US-ASCII). `/a#{x}/` embeds a String. `Regexp.new(s, flags)` builds one from a String. The syntax is Ruby's (Onigmo) as it is, named groups `(?<name>...)` included ([Values and types](../03-values.md)).

The main difference from Ruby is that a match leaves nothing behind globally: `$~`, `$1` and `Regexp.last_match` do not exist, and `/(?<y>\d+)/ =~ s` does not create a local variable `y` (a static error). The result of a match is the MatchData that `Regexp.match` returns, kept in a variable and read from there ([MatchData](MatchData.md)).

The operators on Regexps are `=~` (the position of a match in a String, or nil), `==` and `!=`. The String forms `s =~ re` and `s !~ re` exist too. `String.match`, `match?`, `scan`, `sub`, `gsub`, `split`, `index` and others also take a Regexp ([Operators and indexing](../05-operators.md)). A Regexp cannot be a Hash key or a Set element (`TypeError`).

## new

`Regexp.new(String, [String])`

Makes a Regexp from a source String. The second argument is a String of flag letters ("" by default): the literal's `i`, `m`, `x` and `n`; any other letter is an `ArgumentError` at run time. A source that is not a valid regular expression is a `RegexpError`, which can be rescued (Ruby's Integer flags such as `Regexp::IGNORECASE` are not taken). To match the special characters of the source literally, pass it through `Regexp.escape`.

```ruby
p(Regexp.new("a.b"))                        # => /a.b/
re = Regexp.new("ab+", "i")
p(re)                                       # => /ab+/i
p(Regexp.match?(re, "ABB"))                 # => true
p(Regexp.options(Regexp.new("a", "mi")))    # => 5
p(Regexp.new("a", "n"))                     # => /a/n
begin
  p(Regexp.new("("))
rescue RegexpError => e
  puts(Exception.message(e))                # => end pattern with unmatched parenthesis: /(/
end
```

```ruby error
p(Regexp.new("a", "q"))        # !> ArgumentError: Regexp.new: unknown regexp option: q
```

```ruby error
p(Regexp.new("("))             # !> RegexpError: Regexp.new: end pattern with unmatched parenthesis: /(/
```

## escape

`Regexp.escape(String)`

The String with the regular-expression special characters (`.`, `*`, `(`, space, ...) escaped with backslashes. `Regexp.new(Regexp.escape(s))` is the Regexp that matches `s` itself.

```ruby
p(Regexp.escape("a.b*c"))                                 # => "a\\.b\\*c"
word = "a.b"
re = Regexp.new(Regexp.escape(word))
p(Regexp.match?(re, "a.b"))                               # => true
p(Regexp.match?(re, "axb"))                               # => false
```

## source

`Regexp.source(x)`

The source of the regular expression (a String), without the flags. Embedded expressions appear expanded.

```ruby
p(Regexp.source(/\d+/))        # => "\\d+"
p(Regexp.source(/a b/x))       # => "a b"
x = "c"
p(Regexp.source(/a#{x}/i))     # => "ac"
```

## options

`Regexp.options(x)`

The flags as bits in an Integer (as Ruby's: `i` is 1, `x` is 2, `m` is 4, `n` is 32).

```ruby
p(Regexp.options(/a/))         # => 0
p(Regexp.options(/a/i))        # => 1
p(Regexp.options(/a/imx))      # => 7
p(Regexp.options(/a/n))        # => 32
```

## casefold?

`Regexp.casefold?(x)`

True when the flag `i` is set.

```ruby
p(Regexp.casefold?(/a/i))      # => true
p(Regexp.casefold?(/a/))       # => false
```

## encoding

`Regexp.encoding(x)`

The name of the Regexp's encoding (a String). An ASCII-only source is "US-ASCII", a source with non-ASCII characters "UTF-8", and `/.../n` "US-ASCII" (it matches bytes).

```ruby
p(Regexp.encoding(/a/))        # => "US-ASCII"
p(Regexp.encoding(/あ/))       # => "UTF-8"
p(Regexp.encoding(/a/n))       # => "US-ASCII"
```

## fixed_encoding?

`Regexp.fixed_encoding?(x)`

True when the encoding is fixed (a source with non-ASCII characters). An ASCII-only source and `/.../n` are false and can be matched against Strings of any encoding. As Ruby's.

```ruby
p(Regexp.fixed_encoding?(/a/))     # => false
p(Regexp.fixed_encoding?(/あ/))    # => true
```

## timeout

`Regexp.timeout(x)`

The match timeout set on this Regexp (seconds, a Float) or **nil**. Sake has no way to set one, so it is always nil, and `--strict` (level 2) reports using it unchecked.

```ruby
p(Regexp.timeout(/a/))         # => nil
```

## names

`Regexp.names(x)`

The Array of the named groups' names (Strings, in order of appearance, each once). Empty when there are no named groups.

```ruby
p(Regexp.names(/(?<y>\d+)-(?<m>\d+)/))    # => ["y", "m"]
p(Regexp.names(/(\d+)/))                  # => []
```

## named_captures

`Regexp.named_captures(x)`

A Hash from each named group's name (a String) to the Array of its group numbers. A name used twice lists two numbers.

```ruby
p(Regexp.named_captures(/(?<y>\d+)-(?<m>\d+)/))   # => {"y" => [1], "m" => [2]}
p(Regexp.named_captures(/(?<a>.)(?<a>.)/))        # => {"a" => [1, 2]}
p(Regexp.named_captures(/(a)/))                   # => {}
```

## match

`Regexp.match(x, String, [Integer])`

The MatchData of the first match in the String. Without a match the result is **nil**, which `--strict` (level 2) reports when used unchecked (check it with `if m` or `or raise`). The third argument is the position to start searching from (in characters; 0 by default, negative counts from the end). `String.match(s, re)` is the same. Unlike Ruby, nothing is left in `$~`.

```ruby
re = /(?<y>\d+)-(?<m>\d+)/
m = Regexp.match(re, "on 2026-10")
if m
  p(m["y"])                               # => "2026"
end
p(Regexp.match(re, "none"))               # => nil
n = Regexp.match(/\d/, "a1b2", 2) or raise("no digit")
p(n[0])                                   # => "2"
```

```ruby error
m = Regexp.match(/a/, "a")
p(m[0])                                   # !> the operands may be nil (MatchData | nil)
```

## match?

`Regexp.match?(x, String, [Integer])`

True when there is a match. It builds no MatchData, so it is the lighter choice when only the fact of a match matters. The third argument is the position to start from.

```ruby
p(Regexp.match?(/\d+/, "a1"))         # => true
p(Regexp.match?(/\d+/, "abc"))        # => false
p(Regexp.match?(/\d/, "1abc", 1))     # => false
```

## =~

`Regexp.=~(x, Any)`

The function form of `re =~ s`. The position of the first match (an Integer, in characters), or **nil** when there is none (reported at `--strict` level 2). The right operand must be a String. `s =~ re` gives the same result, and `s !~ re` is true when there is no match. Unlike Ruby, named groups do not become local variables and `$~` is not set.

```ruby
p(/b/ =~ "abc")                # => 1
p(/z/ =~ "abc")                # => nil
p("abc" =~ /c/)                # => 2
p("abc" !~ /z/)                # => true
p(Regexp.=~(/b/, "abc"))       # => 1
```

```ruby error
if /(?<y>\d+)/ =~ "12"
  p(y)                         # !> named captures do not create local variables in Sake
end
```

## ==, !=

`Regexp.==(x, Any)`

`Regexp.!=(x, Any)`

True when the two Regexps have the same source and flags (`!=` is the negation). A right operand that is not a Regexp is never equal.

```ruby
p(/a/ == /a/)                  # => true
p(/a/ == /a/i)                 # => false
p(/a/ != /b/)                  # => true
p(/a/ == "a")                  # => false
```

## union

`Regexp.union(*String|Regexp|Array)`

A Regexp that matches any of the arguments: Strings are escaped, Regexps keep their flags, and all are joined with `|`. With no arguments it is `/(?!)/`, which matches nothing. The patterns are listed one by one, or, as Ruby allows, given as **one** Array of Strings and Regexps (`Regexp.union(Any["a", "b"])`); an Array among other arguments, or an element that is neither a String nor a Regexp, is a `TypeError` at run time.

```ruby
p(Regexp.union("a.b", /c/i))                              # => /a\.b|(?i-mx:c)/
p(Regexp.match?(Regexp.union("cat", "dog"), "hotdog"))    # => true
p(Regexp.union())                                         # => /(?!)/
p(Regexp.union(Any["a", "b"]))                          # => /a|b/
p(Regexp.union(Any["a.b", /c/]))                        # => /a\.b|(?-mix:c)/
```

```ruby error
p(Regexp.union("a", Any["b"]))    # !> TypeError: Regexp.union: no implicit conversion of Array into String
```

# MatchData

A MatchData is the result of a regular-expression match. `Regexp.match(re, s)` and `String.match(s, re)` return one, or nil when there is no match, so it is checked before use ([Regexp](Regexp.md)). Ruby's globals `$~` and `$1` do not exist; the MatchData is kept in a variable and indexed ([Values and types](../03-values.md)).

Groups are numbered as in Ruby: 0 is the whole match, and the `(...)` groups count from 1. A named group `(?<name>...)` can also be read by its name (a String). As in Ruby, once there is a named group, plain `(...)` groups do not count as groups. A group that did not take part in the match (an `(a)?` that matched nothing) is nil.

The only operator on a MatchData is the index `m[k]`; `m[k] = v` is not available ([Operators and indexing](../05-operators.md)). A MatchData itself cannot be a Hash key.

## []

`MatchData.[](x, Any)`

The function form of `m[k]`. With an Integer `k`, the String of that group (0 is the whole match; a negative value counts from the end); with a String or Symbol, the String of the group of that name. A group that did not take part and an **index out of range** give nil, whereas an **unknown name** is an `IndexError` at run time. The nil is that of `x[k]` (an index miss), so `--strict` level 2 does not report using it unchecked; level 3 (`index-nil`) does. An index that is neither an Integer nor a String is a `TypeError`.

```ruby
m = Regexp.match(/(?<y>\d+)-(?<m>\d+)?/, "ab 12- cd") or raise("no match")
p(m[0])                       # => "12-"
p(m[1])                       # => "12"
p(m[2])                       # => nil
p(m[9])                       # => nil
p(m["y"])                     # => "12"
p(m[:y])                      # => "12"
p(MatchData.[](m, -1))        # => nil
```

```ruby error
m = Regexp.match(/(?<y>\d+)/, "12") or raise("no match")
p(m["z"])                     # !> IndexError: MatchData.[]: undefined group name reference: z
```

## captures

`MatchData.captures(x)`

The Array of the Strings of groups 1 onwards (the whole match is not included). A group that did not take part is nil, so the element type is String or nil.

```ruby
m = Regexp.match(/(\d+)-(\d+)?/, "12-") or raise("no match")
p(MatchData.captures(m))      # => ["12", nil]
```

## named_captures

`MatchData.named_captures(x)`

A Hash from each named group's name (a String) to the String it matched (nil when it did not take part). Empty when there are no named groups.

```ruby
m = Regexp.match(/(?<y>\d+)-(?<m>\d+)?/, "12-") or raise("no match")
p(MatchData.named_captures(m))    # => {"y" => "12", "m" => nil}
```

## names

`MatchData.names(x)`

The Array of the named groups' names (as `Regexp.names`).

```ruby
m = Regexp.match(/(?<y>\d+)-(?<m>\d+)?/, "12-") or raise("no match")
p(MatchData.names(m))         # => ["y", "m"]
```

## to_a

`MatchData.to_a(x)`

The Array of the whole match and every group (`m[0]`, `m[1]`, ...). A group that did not take part is nil.

```ruby
m = Regexp.match(/(\d+)-(\d+)?/, "12-") or raise("no match")
p(MatchData.to_a(m))          # => ["12-", "12", nil]
```

## to_s

`MatchData.to_s(x)`

The String of the whole match (as `m[0]`, but never nil).

```ruby
m = Regexp.match(/\d+/, "ab 12 cd") or raise("no match")
p(MatchData.to_s(m))          # => "12"
```

## values_at

`MatchData.values_at(x, *Integer)`

The Array of the Strings of the given group numbers (in the manner of `Array.values_at`). A group that did not take part and a number out of range give nil. Only Integers are accepted, not names (a `type` problem statically).

```ruby
m = Regexp.match(/(\d+)-(\d+)?/, "12-") or raise("no match")
p(MatchData.values_at(m, 0, 2, 9))    # => ["12-", nil, nil]
p(MatchData.values_at(m))             # => []
```

## match

`MatchData.match(x, Integer)`

The String of the group with that number (nil when it did not take part: the nil of a miss, `index-nil`, reported only at `--strict` level 3). It differs from `m[i]` in that a number out of range is an `IndexError` at run time rather than nil.

```ruby
m = Regexp.match(/(\d+)-(\d+)?/, "12-") or raise("no match")
p(MatchData.match(m, 1))      # => "12"
p(MatchData.match(m, 2))      # => nil
```

```ruby error
m = Regexp.match(/(a)/, "a") or raise("no match")
p(MatchData.match(m, 5))      # !> IndexError: MatchData.match: index 5 out of matches
```

## match_length

`MatchData.match_length(x, Integer)`

The number of characters the group with that number matched (an Integer). Nil when it did not take part (the nil of a miss, `index-nil`, level 3); a number out of range is an `IndexError`.

```ruby
m = Regexp.match(/(\d+)-(\d+)?/, "12-") or raise("no match")
p(MatchData.match_length(m, 1))   # => 2
p(MatchData.match_length(m, 2))   # => nil
```

## pre_match, post_match

`MatchData.pre_match(x)`

`MatchData.post_match(x)`

The String before the match and the String after it (Ruby's `` $` `` and `$'`).

```ruby
m = Regexp.match(/\d+/, "ab 12 cd") or raise("no match")
p(MatchData.pre_match(m))     # => "ab "
p(MatchData.post_match(m))    # => " cd"
```

## begin, end

`MatchData.begin(x, Integer)`

`MatchData.end(x, Integer)`

The position where the group with that number starts and where it ends (Integers in characters; `end` is one past the last character). For a group that did not take part they are **nil**; the result is `Integer | nil`, and the nil is that of a miss (`index-nil`), reported only at `--strict` level 3 (so are the nils of `bytebegin`, `byteend`, `offset`, `byteoffset`, `match` and `match_length`). A number out of range is an `IndexError` (`index 5 out of matches`), which can be rescued.

```ruby
m = Regexp.match(/(\d+)-(\d+)?/, "ab 12-") or raise("no match")
p(MatchData.begin(m, 0))      # => 3
p(MatchData.end(m, 0))        # => 6
p(MatchData.begin(m, 1))      # => 3
p(MatchData.end(m, 1))        # => 5
p(MatchData.begin(m, 2))      # => nil
b = MatchData.begin(m, 2)
if b
  p(b)
else
  puts("group 2 did not take part")      # => group 2 did not take part
end
```

```ruby error
m = Regexp.match(/(a)/, "a") or raise("no match")
p(MatchData.begin(m, 5))      # !> IndexError: MatchData.begin: index 5 out of matches
```

## offset

`MatchData.offset(x, Integer)`

The Tuple `[begin, end]` of the group with that number. A group that did not take part gives `[nil, nil]`; a number out of range is an `IndexError`.

```ruby
m = Regexp.match(/(\d+)-(\d+)?/, "ab 12-") or raise("no match")
p(MatchData.offset(m, 1))     # => [3, 5]
p(MatchData.offset(m, 2))     # => [nil, nil]
```

```ruby error
m = Regexp.match(/(a)/, "a") or raise("no match")
p(MatchData.offset(m, 5))     # !> IndexError: MatchData.offset: index 5 out of matches
```

## bytebegin, byteend

`MatchData.bytebegin(x, Integer)`

`MatchData.byteend(x, Integer)`

The byte versions of `begin` and `end` (Integers). They differ from the character positions in a String with multibyte characters. A group that did not take part gives nil (the nil of a miss, `index-nil`, level 3); a number out of range is an `IndexError`.

```ruby
m = Regexp.match(/(い)/, "あいう") or raise("no match")
p(MatchData.begin(m, 1))      # => 1
p(MatchData.bytebegin(m, 1))  # => 3
p(MatchData.byteend(m, 1))    # => 6
```

## byteoffset

`MatchData.byteoffset(x, Integer)`

The byte version of `offset`: the Tuple `[bytebegin, byteend]`. A group that did not take part gives `[nil, nil]`.

```ruby
m = Regexp.match(/(い)/, "あいう") or raise("no match")
p(MatchData.byteoffset(m, 1))     # => [3, 6]
p(MatchData.offset(m, 1))         # => [1, 2]
```

## length, size

`MatchData.length(x)`

`MatchData.size(x)`

The number of groups plus one (the whole match included; the length of `to_a`).

```ruby
m = Regexp.match(/(\d+)-(\d+)?/, "12-") or raise("no match")
p(MatchData.size(m))          # => 3
p(MatchData.length(m))        # => 3
```

## regexp

`MatchData.regexp(x)`

The Regexp that made this match.

```ruby
m = Regexp.match(/\d+/i, "12") or raise("no match")
p(MatchData.regexp(m))        # => /\d+/i
```

## string

`MatchData.string(x)`

The whole String the match was tried on. It is frozen: an in-place change such as `String.upcase!` is a `TypeError`.

```ruby
m = Regexp.match(/\d+/, "ab 12 cd") or raise("no match")
p(MatchData.string(m))        # => "ab 12 cd"
```

# Array

An Array is a sequence whose length varies. `Any[1, 2, 3]` makes an Array. Ruby's `[1, 2, 3]` is an Array, but Sake's literal `[1, 2, 3]` is a Tuple of fixed length, and passing one to `Array.push` and the like is rejected statically ([Values and types](../03-values.md)). A typed Array such as `Integer[1, 2]`, `String[]` or `Point[p]` is an Array with a declared element type: every write (`Array.push`, `append`, `unshift`, `prepend`, `insert`, `concat`, `map!`, `fill`, `a[i] = v`, ...) is checked, and a value of another type is a `type` problem statically and a `TypeError` at run time. Every operation in this chapter works on typed Arrays too. The new Arrays returned by `map`, `select`, `sort`, `first(a, n)`, `+` and so on carry no element type declaration; only `dup` keeps it.

The checker gives one Array (one construction site) a single element type for the whole program. After an in-place rewrite such as `Array.map!` puts another type in, the Array is taken to hold both types from then on, and operations that fit only one of them become `type` (partial) problems ([Values and types](../03-values.md)).

Two kinds of operation use nil for "no element". The nil of a miss (`x[k]`, `first`, `last`, `pop`, `shift`, `min`, `max`, `minmax`, `min_by`, `max_by`, `minmax_by`, `at`, `slice`, `slice!`, `dig`, `sample`, `delete_at`: an empty Array or an index outside it), used unchecked, is an `index-nil` problem at `--strict=3` only; `--strict` (level 2) lets it pass. Every other nil (`find`, `index`, `find_index`, `rindex`, `bsearch`, `delete`, `uniq!`, ...) is a level 2 `nil` problem ([Overview](../01-overview.md)).

The operators on Arrays are `+`, `-`, `*`, `==`, `!=`, `<`, `<=`, `>`, `>=`, `<=>` (two Arrays compare in dictionary order) and the index `a[i]`, `a[i, n]`, `a[range]`, `a[i] = v`. The entries `Array.+(x, y)` and so on are the function forms of those operators ([Operators and indexing](../05-operators.md)). Operations that compare elements (`sort`, `min`, `max`, `<`, ...) need elements of comparable types: Integers and Floats compare with each other, Tuples element by element, class instances with their type's `<=>` (`include Comparable`).

Operations with a block are written as in Ruby (`Array.map(xs) { |x| x * 2 }`). There is no Enumerator (no blockless `each`). The operations whose block is optional (`each_with_index`, `each_slice`, `each_cons`) return an Array when called without one.

## Array[]

`Array[*Any]`

Makes an Array of Arrays, as `T[...]` makes an Array of T for every T: each element must be an Array, checked on every write (a literal of another type is a static error, any other value a `type` problem and a `TypeError` at run time). An Array of any values is `Any[...]` ([Kernel](Kernel.md)).

```ruby
rows = Array[Any[1, 2], Any[3]]
Array.push(rows, Any[4, 5])
p(rows)                        # => [[1, 2], [3], [4, 5]]
```

```ruby error
p(Array[1])                    # !> Array[]: element 1 must be Array, got Integer
```

## new

`Array.new(Integer, [Any]) [{ }]`

Makes a new Array of length `n`. `Array.new(n)` holds n nils, `Array.new(n, v)` the same value `v` n times (one shared value: the two elements of `Array.new(2, Any[])` are the same Array), and `Array.new(n) { |i| ... }` the block's value for each index i. The element type is nil, the type of `v`, or the block's result type. A negative `n` is an `ArgumentError`.

```ruby
p(Array.new(3))                        # => [nil, nil, nil]
p(Array.new(2, "a"))                   # => ["a", "a"]
p(Array.new(3) { |i| i * i })          # => [0, 1, 4]
rows = Array.new(2, Any[])
Array.push(Array.fetch(rows, 0), 1)
p(rows)                                # => [[1], [1]]
rows2 = Array.new(2) { |i| Any[] }
Array.push(Array.fetch(rows2, 0), 1)
p(rows2)                               # => [[1], []]
```

```ruby error
Array.new(-1)                          # !> ArgumentError: Array.new: negative size -1
```

## dup

`Array.dup(x)`

A new Array with the same elements (a shallow copy: the elements are shared). The copy of a typed Array is a typed Array with the same element type; unlike every other new Array in this chapter, this one keeps the declaration.

```ruby
xs = Integer[1, 2]
ys = Array.dup(xs)
Array.push(ys, 3)
p(xs)                          # => [1, 2]
p(ys)                          # => [1, 2, 3]
```

```ruby error
ys = Array.dup(Integer[1])
Array.push(ys, "s")            # !> Array.push: an element must be Integer, but is String
```

## to_a

`Array.to_a(x)`

The subject itself (the same Array), not a copy. It exists for symmetry with `Tuple.to_a`, `Set.to_a`, `Hash.to_a` and the other operations that give an Array of another type.

```ruby
xs = Any[1, 2]
p(Array.to_a(xs))                          # => [1, 2]
p(Kernel.equal?(Array.to_a(xs), xs))       # => true
```

## length, size

`Array.length(x)`

`Array.size(x)`

The number of elements (an Integer).

```ruby
p(Array.size(Any[3, 1, 2]))      # => 3
p(Array.length(Any[]))           # => 0
```

## empty?

`Array.empty?(x)`

True when there are no elements.

```ruby
p(Array.empty?(Any[]))           # => true
p(Array.empty?(Any[1]))          # => false
```

## []

`Array.[](x, Any, [Integer])`

The function form of `a[i]`, `a[i, n]` and `a[range]`. `a[i]` is the element at position `i` (an Integer; a negative one counts from the end), or nil outside the Array. That nil is the nil of a miss: using it unchecked is an `index-nil` problem at `--strict=3` (level 2 does not report it). `a[i, n]` gives n elements from position i and `a[range]` the elements of the range, as a new Array, or nil when the start is past the end. Use `Array.fetch` for an exception instead. An index that is neither an Integer nor a Range is a `TypeError`.

```ruby
xs = Any[3, 1, 2]
p(xs[1])                       # => 1
p(xs[-1])                      # => 2
p(xs[9])                       # => nil
p(xs[1, 2])                    # => [1, 2]
p(xs[0..1])                    # => [3, 1]
p(xs[5, 1])                    # => nil
p(Array.[](xs, 0))             # => 3
p(Array.[](xs, 0, 2))          # => [3, 1]
```

## []=

`Array.[]=(x, Any, Any)`

The function form of `a[i] = v`. Replaces the element at position `i` with `v` and returns `v`. Writing past the end fills the gap with nil, as in Ruby. In a typed Array the type of `v` is checked (`type` statically, `TypeError` at run time), and since nil cannot be an element, writing past the end is an `IndexError`. A negative index before the start is an `IndexError`.

```ruby
xs = Any[1]
xs[3] = 2
p(xs)                          # => [1, nil, nil, 2]
xs[-1] = 5
p(xs)                          # => [1, nil, nil, 5]
p(Array.[]=(xs, 0, 0))         # => 0
```

```ruby error
is = Integer[1]
is[3] = 2                      # !> IndexError: Array.[]=: index 3 is past the end of Integer[] (length 1); the gap would be nil
```

## at

`Array.at(x, Integer)`

The element at position `i` (the same as `a[i]`; a negative one counts from the end). Outside the Array it is nil, the nil of a miss, reported at level 3 only.

```ruby
xs = Any[3, 1, 2]
p(Array.at(xs, 0))             # => 3
p(Array.at(xs, -1))            # => 2
p(Array.at(xs, 9))             # => nil
```

## fetch

`Array.fetch(x, Integer, [Any])`

The element at position `i`. Outside the Array it returns `default` when given and raises `IndexError` otherwise. The result is never nil (unless the default is), so it is the way to avoid the nil check of an index under `--strict`. The result type is the element type (joined with the default's type when given).

```ruby
xs = Any[3, 1, 2]
p(Array.fetch(xs, 1))          # => 1
p(Array.fetch(xs, -1))         # => 2
p(Array.fetch(xs, 9, 0))       # => 0
```

```ruby error
Array.fetch(Any[3, 1, 2], 9)     # !> IndexError: Array.fetch: index 9 outside of array bounds: -3...3
```

## first, last

`Array.first(x, [Integer])`

`Array.last(x, [Integer])`

`first(a)` is the first element and `last(a)` the last; on an empty Array nil (the nil of a miss, reported at level 3 only). `first(a, n)` and `last(a, n)` give the first or last n elements as a new Array (fewer when there are not enough; never nil). A negative n is an `ArgumentError`.

```ruby
xs = Any[3, 1, 2]
p(Array.first(xs))             # => 3
p(Array.last(xs))              # => 2
p(Array.first(xs, 2))          # => [3, 1]
p(Array.last(xs, 5))           # => [3, 1, 2]
p(Array.first(Any[]))        # => nil
p(Array.last(Any[], 2))      # => []
```

```ruby error
Array.first(Any[1], -1)      # !> ArgumentError: Array.first: negative size -1
```

## slice

`Array.slice(x, Integer|Range, [Integer])`

The same results as `a[i]`, `a[i, n]` and `a[range]`: `slice(a, i)` is an element or nil, `slice(a, i, n)` and `slice(a, range)` a new Array or nil (when the start is past the end). Like `a[i]`'s, this nil is the nil of a miss: used unchecked it is an `index-nil` problem at `--strict=3` only (level 2 does not report it). For an exception instead, use `Array.fetch`.

```ruby
xs = Any[3, 1, 2]
p(Array.slice(xs, 1))          # => 1
p(Array.slice(xs, 1, 5))       # => [1, 2]
p(Array.slice(xs, 1..))        # => [1, 2]
p(Array.slice(xs, 9))          # => nil
y = Array.slice(xs, 0)
p(y + 1) if y                  # => 4
```

## dig

`Array.dig(x, Integer, *Any)`

The element at position `i` or nil (the same as `a[i]`). With two or more keys it digs into nested values as Ruby's `dig` does: each further key is applied to the value the previous one gave (an Integer position for an Array, the key for a Hash), and a nil on the way stays nil. The values on the way must be Arrays, Tuples or Hashes; another type (Integer, String, Record, ...) is a `type` problem statically and, run without the check, a `TypeError` (`Integer cannot be dug into (not an Array, a Tuple, or a Hash)`). A non-Integer key applied to an Array or a Tuple is a `TypeError`. The nil is the nil of a miss, reported at level 3 only.

```ruby
xs = Any[Any[1, 2], Any[3]]
p(Array.dig(xs, 0))            # => [1, 2]
p(Array.dig(xs, 5))            # => nil
p(Array.dig(xs, 0, 1))         # => 2
p(Array.dig(xs, 1, 5))         # => nil
p(Array.dig(xs, 5, 0))         # => nil
p(Array.dig(Any[Hash.of(a: 1)], 0, :a))     # => 1
p(Array.dig(Any[[1, "x"]], 0, 1))        # => "x"
p(Array.dig(Any[[1, "x"]], 0, 5))        # => nil
```

```ruby error
p(Array.dig(Any[Any[1, 2]], 0, 1, 2))  # !> Array.dig: the value must be Array|Hash|Tuple, but is Integer
```

```ruby error
p(Array.dig(Any[Any[1]], 0, "k"))      # !> TypeError: Array.dig: an index into Array must be Integer, got String
```

## values_at

`Array.values_at(x, *Integer)`

A new Array of the elements at the given positions. A position outside the Array gives nil (the element type is joined with nil).

```ruby
xs = Any[3, 1, 2]
p(Array.values_at(xs, 0, 2, 7))    # => [3, 2, nil]
```

## fetch_values

`Array.fetch_values(x, *Integer)`

A new Array of the elements at the given positions. A position outside the Array is an `IndexError` (never nil).

```ruby
p(Array.fetch_values(Any[3, 1, 2], 0, 2))    # => [3, 2]
```

```ruby error
Array.fetch_values(Any[3, 1, 2], 0, 9)       # !> IndexError: Array.fetch_values: index 9 outside of array bounds: -3...3
```

## push, append

`Array.push(x, *Any)`

`Array.append(x, *Any)`

Add the values (any number) at the end and return the subject. In place. A typed Array checks each value's type (`type` statically, `TypeError` at run time). The checker adds the added values' types to the Array's element type. There is no Ruby `<<`.

```ruby
xs = Any[1]
p(Array.push(xs, 2, 3))        # => [1, 2, 3]
p(Array.append(xs, 4))         # => [1, 2, 3, 4]
```

```ruby error
Array.push(Integer[1, 2], "x")     # !> Array.push: an element must be Integer, but is String
```

## unshift, prepend

`Array.unshift(x, *Any)`

`Array.prepend(x, *Any)`

Add the values (any number, in the order given) at the front and return the subject. In place. A typed Array checks the types.

```ruby
xs = Any[3]
p(Array.unshift(xs, 1, 2))     # => [1, 2, 3]
p(Array.prepend(xs, 0))        # => [0, 1, 2, 3]
```

## insert

`Array.insert(x, Integer, *Any)`

Inserts the values (any number) before position `i` and returns the subject. An `i` equal to the length appends. A negative `i` counts from the end (`-1` appends, `-size - 1` prepends). When `i` is beyond the length Ruby fills the gap with nil; Sake raises `IndexError` instead (in an untyped Array too: it does not create nils silently). A negative `i` below `-size - 1` (before the start) is also an `IndexError`. A typed Array checks the inserted values' types (`type` statically, `TypeError` at run time).

```ruby
xs = Any[1, 2, 3]
p(Array.insert(xs, 1, 9, 9))   # => [1, 9, 9, 2, 3]
p(Array.insert(xs, -2, 7))     # => [1, 9, 9, 2, 7, 3]
p(Array.insert(xs, 6, 8))      # => [1, 9, 9, 2, 7, 3, 8]
```

```ruby error
Array.insert(Any[1], 3, 2)   # !> IndexError: Array.insert: index 3 is past the end of the Array (length 1); the gap would be nil
```

```ruby error
Array.insert(Any[1, 2], -4, 0)   # !> IndexError: Array.insert: index -4 is before the start of the Array (length 2)
```

```ruby error
Array.insert(Integer[1], 0, "a")   # !> Array.insert: an element must be Integer, but is String
```

## concat

`Array.concat(x, Array)`

Appends all elements of the other Array and returns the subject. In place (`+` makes a new Array). A typed Array checks the elements' types.

```ruby
xs = Any[1]
p(Array.concat(xs, Any[2, 3]))   # => [1, 2, 3]
```

```ruby error
Array.concat(Integer[1], Any["a"])   # !> Array.concat: an element must be Integer, but is String
```

## fill

`Array.fill(x, Any)`

Replaces every element with `v` and returns the subject (the length does not change). Ruby's range and block forms do not exist. A typed Array checks the type of `v` (`type` statically; `TypeError` when run without the check).

```ruby
xs = Any[1, 2, 3]
p(Array.fill(xs, 0))           # => [0, 0, 0]
```

```ruby error
Array.fill(Integer[1], "a")    # !> Array.fill: an element must be Integer, but is String
```

## replace

`Array.replace(x, Array)`

Replaces the subject's contents with the other Array's elements and returns the subject (unlike `a = other`, every place that shares the Array sees it). A typed Array checks the elements' types (`type` statically, `TypeError` at run time).

```ruby
xs = Any[1, 2, 3]
ys = xs
p(Array.replace(xs, Any[9]))     # => [9]
p(ys)                              # => [9]
```

```ruby error
Array.replace(Integer[1], Any["a"])  # !> Array.replace: an element must be Integer, but is String
```

## pop

`Array.pop(x)`

Removes and returns the last element. On an empty Array nil (the nil of a miss, reported at level 3 only). There is no Ruby `pop(n)`.

```ruby
xs = Any[1, 2]
p(Array.pop(xs))               # => 2
p(xs)                          # => [1]
p(Array.pop(Any[]))          # => nil
```

## shift

`Array.shift(x, [Integer])`

`shift(a)` removes and returns the first element, nil on an empty Array (the nil of a miss, reported at level 3 only). `shift(a, n)` removes the first n elements and returns them as a new Array (fewer when there are not enough; never nil). A negative n is an `ArgumentError`.

```ruby
xs = Any[1, 2, 3, 4]
p(Array.shift(xs))             # => 1
p(Array.shift(xs, 2))          # => [2, 3]
p(xs)                          # => [4]
p(Array.shift(Any[]))        # => nil
p(Array.shift(Any[], 2))     # => []
```

## delete

`Array.delete(x, Any)`

Removes every element `==` to `v` and returns `v`. When there was none, nil (a level 2 `nil` problem).

```ruby
xs = Any[1, 2, 1, 3]
p(Array.delete(xs, 1))         # => 1
p(xs)                          # => [2, 3]
p(Array.delete(xs, 42))        # => nil
```

## delete_at

`Array.delete_at(x, Integer)`

Removes and returns the element at position `i`. Outside the Array nil (the nil of a miss, reported at level 3 only).

```ruby
xs = Any[1, 2, 3]
p(Array.delete_at(xs, 1))      # => 2
p(xs)                          # => [1, 3]
p(Array.delete_at(xs, 99))     # => nil
```

## slice!

`Array.slice!(x, Integer|Range, [Integer])`

Returns what `slice` returns and removes it from the subject: `slice!(a, i)` an element or nil, `slice!(a, i, n)` and `slice!(a, range)` a new Array or nil. The nil is the nil of a miss, reported at level 3 only.

```ruby
xs = Any[1, 2, 3, 4]
p(Array.slice!(xs, 1, 2))      # => [2, 3]
p(xs)                          # => [1, 4]
p(Array.slice!(xs, 0))         # => 1
p(Array.slice!(xs, 5))         # => nil
p(Array.slice!(xs, 0..0))      # => [4]
p(xs)                          # => []
```

## clear

`Array.clear(x)`

Removes every element and returns the subject (now empty).

```ruby
xs = Any[1, 2]
p(Array.clear(xs))             # => []
p(xs)                          # => []
```

## each, each_entry

`Array.each(x) { }`

`Array.each_entry(x) { }`

Passes each element in order to the block and returns the subject. The block is required (there is no form returning Ruby's Enumerator). `break v` ends it at once with `v` as the result. `each_entry` is the same as `each`.

```ruby
xs = Any[1, 2, 3]
r = Array.each(xs) { |x| puts(x) }
# => 1
# => 2
# => 3
p(r)                                   # => [1, 2, 3]
p(Array.each(xs) { |x| break x if x == 2 })    # => 2
```

## each_with_index

`Array.each_with_index(x) [{ }]`

Passes each element and its index (from 0) to the block and returns the subject. Without a block it returns, instead of Ruby's Enumerator, a new Array of `[element, index]` Tuples.

```ruby
xs = Any["a", "b"]
Array.each_with_index(xs) { |s, i| puts("#{i}:#{s}") }
# => 0:a
# => 1:b
p(Array.each_with_index(xs))           # => [["a", 0], ["b", 1]]
```

## each_index

`Array.each_index(x) { }`

Passes the indexes 0 to length-1 in order to the block and returns the subject.

```ruby
xs = Any["a", "b"]
Array.each_index(xs) { |i| puts(i) }
# => 0
# => 1
```

## reverse_each

`Array.reverse_each(x) { }`

Passes each element to the block from the last to the first and returns the subject.

```ruby
Array.reverse_each(Any[1, 2, 3]) { |x| puts(x) }
# => 3
# => 2
# => 1
```

## each_slice, each_cons

`Array.each_slice(x, Integer) [{ }]`

`Array.each_cons(x, Integer) [{ }]`

`each_slice(a, n)` passes the elements cut into Arrays of n, and `each_cons(a, n)` each window of n consecutive elements, to the block, and returns the subject. Without a block they return, instead of Ruby's Enumerator, a new Array of those Arrays (`each_cons` gives `[]` when n exceeds the length). n must be at least 1; 0 and a negative n are an `ArgumentError`.

```ruby
xs = Any[1, 2, 3]
p(Array.each_slice(xs, 2))             # => [[1, 2], [3]]
p(Array.each_cons(xs, 2))              # => [[1, 2], [2, 3]]
Array.each_slice(xs, 2) { |s| p(s) }
# => [1, 2]
# => [3]
p(Array.each_cons(xs, 2) { |s| s })    # => [1, 2, 3]
p(Array.each_cons(xs, 5))              # => []
```

```ruby error
Array.each_slice(Any[1], 0)          # !> ArgumentError: Array.each_slice: invalid slice size
```

## each_with_object

`Array.each_with_object(x, Any) { }`

Passes each element and `memo` (in that order) to the block and finally returns `memo`, whose type is the result type. Unlike `reduce`, the block's value is not used: the result accumulates by changing `memo` in place.

```ruby
xs = Any[1, 2, 3]
p(Array.each_with_object(xs, Any[]) { |x, acc| Array.push(acc, x * 2) })   # => [2, 4, 6]
```

## cycle

`Array.cycle(x, Integer) { }`

Passes all elements to the block n times over and returns nil. Ruby's endless form (no argument) does not exist: n is required. For n of 0 or less nothing happens.

```ruby
p(Array.cycle(Any[1, 2], 2) { |x| puts(x) })
# => 1
# => 2
# => 1
# => 2
# => nil
```

## map, collect

`Array.map(x) { }`

`Array.collect(x) { }`

A new Array of the block's results for each element. Its element type is the block's result type, with no declaration (anything can be added, even when made from a typed Array).

```ruby
xs = Any[1, 2, 3]
p(Array.map(xs) { |x| x * 2 })             # => [2, 4, 6]
p(Array.collect(xs) { |x| Integer.to_s(x) })   # => ["1", "2", "3"]
p(xs)                                      # => [1, 2, 3]
```

## map!, collect!

`Array.map!(x) { }`

`Array.collect!(x) { }`

Replace each element with the block's value and return the subject. In place. Because the checker gives one Array one element type, putting in a type other than the original makes the Array hold both types from then on, and operations that fit only one of them become `type` problems. Use them for a mapping within one type, and `map` (a new Array) to change the type. A typed Array checks the results' type (`type` statically, `TypeError` at run time).

```ruby
xs = Any[1, 2, 3]
p(Array.map!(xs) { |x| x * 2 })        # => [2, 4, 6]
p(Array.collect!(xs) { |x| x + 1 })    # => [3, 5, 7]
p(xs)                                  # => [3, 5, 7]
```

```ruby error
xs = Any[1, 2, 3]
Array.map!(xs) { |x| Integer.to_s(x) }
p(Array.sum(xs))               # !> Array.sum: an element must be Integer|Float|Rational|Complex, but can be String
```

## flat_map

`Array.flat_map(x) { }`

A new Array that joins the sequences the block returns for each element. The block must return an Array or a Tuple; any other value is a `TypeError` at run time. Unlike Ruby's, it does not keep a non-Array value as it is.

```ruby
p(Array.flat_map(Any[1, 2]) { |x| Any[x, x * 10] })   # => [1, 10, 2, 20]
p(Array.flat_map(Any[1, 2]) { |x| [x, x] })             # => [1, 1, 2, 2]
```

```ruby error
Array.flat_map(Any[1]) { |x| x }    # !> TypeError: Array.flat_map: the block must return an Array or a Tuple, got Integer
```

## filter_map

`Array.filter_map(x) { }`

A new Array of the block's results for each element, without the nils and falses (`map` and `compact` in one; nil is removed from the element type).

```ruby
xs = Any[1, 2, 3, 4]
p(Array.filter_map(xs) { |x| x * 10 if x > 2 })    # => [30, 40]
p(Array.filter_map(xs) { |x| x > 2 })              # => [true, true]
```

## select, filter, find_all

`Array.select(x) { }`

`Array.filter(x) { }`

`Array.find_all(x) { }`

A new Array of the elements for which the block returns a truthy value (anything but nil and false).

```ruby
xs = Any[1, 2, 3, 4]
p(Array.select(xs) { |x| x > 2 })      # => [3, 4]
p(Array.filter(xs) { |x| x > 2 })      # => [3, 4]
p(Array.find_all(xs) { |x| x > 2 })    # => [3, 4]
```

## select!, filter!, keep_if

`Array.select!(x) { }`

`Array.filter!(x) { }`

`Array.keep_if(x) { }`

Keep only the elements for which the block is truthy (in place). `keep_if` always returns the subject. `select!` and `filter!` return nil when nothing was removed (as Ruby's), and using that result unchecked is a level 2 `nil` problem.

```ruby
xs = Any[1, 2, 3]
p(Array.select!(xs) { |x| x > 1 })     # => [2, 3]
p(Array.select!(xs) { |x| x > 1 })     # => nil
p(Array.keep_if(xs) { |x| x > 1 })     # => [2, 3]
p(Array.keep_if(xs) { |x| x > 2 })     # => [3]
p(xs)                                  # => [3]
```

## reject

`Array.reject(x) { }`

A new Array without the elements for which the block is truthy.

```ruby
p(Array.reject(Any[1, 2, 3, 4]) { |x| x > 2 })   # => [1, 2]
```

## reject!, delete_if

`Array.reject!(x) { }`

`Array.delete_if(x) { }`

Remove the elements for which the block is truthy (in place). `delete_if` always returns the subject. `reject!` returns nil when nothing was removed (as Ruby's), and using that result unchecked is a level 2 `nil` problem.

```ruby
xs = Any[1, 2, 3]
p(Array.reject!(xs) { |x| x > 2 })     # => [1, 2]
p(Array.reject!(xs) { |x| x > 2 })     # => nil
p(Array.delete_if(xs) { |x| x > 5 })   # => [1, 2]
p(Array.delete_if(xs) { |x| x > 1 })   # => [1]
```

## partition

`Array.partition(x) { }`

The two-element Tuple `[selected, rest]`: an Array of the elements for which the block is truthy, and an Array of the others. Take it apart with multiple assignment.

```ruby
evens, odds = Array.partition(Any[1, 2, 3, 4]) { |x| Integer.even?(x) }
p(evens)                       # => [2, 4]
p(odds)                        # => [1, 3]
```

## group_by

`Array.group_by(x) { }`

A Hash from each block value to the Array of the elements that gave it (in order of first appearance). The keys must be values a Hash key can be (a Regexp and the like are a `TypeError`; [Values and types](../03-values.md)).

```ruby
p(Array.group_by(Any[1, 2, 3, 4]) { |x| x % 2 })    # => {1 => [1, 3], 0 => [2, 4]}
```

## chunk_while, slice_when

`Array.chunk_while(x) { }`

`Array.slice_when(x) { }`

Cut the sequence by passing each pair of neighbours `(a, b)` to the block, and return a new Array of the pieces (Arrays). `chunk_while` keeps neighbours together while the block is truthy; `slice_when` cuts where it is.

```ruby
xs = Any[1, 2, 4, 5, 7]
p(Array.chunk_while(xs) { |a, b| b == a + 1 })    # => [[1, 2], [4, 5], [7]]
p(Array.slice_when(xs) { |a, b| b != a + 1 })     # => [[1, 2], [4, 5], [7]]
```

## take, drop

`Array.take(x, Integer)`

`Array.drop(x, Integer)`

`take(a, n)` is a new Array of the first n elements, `drop(a, n)` of the rest after the first n (fewer when there are not enough). A negative n is an `ArgumentError`.

```ruby
xs = Any[1, 2, 3, 4]
p(Array.take(xs, 2))           # => [1, 2]
p(Array.drop(xs, 2))           # => [3, 4]
p(Array.take(xs, 9))           # => [1, 2, 3, 4]
```

## take_while, drop_while

`Array.take_while(x) { }`

`Array.drop_while(x) { }`

A new Array of the leading elements for which the block is truthy (`take_while`), or of the rest after dropping them (`drop_while`).

```ruby
xs = Any[1, 2, 3, 4]
p(Array.take_while(xs) { |x| x < 3 })  # => [1, 2]
p(Array.drop_while(xs) { |x| x < 3 })  # => [3, 4]
```

## include?

`Array.include?(x, Any)`

True when some element is `==` to `v`. class instances compare with their type's equality ([Operators and indexing](../05-operators.md)).

```ruby
p(Array.include?(Any[1, 2], 2))          # => true
p(Array.include?(Any[1, 2], nil))        # => false
p(Array.include?(Any[[1, 2]], [1, 2]))   # => true
```

## index, rindex

`Array.index(x, Any)`

`Array.rindex(x, Any)`

The position (an Integer) of the first (`index`) or last (`rindex`) element `==` to `v`, or nil when there is none: a level 2 `nil` problem. Ruby's block form does not exist; to search by a condition use `find_index`.

```ruby
xs = Any[3, 1, 4, 1]
p(Array.index(xs, 1))          # => 1
p(Array.rindex(xs, 1))         # => 3
p(Array.index(xs, 9))          # => nil
```

```ruby error
i = Array.index(Any[1, 2], 2)
p(i + 1)                       # !> the operands may be nil
```

## find_index

`Array.find_index(x) { }`

The position (an Integer) of the first element for which the block is truthy, or nil when there is none (a level 2 `nil` problem).

```ruby
p(Array.find_index(Any[3, 1, 4]) { |x| x > 3 })   # => 2
p(Array.find_index(Any[3, 1, 4]) { |x| x > 9 })   # => nil
```

## find, detect

`Array.find(x) { }`

`Array.detect(x) { }`

The first element for which the block is truthy, or nil when there is none: a level 2 `nil` problem (an empty Array gives nil too; there is no telling "no elements" from "not found").

```ruby
xs = Any[3, 1, 4]
p(Array.find(xs) { |x| x > 3 })        # => 4
p(Array.detect(xs) { |x| x > 30 })     # => nil
```

```ruby error
y = Array.find(Any[3, 1, 4]) { |x| x > 1 }
p(y + 1)                       # !> the operands may be nil
```

## rfind

`Array.rfind(x) { }`

The last element for which the block is truthy, or nil when there is none (a level 2 `nil` problem). It maps to Ruby 4.0's `Array#rfind` and is unavailable on an older Ruby.

```ruby
p(Array.rfind(Any[3, 1, 4, 1, 5]) { |x| x < 4 })    # => 1
```

## bsearch, bsearch_index

`Array.bsearch(x) { }`

`Array.bsearch_index(x) { }`

Binary search in a sorted Array, giving the element (`bsearch`) or its position (`bsearch_index`). Ruby's two modes apply: when the block returns true/false, the first element for which it is true (find-minimum); when it returns an Integer (the result of `<=>`), an element for which it is 0 (find-any). Nil when nothing is found (a level 2 `nil` problem). On an unsorted Array the result is undefined.

```ruby
xs = Any[1, 3, 5, 7]
p(Array.bsearch(xs) { |x| x >= 4 })        # => 5
p(Array.bsearch_index(xs) { |x| x >= 4 })  # => 2
p(Array.bsearch(xs) { |x| 3 <=> x })       # => 3
p(Array.bsearch(xs) { |x| x >= 40 })       # => nil
```

## assoc, rassoc

`Array.assoc(x, Any)`

`Array.rassoc(x, Any)`

In an Array of pairs (Tuples or Arrays), the first element whose first item equals `k` (`assoc`) or whose second item equals `v` (`rassoc`). Nil when there is none (a level 2 `nil` problem). Ruby's `assoc` looks at Array elements only; Sake's pairs are usually Tuples, so Tuples are looked at too.

```ruby
pairs = Any[[1, "a"], [2, "b"]]
p(Array.assoc(pairs, 2))       # => [2, "b"]
p(Array.rassoc(pairs, "a"))    # => [1, "a"]
p(Array.assoc(pairs, 9))       # => nil
```

## count

`Array.count(x, [Any]) [{ }]`

`count(a)` is the number of elements, `count(a, v)` the number of elements `==` to `v`, and `count(a) { |x| ... }` the number of elements for which the block is truthy (an Integer).

```ruby
xs = Any[3, 1, 4, 1]
p(Array.count(xs))                     # => 4
p(Array.count(xs, 1))                  # => 2
p(Array.count(xs) { |x| x > 1 })       # => 2
```

## any?, all?, none?, one?

`Array.any?(x) { }`

`Array.all?(x) { }`

`Array.none?(x) { }`

`Array.one?(x) { }`

True when the block is truthy for at least one element (`any?`), for every element (`all?`), for no element (`none?`), or for exactly one (`one?`). The block is required (Ruby's blockless form and pattern argument do not exist). On an empty Array `all?` and `none?` are true, `any?` and `one?` false.

```ruby
xs = Any[3, 1, 4]
p(Array.any?(xs) { |x| x > 3 })        # => true
p(Array.all?(xs) { |x| x > 0 })        # => true
p(Array.none?(xs) { |x| x > 4 })       # => true
p(Array.one?(xs) { |x| x > 3 })        # => true
p(Array.all?(Any[]) { |x| x > 0 })   # => true
```

```ruby error
Array.any?(Any[1])           # !> Array.any? requires a block
```

## sum

`Array.sum(x, [Any]) [{ }]`

The sum of the elements; with a block, of the block's values. Addition starts from `init` (default 0), and on an empty Array the result is `init` itself. The elements and `init` must be numbers (Integer, Float, Rational, Complex) or values of a class that includes `Arithmetic` and defines `+`; anything else is a `type` problem statically and a `TypeError` at run time (to concatenate Strings use `join`). Write `sum(a, 0.0)` for a Float sum: it is a Float even when the Array is empty.

```ruby
p(Array.sum(Any[1, 2, 3]))                       # => 6
p(Array.sum(Any[]))                              # => 0
p(Array.sum(Any[], 0.0))                         # => 0.0
p(Array.sum(Any[1, 2.5]))                        # => 3.5
p(Array.sum(Any[1, 2], 10))                      # => 13
p(Array.sum(Any["a", "bb"]) { |s| String.size(s) })  # => 3
```

```ruby error
Array.sum(Any["a", "b"])     # !> Array.sum: an element must be Integer|Float|Rational|Complex, but is String
```

## reduce, inject

`Array.reduce(x, Any) { }`

`Array.inject(x, Any) { }`

Starting from `init`, passes the accumulated value and each element (in that order) to the block, takes its value as the next accumulated value, and returns the last. Unlike Ruby's, `init` is required (leaving it out is a static argument-count error). On an empty Array the result is `init`. Ruby's Symbol form (`inject(:+)`) does not exist. The result type is the union of `init`'s type and the block's result type.

```ruby
p(Array.reduce(Any[1, 2, 3], 0) { |acc, x| acc + x })     # => 6
p(Array.inject(Any[1, 2, 3], 1) { |acc, x| acc * x })     # => 6
p(Array.reduce(Any[], 0) { |acc, x| acc + x })            # => 0
p(Array.reduce(Any["a", "b"], "") { |acc, x| acc + x })   # => "ab"
```

```ruby error
Array.reduce(Any[1, 2]) { |acc, x| acc + x }    # !> wrong number of arguments for Array.reduce (given 1, expected 2)
```

## min, max

`Array.min(x)`

`Array.max(x)`

The smallest or largest element. On an empty Array nil (the nil of a miss, reported at level 3 only). The elements must be comparable with each other: a pair of incomparable types in the element type (an Integer and a String, say) is a `type` problem statically (`elements compared in order may be (Integer, String), which cannot be compared`), and an element that may be nil a `nil` problem. A comparison failure the checker cannot see (`Float.NAN`, say) is an `ArgumentError` at run time. Integers and Floats compare with each other. class instances compare with their type's `<=>` (`include Comparable`). Ruby's `min(n)` and block forms do not exist.

```ruby
xs = Any[3, 1, 4, 1, 5]
p(Array.min(xs))               # => 1
p(Array.max(xs))               # => 5
p(Array.max(Any[2, 1.5]))    # => 2
p(Array.min(Any[]))          # => nil
```

```ruby error
Array.min(Any[1, "a"])       # !> Array.min: elements compared in order may be (Integer, String), which cannot be compared
```

```ruby error
Array.max(Any[1.0, Float.NAN])   # !> ArgumentError: Array.max: cannot compare elements of types Float
```

## minmax

`Array.minmax(x)`

The two-element Tuple `[min, max]`. On an empty Array `[nil, nil]`, and each position's type is the element type joined with nil (the nil of a miss, like `min`'s and `max`'s: reported at level 3 only). The comparison rules are those of `min` and `max`: incomparable types are a `type` problem statically, a failure at run time an `ArgumentError`.

```ruby
p(Array.minmax(Any[3, 1, 4]))    # => [1, 4]
p(Array.minmax(Any[]))           # => [nil, nil]
lo, hi = Array.minmax(Any[3, 1, 4])
p(lo + hi)                         # => 5
```

## min_by, max_by

`Array.min_by(x) { }`

`Array.max_by(x) { }`

The element for which the block's value is smallest or largest. On an empty Array nil (the nil of a miss, reported at level 3 only, like `min`'s and `max`'s). The block's values must be comparable with each other: incomparable types are a `type` problem statically, a failure the checker cannot see an `ArgumentError` at run time.

```ruby
ws = Any["bb", "a", "ccc"]
p(Array.min_by(ws) { |s| String.size(s) })     # => "a"
p(Array.max_by(ws) { |s| String.size(s) })     # => "ccc"
p(Array.min_by(Any[]) { |s| s })             # => nil
```

```ruby error
Array.min_by(Any[1, "a"]) { |x| x }  # !> Array.min_by: elements compared in order may be (Integer, String), which cannot be compared
```

## minmax_by

`Array.minmax_by(x) { }`

The two-element Tuple of the element with the smallest block value and the one with the largest. On an empty Array `[nil, nil]` (each position the nil of a miss, reported at level 3 only). The comparison rules are those of `min_by` and `max_by`.

```ruby
lo, hi = Array.minmax_by(Any["bb", "a", "ccc"]) { |s| String.size(s) }
p([lo, hi])                    # => ["a", "ccc"]
```

## tally

`Array.tally(x)`

A Hash from each element to the number of times it occurs (an Integer), in order of first appearance. The elements must be values a Hash key can be (`TypeError`).

```ruby
p(Array.tally(Any["a", "b", "a"]))   # => {"a" => 2, "b" => 1}
```

## sort, sort!

`Array.sort(x)`

`Array.sort!(x)`

A new Array of the elements in ascending order (`sort`), or the subject sorted in place and returned (`sort!`). Ruby's comparison block is not accepted (a static error); for another order use `sort_by`. The elements must be comparable with each other: a pair of incomparable types in the element type is a `type` problem statically, and an element that may be nil a `nil` problem (`compact` removes the nils). A comparison failure the checker cannot see (`Float.NAN`, say) is an `ArgumentError` at run time. Integers and Floats compare with each other, Tuples element by element, class instances with their type's `<=>`.

```ruby
xs = Any[3, 1, 2]
p(Array.sort(xs))              # => [1, 2, 3]
p(xs)                          # => [3, 1, 2]
p(Array.sort!(xs))             # => [1, 2, 3]
p(xs)                          # => [1, 2, 3]
p(Array.sort(Any[[2, "a"], [1, "b"]]))   # => [[1, "b"], [2, "a"]]
```

```ruby error
Array.sort(Any[1, 2]) { |a, b| b <=> a }     # !> Array.sort does not take a block
```

```ruby error
Array.sort(Any[1, "a"])      # !> Array.sort: elements compared in order may be (Integer, String), which cannot be compared
```

```ruby error
Array.sort(Any[1, nil])      # !> Array.sort: argument elements may be nil
```

```ruby error
Array.sort(Any[1.0, Float.NAN])  # !> ArgumentError: Array.sort: cannot compare elements of types Float
```

## sort_by, sort_by!

`Array.sort_by(x) { }`

`Array.sort_by!(x) { }`

A new Array of the elements in ascending order of the block's values (the keys), or the subject sorted in place and returned (`sort_by!`). The keys must be comparable with each other: incomparable types are a `type` problem statically, a key that may be nil a `nil` problem, and a failure the checker cannot see an `ArgumentError` at run time. A descending order is a negated key; several keys are a Tuple `[k1, k2]` (Tuples compare in dictionary order).

```ruby
ws = Any["bb", "a", "ccc"]
p(Array.sort_by(ws) { |s| -String.size(s) })           # => ["ccc", "bb", "a"]
p(Array.sort_by(Any["bb", "a", "c"]) { |s| [String.size(s), s] })   # => ["a", "c", "bb"]
xs = Any[3, 1, 2]
p(Array.sort_by!(xs) { |x| -x })                       # => [3, 2, 1]
p(xs)                                                  # => [3, 2, 1]
```

## reverse, reverse!

`Array.reverse(x)`

`Array.reverse!(x)`

A new Array of the elements in reverse order (`reverse`), or the subject reversed in place and returned (`reverse!`).

```ruby
xs = Any[1, 2, 3]
p(Array.reverse(xs))           # => [3, 2, 1]
p(xs)                          # => [1, 2, 3]
p(Array.reverse!(xs))          # => [3, 2, 1]
p(xs)                          # => [3, 2, 1]
```

## rotate, rotate!

`Array.rotate(x, [Integer])`

`Array.rotate!(x, [Integer])`

A new Array with the first n elements (default 1) moved to the end (`rotate`), or the subject rotated in place and returned (`rotate!`). A negative n rotates the other way.

```ruby
xs = Any[1, 2, 3]
p(Array.rotate(xs))            # => [2, 3, 1]
p(Array.rotate(xs, 2))         # => [3, 1, 2]
p(Array.rotate(xs, -1))        # => [3, 1, 2]
p(Array.rotate!(xs))           # => [2, 3, 1]
p(xs)                          # => [2, 3, 1]
```

## shuffle, shuffle!

`Array.shuffle(x)`

`Array.shuffle!(x)`

A new Array of the elements in random order (`shuffle`), or the subject shuffled in place and returned (`shuffle!`).

```ruby
xs = Any[1, 2, 3]
p(Array.size(Array.shuffle(xs)))       # => 3
p(Array.sort(Array.shuffle!(xs)))      # => [1, 2, 3]
```

## sample

`Array.sample(x)`

One element chosen at random. On an empty Array nil (the nil of a miss, reported at level 3 only). There is no Ruby `sample(n)`.

```ruby
p(Array.sample(Any[7]))      # => 7
p(Array.sample(Any[]))       # => nil
```

## uniq, uniq!

`Array.uniq(x)`

`Array.uniq!(x)`

A new Array without the duplicate elements (`==` to an earlier one; the first is kept), or the subject with them removed in place and returned (`uniq!`). `uniq!` returns nil when nothing was removed (as Ruby's), and using that result unchecked is a level 2 `nil` problem. Ruby's block form does not exist.

```ruby
p(Array.uniq(Any[1, 2, 1, 3, 2]))    # => [1, 2, 3]
xs = Any[1, 1]
p(Array.uniq!(xs))                     # => [1]
p(Array.uniq!(xs))                     # => nil
```

```ruby error
p(Array.size(Array.uniq!(Any[1, 1])))    # !> Array.size: argument 1 may be nil
```

## compact, compact!

`Array.compact(x)`

`Array.compact!(x)`

A new Array without the nil elements (`compact`; nil is removed from the element type), or the subject with them removed in place and returned (`compact!`). `compact!` returns nil when there was no nil, and using that result unchecked is a level 2 `nil` problem.

```ruby
p(Array.compact(Any[1, nil, 2]))     # => [1, 2]
xs = Any[1, nil]
p(Array.compact!(xs))                  # => [1]
p(Array.compact!(xs))                  # => nil
```

## flatten, flatten!

`Array.flatten(x)`

`Array.flatten!(x)`

A new Array with every nested Array expanded into its elements (`flatten`), or the subject flattened in place and returned (`flatten!`). Only Arrays are expanded: a Tuple is one value and stays whole (Ruby's `flatten` would open it, so a Tuple element is reported statically as a `type` problem; to open Tuples, write `Array.flat_map(xs) { |t| Tuple.to_a(t) }`). `flatten!` returns nil when there was nothing nested, and using that result unchecked is a level 2 `nil` problem. Ruby's depth argument does not exist.

```ruby
p(Array.flatten(Any[1, Any[2, Any[3]], 4]))     # => [1, 2, 3, 4]
xs = Any[1, Any[2]]
p(Array.flatten!(xs))                                 # => [1, 2]
p(Array.flatten!(xs))                                 # => nil
```

```ruby error
p(Array.flatten(Any[[1, 2], [3, 4]]))   # !> which flatten leaves whole
```

## zip

`Array.zip(x, *Array)`

A new Array of Tuples pairing each element of the subject with the elements at the same position in the other Arrays. Its length is the subject's: a shorter other Array is padded with nil (that position's type is the element type joined with nil), and a longer one's extra elements are dropped. Unlike Ruby's, the groups are Tuples, not Arrays.

```ruby
p(Array.zip(Any[1, 2, 3], Any["a", "b"]))              # => [[1, "a"], [2, "b"], [3, nil]]
p(Array.zip(Any[1, 2], Any["a", "b"], Any[:x, :y]))  # => [[1, "a", :x], [2, "b", :y]]
```

## product

`Array.product(x, Array)`

A new Array of all combinations `[a, b]` (Tuples) of an element of the subject and an element of the other Array. Unlike Ruby's, it takes exactly one other Array, and the groups are Tuples.

```ruby
p(Array.product(Any[1, 2], Any["a", "b"]))    # => [[1, "a"], [1, "b"], [2, "a"], [2, "b"]]
p(Array.product(Any[1], Any[]))               # => []
```

## transpose

`Array.transpose(x)`

Takes an Array of Arrays as a matrix and returns a new Array with rows and columns exchanged. A row may be an Array or a Tuple (a sequence of Tuples such as `zip`'s result transposes too); the result's rows are Arrays. The rows must all have the same length: a different length is an `IndexError`. An element that is neither an Array nor a Tuple is a `TypeError`.

```ruby
p(Array.transpose(Any[Any[1, 2], Any[3, 4]]))   # => [[1, 3], [2, 4]]
p(Array.transpose(Array.zip(Any[1, 2], Any["a", "b"])))   # => [[1, 2], ["a", "b"]]
```

```ruby error
Array.transpose(Any[Any[1, 2], Any[3]])  # !> IndexError: Array.transpose: element size differs (1 should be 2)
```

## combination, repeated_combination

`Array.combination(x, Integer)`

`Array.repeated_combination(x, Integer)`

A new Array of the combinations of n elements, as Arrays: each element at most once (`combination`), or with repetition (`repeated_combination`). An Array, not Ruby's Enumerator. When n exceeds the length, `combination` gives `[]`.

```ruby
p(Array.combination(Any[1, 2, 3], 2))           # => [[1, 2], [1, 3], [2, 3]]
p(Array.repeated_combination(Any[1, 2], 2))     # => [[1, 1], [1, 2], [2, 2]]
p(Array.combination(Any[1, 2], 5))              # => []
```

## permutation, repeated_permutation

`Array.permutation(x, [Integer])`

`Array.repeated_permutation(x, Integer)`

A new Array of the permutations of n elements, as Arrays. `permutation(a)` without n uses all elements. `repeated_permutation` may pick the same element again (n is required).

```ruby
p(Array.permutation(Any[1, 2, 3], 2))   # => [[1, 2], [1, 3], [2, 1], [2, 3], [3, 1], [3, 2]]
p(Array.permutation(Any[1, 2]))         # => [[1, 2], [2, 1]]
p(Array.repeated_permutation(Any[1, 2], 2))     # => [[1, 1], [1, 2], [2, 1], [2, 2]]
```

## union, intersection, difference

`Array.union(x, *Array)`

`Array.intersection(x, *Array)`

`Array.difference(x, *Array)`

New Arrays treating the Arrays as sets (in the subject's order, comparing with `==`). `union` is the elements of the subject and all the other Arrays without duplicates (the element type is the union of all); `intersection` the elements that are in every other Array, without duplicates; `difference` the elements that are in none of the other Arrays (duplicates kept). Any number of other Arrays may be given, even none.

```ruby
a = Any[1, 2, 3]
b = Any[2, 3, 4]
p(Array.union(a, b))                   # => [1, 2, 3, 4]
p(Array.union(Any[1, 1]))            # => [1]
p(Array.intersection(a, b))            # => [2, 3]
p(Array.intersection(a, b, Any[3]))  # => [3]
p(Array.difference(a, b))              # => [1]
p(Array.difference(Any[1, 2, 2, 3], Any[2], Any[3]))    # => [1]
```

## intersect?

`Array.intersect?(x, Array)`

True when the two Arrays have at least one element in common.

```ruby
p(Array.intersect?(Any[1, 2], Any[2, 3]))  # => true
p(Array.intersect?(Any[1, 2], Any[9]))     # => false
```

## join

`Array.join(x, [String])`

A String of the elements converted with `to_s`, separated by `sep` (default `""`). `to_s` follows the rules of `puts`: nil is empty, Arrays and Tuples take their `inspect` form, and a class instance uses its type's `to_s` ([Values and types](../03-values.md)).

```ruby
p(Array.join(Any[1, 2, 3], ", "))            # => "1, 2, 3"
p(Array.join(Any[1, "a", nil, :s, [1, 2]]))  # => "1as[1, 2]"
p(Array.join(Any[]))                         # => ""
```

## to_h

`Array.to_h(x)`

A Hash from a sequence of `[key, value]` Tuples. Every element must be a two-element Tuple (not an Array); any other value is a `TypeError`. When a key occurs more than once the later one stays. The keys must be values a Hash key can be. Ruby's block form does not exist.

```ruby
p(Array.to_h(Any[[:a, 1], [:b, 2]]))     # => {a: 1, b: 2}
p(Array.to_h(Any[[1, "a"], [1, "b"]]))   # => {1 => "b"}
p(Array.to_h(Any[]))                     # => {}
```

```ruby error
Array.to_h(Any[1, 2])        # !> TypeError: Array.to_h: Array.to_h needs [key, value] Tuples, got Integer
```

## to_set

`Array.to_set(x)`

A new Set of the elements (duplicates become one). The elements must be values a Set element can be (`TypeError`).

```ruby
p(Array.to_set(Any[1, 2, 1]))    # => Set[1, 2]
```

## pack

`Array.pack(x, String)`

Ruby's `Array#pack`: the elements as a byte String according to the format `fmt`. An invalid format is an `ArgumentError`; an element that does not fit the format (a String for `"C*"`, say) a `TypeError`. The reverse is `String.unpack`.

```ruby
p(Array.pack(Any[65, 66], "C*"))     # => "AB"
p(Array.pack(Any[1, 2], "n*"))       # => "\x00\x01\x00\x02"
```

```ruby error
Array.pack(Any["a"], "C")    # !> TypeError: Array.pack: no implicit conversion of String into Integer
```

## +, -, *

`Array.+(x, Any)`

`Array.-(x, Any)`

`Array.*(x, Any)`

`a + b` is a new Array of both Arrays' elements (the element type is the union; `concat` does it in place). `a - b` is a new Array without every element that is `==` to one in `b` (the same as `difference`). `a * n` is a new Array of `a` repeated n times; a negative n is an `ArgumentError`. The right operand must be an Array for `+` and `-` and an Integer for `*`; a Tuple or a String (Ruby's `a * ","` is `join`) is a `type` problem statically.

```ruby
a = Any[1, 2, 3]
b = Any[2, 3, 4]
p(a + b)                       # => [1, 2, 3, 2, 3, 4]
p(a - b)                       # => [1]
p(Any[1, 2, 2] - Any[2])   # => [1]
p(a * 2)                       # => [1, 2, 3, 1, 2, 3]
p(Array.+(a, Any[9]))        # => [1, 2, 3, 9]
p(Array.*(a, 0))               # => []
```

```ruby error
p(Any[1] + [2])              # !> which the left operand's type does not support
```

## ==, !=

`Array.==(x, Any)`

`Array.!=(x, Any)`

True when the two Arrays have the same length and the elements at each position are `==` (`!=` is the negation). A right operand that is not an Array (a Tuple, nil) is never equal. `[1, 2]` is a Tuple and so is not equal to `Any[1, 2]`.

```ruby
a = Any[1, 2, 3]
p(a == Any[1, 2, 3])         # => true
p(a != Any[1, 2])            # => true
p(a == [1, 2, 3])              # => false
p(a == nil)                    # => false
p(Array.==(a, a))              # => true
```

## <, <=, >, >=

`Array.<(x, Any)`

`Array.<=(x, Any)`

`Array.>(x, Any)`

`Array.>=(x, Any)`

Compare two Arrays in dictionary order: elements are compared from the front, the first differing position decides, and when all are equal the shorter Array is smaller. The elements must be of comparable types; a pair that is not is a `type` problem statically (`which cannot be compared`) and an `ArgumentError` at run time. The right operand must be an Array (a Tuple is a `type` problem).

```ruby
p(Any[1, 2] < Any[1, 3])           # => true
p(Any[1, 2] < Any[1, 2, 0])        # => true
p(Any[2] > Any[1, 9])              # => true
p(Any[1, 2] <= Any[1, 2])          # => true
p(Array.>=(Any[1], Any[1]))        # => true
```

```ruby error
p(Any[1, "a"] < Any[1, 2])         # !> which cannot be compared
```

## <=>

`Array.<=>(x, Any)`

The result of the dictionary-order comparison as -1, 0, or 1. The rules are those of `<`. An incomparable pair is a `type` problem statically; run without the check (`--strict=0`) it gives nil (where `<` and the others raise `ArgumentError`).

```ruby
a = Any[1, 2, 3]
p(a <=> Any[1, 2, 4])        # => -1
p(a <=> a)                     # => 0
p(Array.<=>(a, Any[1]))      # => 1
```

# Enum

Enum is Ruby's Enumerable under a short name, a module that the prelude (`sakelib/prelude.sake`, read before every program) defines in Sake. Its functions, `Enum.map(x) { }` and the others, are mixin functions: they dispatch on the type of the first argument ([Program structure](../02-program.md), "Calling a module's functions"). Array, Hash, Set and Range include Enum and answer with their own operations: `Enum.map(xs)` is `Array.map(xs)` when xs is an Array, and `Enum.each(h) { |k, v| }` is `Hash.each`. Nothing is copied into a built-in type. A value of any other type (an Integer, a Tuple, a String) is a `type` problem before running and a `TypeError` while running. Tuple does not include Enum: its length and position types are fixed, so it is indexed and destructured instead.

A class of your own joins with `include Enum` and `def each(c) = ... yield(v) ...`. Every other function of Enum is written over `each`, so they are copied into the class ([include](../02-program.md)) and `C.map(c) { }`, `C.count(c)` and `Enum.first(c)` all work; the class's own definition of one of them wins.

```ruby
class Deck
  include Enum
  attr_reader cards
  def each(d) = Array.each(@cards) { |c| yield(c) }
end
d = Deck.new(Any["A", "K", "Q", "J"])
p(Deck.map(d) { |c| String.downcase(c) })     # => ["a", "k", "q", "j"]
p(Enum.count(d))                              # => 4
p(Enum.select(Any[3, 1, 2]) { |v| v > 1 })  # => [3, 2]
```

```ruby error
p(Enum.map(5) { |v| v })                      # !> Enum.map dispatches on its first argument, which is Integer; the types that include Enum are Array, Hash, Set, Range
```

The functions whose result may be nil (`find`, `first`, `min_by`, `max_by`) return `nil | T` in a class's copy, and using that unchecked is reported at level 2; dispatched to a built-in type, they follow that type's operation (the nil of a miss of `Array.first` is level 3). The sections below describe Enum's own definitions; on a built-in type the operation of the same name runs ([Array](Array.md), [Hash](Hash.md), [Set](Set.md), [Range](Range.md)).

## each

`Enum.each(x)`

The required function. A class that includes Enum defines `def each(c)` and calls `yield(v)` for each element (two values are fine, as Hash does). `Enum.each(x) { }` dispatches to the `each` of x's type. A class that does not define it is a `type` problem wherever a call can reach it, and `NotImplementedError` while running.

```ruby
Enum.each(Hash.of(a: 1, b: 2)) { |k, v| puts("#{k}: #{v}") }
# => a: 1
# => b: 2
```

## map

`Enum.map(x) { }`

An Array of the block's value for each element.

```ruby
p(Enum.map(1..3) { |v| v * v })               # => [1, 4, 9]
p(Enum.map(Set.of(1, 2)) { |v| -v })             # => [-1, -2]
```

## select, filter

`Enum.select(x) { }`

`Enum.filter(x) { }`

An Array of the elements for which the block is true.

```ruby
p(Enum.select(Any[1, 2, 3, 4]) { |v| v % 2 == 0 })   # => [2, 4]
p(Enum.filter(1..6) { |v| v > 4 })                     # => [5, 6]
```

## reject

`Enum.reject(x) { }`

An Array of the elements for which the block is false.

```ruby
p(Enum.reject(Any[1, 2, 3, 4]) { |v| v % 2 == 0 })   # => [1, 3]
```

## filter_map

`Enum.filter_map(x) { }`

Applies the block to each element and keeps the true results (nil and false are dropped).

```ruby
p(Enum.filter_map(Any["1", "x", "3"]) { |s| String.to_i(s) if String.match?(s, /\A\d+\z/) })   # => [1, 3]
```

## flat_map

`Enum.flat_map(x) { }`

The Arrays (or Tuples) the block returns, joined one level deep.

```ruby
p(Enum.flat_map(Any[1, 2]) { |v| Any[v, v * 10] })   # => [1, 10, 2, 20]
```

## find, detect

`Enum.find(x) { }`

`Enum.detect(x) { }`

The first element for which the block is true, or nil when there is none (level 2 in a class's copy, and for `Array.find` and its kin).

```ruby
p(Enum.find(Any[3, 8, 5]) { |v| v > 4 })    # => 8
p(Enum.detect(1..3) { |v| v > 9 })            # => nil
```

## reduce, inject

`Enum.reduce(x, init) { }`

`Enum.inject(x, init) { }`

Starting from `init`, calls the block as `yield(acc, v)` for each element and returns the last accumulated value. Unlike Ruby's, the initial value cannot be left out: the result type would not follow from the element type alone (as for [Array](Array.md)'s `reduce`).

```ruby
p(Enum.reduce(Any[1, 2, 3], 0) { |acc, v| acc + v })     # => 6
p(Enum.inject(1..4, 1) { |acc, v| acc * v })               # => 24
```

## sum

`Enum.sum(x, [init]) [{ }]`

The sum of the elements, or of the block's values, starting from `init` (default 0). On a built-in type the elements must be numbers or values of a type that includes `Arithmetic` ([Array](Array.md)).

```ruby
p(Enum.sum(1..4))                             # => 10
p(Enum.sum(Any[1, 2, 3]) { |v| v * 10 })      # => 60
```

## count

`Enum.count(x) [{ }]`

The number of elements, or of those for which the block is true.

```ruby
p(Enum.count(Set.of(1, 2, 3)))                   # => 3
p(Enum.count(1..10) { |v| v % 3 == 0 })       # => 3
```

## include?

`Enum.include?(x, v)`

True when some element is `==` to `v`.

```ruby
p(Enum.include?(1..5, 3))                     # => true
p(Enum.include?(Any["a"], "b"))             # => false
```

## any?, all?, none?, one?

`Enum.any?(x) [{ }]`

`Enum.all?(x) [{ }]`

`Enum.none?(x) [{ }]`

`Enum.one?(x) [{ }]`

True when the block is true for at least one element / for all / for none / for exactly one. Without a block, the elements' own truth counts.

```ruby
xs = Any[1, 2, 3]
p(Enum.any?(xs) { |v| v > 2 })                # => true
p(Enum.all?(xs) { |v| v > 2 })                # => false
p(Enum.none?(xs) { |v| v > 5 })               # => true
p(Enum.one?(xs) { |v| v > 2 })                # => true
```

## min_by, max_by

`Enum.min_by(x) { }`

`Enum.max_by(x) { }`

The element with the smallest / largest block value; nil when empty. The block's values must be comparable with each other (the checker sees to it statically).

```ruby
words = Any["pear", "fig", "banana"]
p(Enum.min_by(words) { |w| String.length(w) })   # => "fig"
p(Enum.max_by(words) { |w| String.length(w) })   # => "banana"
```

## sort_by

`Enum.sort_by(x) { }`

An Array of the elements in ascending order of the block's value.

```ruby
p(Enum.sort_by(Any["bb", "a", "ccc"]) { |s| -String.length(s) })   # => ["ccc", "bb", "a"]
```

## to_a

`Enum.to_a(x)`

A new Array of the elements.

```ruby
p(Enum.to_a(1..3))                            # => [1, 2, 3]
p(Enum.to_a(Hash.of(a: 1)))                      # => [[:a, 1]]
```

## first

`Enum.first(x, [n])`

The first element (nil when empty), or with `n` an Array of the first `n`.

```ruby
p(Enum.first(Any[7, 8, 9]))                 # => 7
p(Enum.first(1..10, 3))                       # => [1, 2, 3]
```

## take, drop

`Enum.take(x, n)`

`Enum.drop(x, n)`

An Array of the first `n` elements / of all but the first `n`.

```ruby
p(Enum.take(1..5, 2))                         # => [1, 2]
p(Enum.drop(1..5, 2))                         # => [3, 4, 5]
```

## group_by

`Enum.group_by(x) { }`

A Hash from each block value to the Array of the elements that gave it.

```ruby
p(Enum.group_by(1..6) { |v| v % 3 })          # => {1 => [1, 4], 2 => [2, 5], 0 => [3, 6]}
```

## partition

`Enum.partition(x) { }`

A Tuple `[yes, no]`: the elements for which the block is true, and the others.

```ruby
yes, no = Enum.partition(1..6) { |v| v > 3 }
p(yes)                                        # => [4, 5, 6]
p(no)                                         # => [1, 2, 3]
```

## each_with_object

`Enum.each_with_object(x, memo) { }`

Calls the block as `yield(v, memo)` for each element and returns `memo`.

```ruby
p(Enum.each_with_object(Any[1, 2], Any[]) { |v, m| Array.unshift(m, v) })   # => [2, 1]
```

## compact

`Enum.compact(x)`

An Array of the elements that are not nil.

```ruby
p(Enum.compact(Any[1, nil, 2]))             # => [1, 2]
```

# Tuple

A Tuple is a sequence of fixed length with a type per position. The literal `[1, "a"]` makes a Tuple (Ruby's `[1, "a"]` is an Array; in Sake the Array is `Any[1, "a"]`, see [Values and types](../03-values.md)). The number of elements and the type at each position are part of the value's type, so the checker sees index ranges and position types statically. A Tuple cannot grow or shrink; use an Array for a sequence of varying length.

Multiple assignment `a, b = t` and the Tuple pattern `in [Integer, String]` take a Tuple apart. A function's `return a, b` returns a Tuple.

The operators on Tuples are `==`, `!=`, `<`, `<=`, `>`, `>=`, `<=>` (two Tuples compare in dictionary order) and the index `t[i]`, `t[i] = v`. The entries `Tuple.<(x, y)` and so on below are the function forms of those operators ([Operators and indexing](../05-operators.md)).

## Tuple[]

`Tuple[*Any]`

Makes an **Array of Tuples** (not a Tuple). It is a typed Array like `Integer[]` and `String[]`: every element must be a Tuple, and every write (`Array.push`, ...) is checked. An element that is not a Tuple is a `type` problem statically and a `TypeError` at run time. The empty Array of Tuples is `Tuple[]`.

```ruby
pairs = Tuple[[1, "a"], [2, "b"]]
Array.push(pairs, [3, "c"])
p(pairs)                   # => [[1, "a"], [2, "b"], [3, "c"]]
p(Array.size(Tuple[]))     # => 0
```

```ruby error
Array.push(Tuple[], 1)     # !> Array.push: an element must be Tuple, but is Integer
```

## []

`Tuple.[](x, Any)`

The function form of `t[i]`. Returns the element at position `i` (an Integer; a negative one counts from the end). The result has the type of that position. Because a Tuple's length is part of its type, an index outside it does not give nil as an Array's does: it is a `type` problem statically (`the index is outside the Tuple`) and an `IndexError` at run time. A Range is not accepted as the index (`TypeError`).

```ruby
t = [1, "a", 2.5]
p(t[0])                    # => 1
p(t[-1])                   # => 2.5
p(Tuple.[](t, 1))          # => "a"
```

```ruby error
t = [1, 2]
p(t[5])                    # !> the index is outside the Tuple
```

## []=

`Tuple.[]=(x, Any, Any)`

The function form of `t[i] = v`. Replaces the element at position `i` with `v` and returns `v`. A value whose type differs from that position's type is a `type` problem statically and a `TypeError` at run time. An index outside the Tuple is an `IndexError`.

```ruby
t = [1, "a"]
t[0] = 10
Tuple.[]=(t, 1, "b")
p(t)                       # => [10, "b"]
```

```ruby error
t = [1, 2]
t[0] = "x"                 # !> the value must be Integer, but is String
```

## length, size

`Tuple.length(x)`

`Tuple.size(x)`

The number of elements (an Integer). A Tuple's length follows from its type, so to the checker it is a constant.

```ruby
p(Tuple.size([1, "a", 2.5]))     # => 3
p(Tuple.length([]))              # => 0
```

## max, min

`Tuple.max(x)`

`Tuple.min(x)`

The largest or smallest element (Ruby's `[a, b].max`). Since each position's type is known, whether the elements are comparable with each other is checked statically: an incomparable pair (an Integer and a String, say) is a `type` problem (`elements compared in order may be (Integer, String), which cannot be compared`), and a position that may be nil a `nil` problem. A comparison failure the checker cannot see (`Float.NAN`, say) is an `ArgumentError` at run time. Integers and Floats compare with each other. The empty Tuple `[]` gives nil. class instances compare with their type's `<=>` (`include Comparable`). The result's type is the join of the positions' types; for a non-empty Tuple it is never nil.

```ruby
p(Tuple.max([3, 1, 2]))          # => 3
p(Tuple.min([3, 1, 2]))          # => 1
p(Tuple.max([2, 1.5]))           # => 2
p(Tuple.max([3, 1]) + 1)         # => 4
p(Tuple.max([]))                 # => nil
```

```ruby error
p(Tuple.max([1, "a"]))           # !> Tuple.max: elements compared in order may be (Integer, String), which cannot be compared
```

```ruby error
p(Tuple.max([1.0, Float.NAN]))   # !> ArgumentError: Tuple.max: cannot compare elements of types Float
```

## minmax

`Tuple.minmax(x)`

The two-element Tuple `[min, max]`. The comparison rules are those of `min` and `max` (an incomparable pair is a `type` problem statically).

```ruby
lo, hi = Tuple.minmax([3, 1, 2])
p([lo, hi])                      # => [1, 3]
```

## to_a

`Tuple.to_a(x)`

A new Array with the same elements. It is independent of the Tuple, and the Array can grow. Its element type is the union of the Tuple's position types.

```ruby
t = [1, "a"]
a = Tuple.to_a(t)
Array.push(a, :x)
p(a)                             # => [1, "a", :x]
p(t)                             # => [1, "a"]
```

## ==, !=

`Tuple.==(x, Any)`

`Tuple.!=(x, Any)`

True when the two Tuples have the same length and the elements at each position are `==` (`!=` is the negation). A right operand that is not a Tuple (an Array, nil) is never equal. `Any[1, 2]` and `[1, 2]` differ in type and are not equal.

```ruby
p([1, 2] == [1, 2])              # => true
p([1, 2] != [1, 2, 3])           # => true
p([1, 2] == Any[1, 2])         # => false
```

## <, <=, >, >=

`Tuple.<(x, Any)`

`Tuple.<=(x, Any)`

`Tuple.>(x, Any)`

`Tuple.>=(x, Any)`

Compare two Tuples in dictionary order: elements are compared from the front, the first differing position decides, and when all are equal the shorter Tuple is smaller. Each pair of positions must be of comparable types; a pair that is not is a `type` problem statically (`elements compared in order may be (Integer, String)`). The right operand must be a Tuple.

```ruby
p([1, 2] < [1, 3])               # => true
p([2] > [1, 9])                  # => true
p([1] < [1, 0])                  # => true
p(Tuple.<=([1, 2], [1, 2]))      # => true
```

```ruby error
p([1, 2] < [1, "a"])             # !> which cannot be compared
```

## <=>

`Tuple.<=>(x, Any)`

The result of the dictionary-order comparison as -1, 0, or 1. The rules are those of `<`. A Tuple returned as the key of `Array.sort_by` sorts in this order.

```ruby
p([1, 2] <=> [1, 2])             # => 0
p([1, 2] <=> [1, 2, 3])          # => -1
p(Array.sort_by(Any["bb", "a", "c"]) { |s| [String.size(s), s] })   # => ["a", "c", "bb"]
```

# Range

A Range is a pair of ends with a flag that says whether the end is included: `1..5` includes 5, `1...5` does not. The ends are Integers, Floats, Strings, or nil ([Values and types](../03-values.md)): `1..` is an endless Range, `..5` a beginless one. Write an endless Range in parentheses at the end of a line, `r = (3..)`, because `3..` followed by a line break continues onto the next line, as it does in Ruby. Two ends of different types (`1.."a"`) are an `ArgumentError` at run time. A Range is a value: no operation changes it, and every result is a new value.

The checker keeps the type of the ends with the Range (`Range[Integer]`), and that type decides which operations apply:

- **Walking the values** (`each`, `map`, `to_a`, `select`, `find`, `count`, ...) needs a Range that starts with an Integer or a String. `"a".."e"` walks with Ruby's `String#succ`. A Range of another type, such as `1.0..2.0`, is a `type` problem statically (`the Range's first value must be Integer|String, but is Float`) and a `TypeError` at run time.
- `step`, `sum`, and `size` need an Integer start; a String Range is a `type` problem for them.
- **Finite** is required where the whole Range is used (`to_a`, `map`, `sum`, `last`, `min`, `max`, ...): on an endless Range those raise `RangeError` (`cannot do this on an endless Range 1..`). Operations that can stop early (`each`, `find`, `take`, `first(r, n)`, `each_slice`, `bsearch`) accept an endless Range; `each` on one runs until `break`.
- `cover?`, `overlap?`, `begin`, `end`, and `exclude_end?` compare with the ends only and take any Range, Float and endless ones included. `include?` and `member?` do the same on a Range of numbers, but walk a String Range, as Ruby's do.
- A Range whose beginning is above its end (`5..1`) has no values: walking it does nothing, `to_a` is `[]`, `min` is nil; `first`, `last`, `begin`, and `end` still give the ends.

The operators on Ranges are `==` and `!=` (two Ranges are equal when their ends and `exclude_end?` are). Indexing with a Range (`a[1..3]`, `s[0...2]`) is an operation of Array and String ([Operators and indexing](../05-operators.md)). A Range cannot be a Hash key or a Set element (`TypeError`). Results that may be nil (`first`, `last`, `min`, `max`, `begin`, `end`, `find`, `find_index`, `bsearch`, `min_by`, `max_by`) are typed `T | nil`: `--strict` (level 2) reports using them unchecked. Only the elements of the Tuples of `minmax` and `minmax_by` are the "nil of a miss" (as `a, b = tuple`), which level 3 alone reports.

Many operations are Ruby's Enumerable methods. Unlike Ruby, every block is **required** where the signature shows `{ }` (except `count` and `sum`, whose block is optional), `reduce` and `inject` need the initial value, and the block of `each`, `map`, and the others takes exactly one parameter (`each_with_index`, `each_with_object`, `chunk_while`, `slice_when` take two); a block with the wrong number of parameters is an `ArgumentError` at run time.

## begin, end

`Range.begin(x)`

`Range.end(x)`

The two ends as written, as Ruby's `r.begin` and `r.end`. `end` is the written end even when it is excluded (`Range.end(1...5)` is 5, while `Range.max(1...5)` is 4). The result is `T | nil`: `end` of an endless Range and `begin` of a beginless one are nil, so `--strict` asks for a check before the result is used in arithmetic.

```ruby
p(Range.begin(1..5))         # => 1
p(Range.end(1...5))          # => 5
p(Range.end(1..))            # => nil
p(Range.begin("a".."z"))     # => "a"
```

```ruby error
x = Range.begin(1..5)
p(x + 1)                     # !> the operands may be nil
```

## exclude_end?

`Range.exclude_end?(x)`

True for a Range written with three dots (`1...5`), false for two (`1..5`, `1..`).

```ruby
p(Range.exclude_end?(1..5))      # => false
p(Range.exclude_end?(1...5))     # => true
p(Range.exclude_end?(1..))       # => false
```

## ==, !=

`Range.==(x, Any)`

`Range.!=(x, Any)`

Two Ranges are equal when both ends are `==` and `exclude_end?` agrees (`!=` is the negation). `1..5` and `1...5` differ. A right operand that is not a Range (an Integer, nil) is never equal, with the function form as with the operator: `(1..5) == 5` and `Range.==(1..5, 5)` are both false.

```ruby
p((1..5) == (1..5))          # => true
p((1..5) == (1...5))         # => false
p((1..5) != (1...6))         # => true
p((1..5) == 5)               # => false
p(Range.==(1..5, 1..5))      # => true
p(Range.==(1..5, 5))         # => false
p(Range.!=(1..5, nil))       # => true
```

## include?, cover?, member?

`Range.include?(x, Any)`

`Range.cover?(x, Any)`

`Range.member?(x, Any)`

True when the value is in the Range. `cover?` is Ruby's `cover?`: it compares with the ends only (`begin <= v <= end`, or `< end` for `...`) and never walks the Range, so it works on endless, beginless, and Float Ranges, and a Float is found in an Integer Range. `include?` and `member?` are Ruby's `include?`: on a Range of numbers they are the same as `cover?`, but a **String Range is walked** with `String#succ`, so `"a".."z"` includes `"m"` but not `"mm"` (which `cover?` accepts, since `"a" <= "mm" <= "z"`), and an endless or beginless String Range is a `TypeError` (`cannot determine inclusion in beginless/endless ranges`). A value of another type than the ends (`"a"` in `1..5`, nil) gives false. A Range as the value: `cover?` asks whether it lies entirely inside, as Ruby's; `include?` and `member?` answer false.

```ruby
p(Range.include?(1..5, 5))         # => true
p(Range.include?(1...5, 5))        # => false
p(Range.cover?(1..5, 2.5))         # => true
p(Range.member?(1..5, 0))          # => false
p(Range.include?(1.., 100))        # => true
p(Range.include?("a".."z", "m"))   # => true
p(Range.include?("a".."z", "mm"))  # => false
p(Range.cover?("a".."z", "mm"))    # => true
p(Range.include?(1..5, "a"))       # => false
p(Range.cover?(1..5, 2..3))        # => true
p(Range.include?(1..5, 2..3))      # => false
```

```ruby error
p(Range.include?("a".., "b"))      # !> TypeError: Range.include?: cannot determine inclusion in beginless/endless ranges
```

## overlap?

`Range.overlap?(x, Range)`

True when the two Ranges share at least one value, as Ruby's `overlap?`. An excluded end does not count (`1...5` and `5..8` do not overlap). Any ends are accepted; Ranges of different types do not overlap.

```ruby
p(Range.overlap?(1..5, 3..8))        # => true
p(Range.overlap?(1..5, 6..8))        # => false
p(Range.overlap?(1...5, 5..8))       # => false
p(Range.overlap?(1.., 100..200))     # => true
p(Range.overlap?(1.0..2.0, 1.5..3.0))  # => true
```

## size

`Range.size(x)`

The number of Integers in the Range (an Integer). Only for an Integer Range: a String Range is a `type` problem (`the Range's first value must be Integer, but is String`), as is one that may be a Float (`1..2.5`). An endless Range is a `RangeError`. A Range with the beginning above the end has size 0. Use `count` for a String Range.

```ruby
p(Range.size(1..5))      # => 5
p(Range.size(1...5))     # => 4
p(Range.size(5..1))      # => 0
```

```ruby error
p(Range.size(1..))       # !> RangeError: Range.size: cannot do this on an endless Range 1..
```

## count

`Range.count(x) [{ }]`

Without a block, the number of values (an Integer), walking the Range; with a block, the number of values for which the block gives a truthy result. Works on Integer and String Ranges; needs a finite Range (`RangeError`). Unlike Ruby's `count(v)`, there is no form with a value: use `include?`.

```ruby
p(Range.count("a".."c"))                        # => 3
p(Range.count(1..5) { |i| Integer.odd?(i) })    # => 3
```

## each

`Range.each(x) { }`

Calls the block with each value in turn and returns the Range. An Integer Range walks by 1; a String Range by `String#succ` (`"az".."bc"` gives `"az"`, `"ba"`, `"bb"`, `"bc"`). An endless Range runs until a `break`; `break v` makes `v` the result. The block takes one parameter. A Float Range is a `type` problem.

```ruby
r = Range.each(1..3) { |i| puts(i) }     # => 1
                                         # => 2
                                         # => 3
p(r)                                     # => 1..3
Range.each("a".."c") { |s| print(s) }    # => abc
puts("")
x = Range.each(1..) { |i| break i * 10 if i > 2 }
p(x)                                     # => 30
```

```ruby error
Range.each(1.0..2.0) { |f| p(f) }        # !> the Range's first value must be Integer|String, but is Float
```

## each_with_index

`Range.each_with_index(x) { }`

Calls the block with each value and its position (0, 1, 2, ...) and returns the Range. The block takes two parameters. An endless Range needs a `break`.

```ruby
r = Range.each_with_index("a".."c") { |s, i| p([s, i]) }   # => ["a", 0]
                                                            # => ["b", 1]
                                                            # => ["c", 2]
p(r)                                                        # => "a".."c"
```

## each_entry

`Range.each_entry(x) { }`

The same as `each`: Ruby's `Enumerable#each_entry`, kept for programs written against it. Returns the Range.

```ruby
p(Range.each_entry(1..3) { |i| puts(i) })    # => 1
                                             # => 2
                                             # => 3
                                             # => 1..3
```

## reverse_each

`Range.reverse_each(x) { }`

Calls the block with each value from the end to the beginning and returns the Range. Needs a finite Range (`RangeError`).

```ruby
p(Range.reverse_each(1..3) { |i| puts(i) })  # => 3
                                             # => 2
                                             # => 1
                                             # => 1..3
```

## step

`Range.step(x, Integer) { }`

Calls the block with the beginning, the beginning plus the step, and so on while the value lies in the Range, and returns the Range. Only for an Integer Range (`type` for a String or Float one). An endless Range needs a `break`. A negative step walks a descending Range (`10..1` with -3 gives 10, 7, 4, 1) and gives nothing on an ascending one. A step of 0 is an `ArgumentError` (`step can't be 0`).

```ruby
p(Range.step(1..10, 3) { |i| puts(i) })    # => 1
                                           # => 4
                                           # => 7
                                           # => 10
                                           # => 1..10
Range.step(1...9, 4) { |i| p(i) }          # => 1
                                           # => 5
Range.step(10..1, -3) { |i| p(i) }         # => 10
                                           # => 7
                                           # => 4
                                           # => 1
```

```ruby error
Range.step("a".."z", 2) { |s| p(s) }       # !> the Range's first value must be Integer, but is String
```

```ruby error
Range.step(1..5, 0) { |i| p(i) }           # !> ArgumentError: Range.step: step can't be 0
```

## each_slice

`Range.each_slice(x, Integer) { }`

Calls the block with consecutive Arrays of n values (the last one shorter when the values run out) and returns the Range. An endless Range needs a `break`. A size of 0 or less is an `ArgumentError`.

```ruby
p(Range.each_slice(1..7, 3) { |a| p(a) })    # => [1, 2, 3]
                                             # => [4, 5, 6]
                                             # => [7]
                                             # => 1..7
```

## each_cons

`Range.each_cons(x, Integer) { }`

Calls the block with each window of n consecutive values as an Array and returns the Range. With fewer than n values the block is not called. A size of 0 or less is an `ArgumentError`.

```ruby
p(Range.each_cons(1..4, 2) { |a| p(a) })     # => [1, 2]
                                             # => [2, 3]
                                             # => [3, 4]
                                             # => 1..4
```

## each_with_object

`Range.each_with_object(x, Any) { }`

Calls the block with each value and the given object, and returns that object. Use it to fill a container; as in Ruby, a block that returns a new value does not change the object (`Range.each_with_object(1..3, 0) { |i, acc| acc + i }` returns 0: use `reduce`). An endless Range needs a `break`.

```ruby
a = Range.each_with_object(1..3, Any[]) { |i, acc| Array.push(acc, i * i) }
p(a)                                         # => [1, 4, 9]
```

## cycle

`Range.cycle(x, Integer) { }`

Walks the Range n times over and returns nil. Nothing happens for n of 0 or less. Needs a finite Range (`RangeError`). Unlike Ruby's `cycle` without an argument, the count is required.

```ruby
p(Range.cycle(1..2, 2) { |i| puts(i) })      # => 1
                                             # => 2
                                             # => 1
                                             # => 2
                                             # => nil
```

## to_a, entries

`Range.to_a(x)`

`Range.entries(x)`

A new Array of all the values. Needs an Integer or String Range (`type` otherwise) and a finite one (`RangeError`). The element type of the Array is the type of the ends.

```ruby
p(Range.to_a(1..5))          # => [1, 2, 3, 4, 5]
p(Range.to_a(1...5))         # => [1, 2, 3, 4]
p(Range.entries("a".."e"))   # => ["a", "b", "c", "d", "e"]
p(Range.to_a(5..1))          # => []
```

```ruby error
p(Range.to_a(1..))           # !> RangeError: Range.to_a: cannot do this on an endless Range 1..
```

## first

`Range.first(x, [Integer])`

Without a count: the beginning of the Range, whatever the Range contains (`Range.first(5..1)` is 5, `Range.first(1...1)` is 1, `Range.first(1.0..2.0)` is 1.0). The type is `T | nil` and `--strict` asks for a check before arithmetic; a beginless Range does not give nil but raises `RangeError` (`cannot get the first element of beginless range`). With a count n: a new Array of the first n values (fewer when the Range is shorter), walking the Range, so an Integer or String Range is needed (a Float Range is a `TypeError` at run time, `can't iterate from Float`); an endless Range is fine. A negative count is a `RangeError` (`negative array size (or size too big)`).

```ruby
p(Range.first(1..5))         # => 1
p(Range.first(1..5, 2))      # => [1, 2]
p(Range.first(1..5, 10))     # => [1, 2, 3, 4, 5]
p(Range.first(1.., 3))       # => [1, 2, 3]
p(Range.first("a".."c", 2))  # => ["a", "b"]
```

```ruby error
p(Range.first(..5))          # !> RangeError: Range.first: cannot get the first element of beginless range
```

```ruby error
p(Range.first(1.0..2.0, 2))  # !> TypeError: Range.first: can't iterate from Float
```

## last

`Range.last(x, [Integer])`

Without a count: the written end, as `Range.end` (so `Range.last(1...5)` is 5, as Ruby's). With a count n: a new Array of the last n values, which excludes an excluded end (`Range.last(1...5, 2)` is `[3, 4]`). Needs a finite Range (`RangeError` on an endless one, even without a count). The result without a count is `T | nil`. A negative count is a `RangeError`.

```ruby
p(Range.last(1..5))          # => 5
p(Range.last(1...5))         # => 5
p(Range.last(1..5, 2))       # => [4, 5]
p(Range.last(1...5, 2))      # => [3, 4]
p(Range.last("a".."c"))      # => "c"
```

```ruby error
p(Range.last(1..))           # !> RangeError: Range.last: cannot do this on an endless Range 1..
```

## take

`Range.take(x, Integer)`

A new Array of the first n values (all of them when there are fewer), as `first(r, n)`. An endless Range is fine. A negative n is an `ArgumentError`.

```ruby
p(Range.take(1..10, 3))      # => [1, 2, 3]
p(Range.take(1.., 3))        # => [1, 2, 3]
p(Range.take(1..3, 10))      # => [1, 2, 3]
```

## drop

`Range.drop(x, Integer)`

A new Array of the values after the first n (empty when there are not more). Needs a finite Range (`RangeError`). A negative n is an `ArgumentError`.

```ruby
p(Range.drop(1..5, 2))       # => [3, 4, 5]
p(Range.drop(1..5, 10))      # => []
```

## take_while

`Range.take_while(x) { }`

A new Array of the values from the beginning up to, not including, the first for which the block gives a falsy result. An endless Range is fine as long as the block eventually says no.

```ruby
p(Range.take_while(1..10) { |i| i < 4 })   # => [1, 2, 3]
p(Range.take_while(1..) { |i| i < 4 })     # => [1, 2, 3]
```

## drop_while

`Range.drop_while(x) { }`

A new Array of the values from the first for which the block gives a falsy result to the end. An endless Range is not rejected, but the result would be endless, so the operation never returns on one.

```ruby
p(Range.drop_while(1..6) { |i| i < 4 })    # => [4, 5, 6]
```

## map, collect

`Range.map(x) { }`

`Range.collect(x) { }`

A new Array of the block's results, one per value. Needs a finite Integer or String Range. The element type of the Array is the type of the block's results.

```ruby
n = 4
p(Range.map(1..n) { |i| i * i })                 # => [1, 4, 9, 16]
p(Range.collect("a".."c") { |s| String.upcase(s) })   # => ["A", "B", "C"]
```

```ruby error
p(Range.map(1..) { |i| i })                      # !> RangeError: Range.map: cannot do this on an endless Range 1..
```

## flat_map, collect_concat

`Range.flat_map(x) { }`

`Range.collect_concat(x) { }`

A new Array that joins the Arrays (or Tuples) the block returns, one level deep. The block must return an Array or a Tuple: another value is a `TypeError` at run time (`the block must return an Array or a Tuple, got Integer`). Needs a finite Range.

```ruby
p(Range.flat_map(1..3) { |i| Any[i, i] })      # => [1, 1, 2, 2, 3, 3]
p(Range.flat_map(1..2) { |i| [i, i * 10] })      # => [1, 10, 2, 20]
```

```ruby error
p(Range.flat_map(1..2) { |i| i })                # !> TypeError: Range.flat_map: the block must return an Array or a Tuple, got Integer
```

## select, filter, find_all

`Range.select(x) { }`

`Range.filter(x) { }`

`Range.find_all(x) { }`

A new Array of the values for which the block gives a truthy result. Needs a finite Integer or String Range. The Array's element type is the type of the ends.

```ruby
p(Range.select(1..6) { |i| Integer.even?(i) })   # => [2, 4, 6]
p(Range.filter("a".."e") { |s| s > "c" })        # => ["d", "e"]
```

## reject

`Range.reject(x) { }`

A new Array of the values for which the block gives a falsy result; the complement of `select`. Needs a finite Range.

```ruby
p(Range.reject(1..6) { |i| Integer.even?(i) })   # => [1, 3, 5]
```

## filter_map

`Range.filter_map(x) { }`

A new Array of the block's results, leaving out nil and false, as Ruby's `filter_map`. The checker removes nil from the element type, so the Array can be used without nil checks. Needs a finite Range.

```ruby
p(Range.filter_map(1..6) { |i| Integer.even?(i) ? i * 10 : nil })   # => [20, 40, 60]
```

## partition

`Range.partition(x) { }`

A Tuple of two Arrays: the values for which the block is truthy, then the others. Needs a finite Range.

```ruby
evens, odds = Range.partition(1..6) { |i| Integer.even?(i) }
p(evens)                                         # => [2, 4, 6]
p(odds)                                          # => [1, 3, 5]
```

## group_by

`Range.group_by(x) { }`

A Hash from each block result to the Array of the values that gave it, in order of first appearance. Needs a finite Range. The key type is the type of the block's results; the value type is an Array of the ends' type (reading `h[k]` gives nil for a key that is not there, as any Hash does).

```ruby
h = Range.group_by(1..6) { |i| i % 3 }
p(h)                                             # => {1 => [1, 4], 2 => [2, 5], 0 => [3, 6]}
p(h[1])                                          # => [1, 4]
```

## chunk_while, slice_when

`Range.chunk_while(x) { }`

`Range.slice_when(x) { }`

An Array of Arrays that cuts the values into runs. The block receives two consecutive values; `chunk_while` keeps them in the same run while the block is truthy, `slice_when` starts a new run when it is truthy. Needs a finite Range.

```ruby
p(Range.chunk_while(1..5) { |a, b| b != 3 })         # => [[1, 2], [3, 4, 5]]
p(Range.slice_when(1..6) { |a, b| Integer.even?(b) })  # => [[1], [2, 3], [4, 5], [6]]
```

## slice_before, slice_after

`Range.slice_before(x) { }`

`Range.slice_after(x) { }`

An Array of Arrays that cuts the values before (or after) each value for which the block is truthy. Needs a finite Range.

```ruby
p(Range.slice_before(1..6) { |i| Integer.even?(i) })  # => [[1], [2, 3], [4, 5], [6]]
p(Range.slice_after(1..6) { |i| Integer.even?(i) })   # => [[1, 2], [3, 4], [5, 6]]
```

## find, detect

`Range.find(x) { }`

`Range.detect(x) { }`

The first value for which the block gives a truthy result, or nil when there is none. The result is `T | nil`, so `--strict` wants a check before it is used. An endless Range is fine when some value satisfies the block; otherwise the search never ends.

```ruby
p(Range.find(1..10) { |i| i * i > 10 })      # => 4
p(Range.detect(1..10) { |i| i > 100 })       # => nil
p(Range.find(1..) { |i| i * i > 10 })        # => 4
s = Range.find("a".."e") { |c| c > "b" }
if s
  p(String.upcase(s))                        # => "C"
end
```

## find_index

`Range.find_index(x) { }`

The position (0, 1, 2, ...) of the first value for which the block is truthy, or nil (`Integer | nil`). Unlike Ruby's `find_index(v)`, a block is required and there is no form with a value. An endless Range is fine when a value is found.

```ruby
p(Range.find_index(1..10) { |i| i * i > 10 })  # => 3
p(Range.find_index(1..10) { |i| i > 100 })     # => nil
```

## bsearch

`Range.bsearch(x) { }`

Binary search over an Integer Range, as Ruby's: in find-minimum mode the block gives true for the wanted value and every value above it, and the result is the smallest such value, or nil when the block is never true (`T | nil`). An endless Range is fine. A String Range is a `TypeError` at run time (`can't do binary search for String`).

```ruby
p(Range.bsearch(1..100) { |i| i * i >= 50 })   # => 8
p(Range.bsearch(1..100) { |i| i > 1000 })      # => nil
p(Range.bsearch(1..) { |i| i * i >= 50 })      # => 8
```

```ruby error
p(Range.bsearch("a".."z") { |s| s >= "m" })    # !> TypeError: Range.bsearch: can't do binary search for String
```

## all?, any?, none?, one?

`Range.all?(x) { }`

`Range.any?(x) { }`

`Range.none?(x) { }`

`Range.one?(x) { }`

Whether the block is truthy for every value, for at least one, for none, or for exactly one. Unlike Ruby's, the block is required (there is no `any?` without a block and no pattern argument). Needs a finite Range (`RangeError`). On a Range with no values, `all?` and `none?` are true, `any?` and `one?` false.

```ruby
p(Range.all?(1..5) { |i| i > 0 })    # => true
p(Range.any?(1..5) { |i| i > 4 })    # => true
p(Range.none?(1..5) { |i| i > 5 })   # => true
p(Range.one?(1..5) { |i| i == 3 })   # => true
p(Range.any?(5..1) { |i| true })     # => false
```

## reduce, inject

`Range.reduce(x, Any) { }`

`Range.inject(x, Any) { }`

Folds the values: the block receives the accumulated value and the next value, and its result becomes the accumulated value; the result is the last accumulated value. The initial value is **required** (Ruby's `inject` without one is a `wrong number of arguments` problem), and the result's type is that of the initial value joined with the block's results. There is no symbol form (`reduce(:+)`); use `sum` for that. Needs a finite Range.

```ruby
p(Range.reduce(1..5, 0) { |acc, i| acc + i })                   # => 15
p(Range.inject(1..5, 1) { |acc, i| acc * i })                   # => 120
p(Range.reduce(1..3, "") { |acc, i| acc + Integer.to_s(i) })    # => "123"
p(Range.reduce(5..1, 7) { |acc, i| acc + i })                   # => 7
```

```ruby error
p(Range.inject(1..5) { |acc, i| acc + i })   # !> wrong number of arguments for Range.inject (given 1, expected 2)
```

## sum

`Range.sum(x, [Integer|Float|Rational|Complex]) [{ }]`

The sum of the values, or of the block's results, added to the initial value (default 0). Only for an Integer Range (`type` for a String Range), and a finite one (`RangeError`). The result's type follows the initial value and the block: Integer values with an Integer start give an Integer; a Float start gives a Float. As Ruby's `Range#sum`, an Integer Range with a Rational initial value gives a Float (`Range.sum(1..3, 2r)` is 8.0), and a Complex initial value is a `RangeError` at run time (`can't convert 1+2i into Float`). A block whose results are not numbers is a `type` problem.

```ruby
p(Range.sum(1..100))                 # => 5050
p(Range.sum(1..10, 100))             # => 155
p(Range.sum(1..3) { |i| i * 2 })     # => 12
p(Range.sum(1..3, 0.5))              # => 6.5
p(Range.sum(1..3, 2r))               # => 8.0
p(Range.sum(1..0))                   # => 0
```

```ruby error
p(Range.sum("a".."c"))               # !> the Range's first value must be Integer, but is String
```

```ruby error
p(Range.sum(1..3, Complex(1, 2)))    # !> RangeError: Range.sum: can't convert 1+2i into Float
```

## min, max

`Range.min(x)`

`Range.max(x)`

The smallest and the largest value, or nil when the Range has no values (`5..1`): the type is `T | nil`. `max` respects an excluded end (`Range.max(1...5)` is 4). Needs a finite Range (`RangeError`). Both accept a Float Range with an included end (`Range.max(1.0..2.5)` is 2.5), as Ruby's; `max` of a Float Range with an excluded end is a `TypeError` (`cannot exclude non Integer end value`; `min` of it is fine).

```ruby
p(Range.min(1..5))           # => 1
p(Range.max(1...5))          # => 4
p(Range.max(5..1))           # => nil
p(Range.min("a".."c"))       # => "a"
p(Range.max(1.0..2.5))       # => 2.5
p(Range.min(1.0...2.5))      # => 1.0
```

```ruby error
x = Range.min(1..5)
p(x + 1)                     # !> the operands may be nil
```

```ruby error
p(Range.max(1.0...2.5))      # !> TypeError: Range.max: cannot exclude non Integer end value
```

## minmax

`Range.minmax(x)`

The Tuple `[min, max]`; both are nil on a Range with no values (`5..1`, `2.5..1.0`). Those are the nil of a miss: using an element unchecked is reported only at `--strict=3`. Needs a finite Range (`RangeError`). As `min` and `max`, it accepts a Float Range with an included end (`Range.minmax(1.0..2.5)` is `[1.0, 2.5]`); a Float Range with an excluded end is a `TypeError` (`cannot exclude non Integer end value`).

```ruby
lo, hi = Range.minmax(1..5)
p([lo, hi])                  # => [1, 5]
p(Range.minmax(5..1))        # => [nil, nil]
p(Range.minmax(1.0..2.5))    # => [1.0, 2.5]
p(Range.minmax("a".."c"))    # => ["a", "c"]
```

```ruby error
p(Range.minmax(1.0...2.5))   # !> TypeError: Range.minmax: cannot exclude non Integer end value
```

## min_by, max_by

`Range.min_by(x) { }`

`Range.max_by(x) { }`

The value for which the block gives the smallest (largest) result, or nil on a Range with no values (`T | nil`). Needs a finite Range.

```ruby
p(Range.min_by(1..5) { |i| -i })               # => 5
p(Range.max_by("a".."e") { |s| String.ord(s) })  # => "e"
p(Range.min_by(5..1) { |i| i })                # => nil
```

## minmax_by

`Range.minmax_by(x) { }`

The Tuple of the values with the smallest and the largest block result; `[nil, nil]` on a Range with no values (the nil of a miss, reported only at `--strict=3`). Needs a finite Range.

```ruby
p(Range.minmax_by(1..5) { |i| -i })    # => [5, 1]
```

## sort

`Range.sort(x)`

A new Array of the values in ascending order, which for a Range is `to_a`. Needs a finite Integer or String Range.

```ruby
p(Range.sort(1..3))          # => [1, 2, 3]
p(Range.sort(3..1))          # => []
```

## sort_by

`Range.sort_by(x) { }`

A new Array of the values ordered by the block's results. Needs a finite Range.

```ruby
p(Range.sort_by(1..5) { |i| -i })                    # => [5, 4, 3, 2, 1]
p(Range.sort_by("a".."c") { |s| -String.ord(s) })    # => ["c", "b", "a"]
```

## uniq

`Range.uniq(x)`

A new Array of the values without duplicates; since a Range has none, it is `to_a`. Needs a finite Integer or String Range.

```ruby
p(Range.uniq(1..3))          # => [1, 2, 3]
```

## compact

`Range.compact(x)`

A new Array of the values without nil; since a Range has no nil, it is `to_a`. Needs a finite Integer or String Range.

```ruby
p(Range.compact(1..3))       # => [1, 2, 3]
```

## tally

`Range.tally(x)`

A Hash from each value to how often it appears, which for a Range is always 1. Needs a finite Integer or String Range.

```ruby
p(Range.tally("a".."c"))     # => {"a" => 1, "b" => 1, "c" => 1}
```

## to_set

`Range.to_set(x)`

A new Set of the values. Needs a finite Integer or String Range.

```ruby
p(Range.to_set(1..3))        # => Set[1, 2, 3]
```

## zip

`Range.zip(x, *Array)`

A new Array of Tuples: the i-th Tuple holds the i-th value of the Range and the i-th element of each Array given. A shorter Array contributes nil (the Tuple's type says so). Without Arrays each Tuple holds one value. Needs a finite Integer or String Range.

```ruby
p(Range.zip(1..3, Any["a", "b", "c"]))     # => [[1, "a"], [2, "b"], [3, "c"]]
p(Range.zip(1..3, Any[10, 20]))            # => [[1, 10], [2, 20], [3, nil]]
p(Range.zip(1..2))                           # => [[1], [2]]
```

# Hash

A Hash maps keys to values and keeps insertion order. There is no literal like Ruby's `{"a" => 1}`: a Hash is made by `Hash.of("a" => 1, b: 2)`, `Hash.new`, or `Hash.new(default)` ([Values and types](../03-values.md)). **`{k: v}` is a Record, not a Hash** (and `{}` is an empty Record, rejected statically; see [Record](Record.md)). Keys written as labels, `Hash.of(name: v)`, are Symbols.

A key may be an Integer, Float, String, Symbol, true, false, nil, Time, or a Tuple, Record, Array, Hash, Set, or a value of a class made only of these. A Regexp, a Range, or a value of a class that defines its own equality (`==` or `Comparable`) cannot be a key: `TypeError` at run time. Tuple and Record keys are copied when stored, so writing to the original later does not change the key (an Array key, as in Ruby, becomes unfindable when changed). `"a"` and `:a` are different keys, and so are `1` and `1.0`.

`h[k]` gives nil on a miss (the default, for a Hash made by `Hash.new(default)`). Using that nil unchecked is reported only at `--strict=3`, as `index-nil` (level 2 does not report it). To make a miss an exception, use `Hash.fetch`. The nil of `Hash.dig`, `Hash.min_by` and `Hash.max_by` is a miss too (level 3). The nil of `Hash.delete`, `Hash.key`, `Hash.first`, `Hash.shift`, `Hash.find` and the like is reported at level 2.

An operation that takes a block passes each entry as one Tuple `[key, value]`: `|k, v|` takes it apart, `|kv|` receives the Tuple. `each_with_object` takes `|(k, v), memo|` and `reduce` takes `|acc, (k, v)|`. Iteration follows insertion order. The pairs returned by `to_a`, `sort_by`, `take`, `first` and others are `[k, v]` Tuples too.

The checker gives each Hash, per the place it is made, one key type and one value type. Storing another type with `Hash.store` or `[]=` makes the value type a union (after putting `"x"` into `Hash.of("a" => 1)` the values are `Integer | String`, and `+ 1` on a fetched value is a `type` problem). In-place operations that change the element type, `transform_keys!` and `transform_values!`, do the same (see below).

The operators on a Hash are `==`, `!=` and the index `h[k]`, `h[k] = v`. `Hash.==(x, y)` and so on are the function forms of those operators ([Operators and indexing](../05-operators.md)).

## of

`Hash.of(*Any)`

`Hash.of(k => v, ...)` makes a new Hash. The arguments are `key => value` pairs only (in the label form `Hash.of(name: v)` the key is a Symbol); anything else (`Hash.of(1)`, `Hash.of({x: 1})`) is a static error. `Hash.new` is the empty Hash. When a key is written twice, the later value stays. A value that cannot be a key is a `TypeError` at run time.

```ruby
h = Hash.of("a" => 1, b: 2)
p(h)                               # => {"a" => 1, b: 2}
p(Hash.new)                          # => {}
p(Hash.of(1 => 2, 1 => 3))            # => {1 => 3}
p(Hash.of([1, 2] => "t", nil => "n"))  # => {[1, 2] => "t", nil => "n"}
```

```ruby error
p(Hash.of(1))                         # !> Hash.of takes `key => value` pairs, like `Hash.of("a" => 1)`
```

## Hash[]

`Hash[*Any]`

Makes an Array of Hashes, as `T[...]` does for every T: each element must be a Hash, checked on every write. A Hash itself is made with `Hash.of(k => v, ...)` or `Hash.new`.

```ruby
hs = Hash[Hash.of(a: 1), Hash.new]
p(hs)                              # => [{a: 1}, {}]
```

## new

`Hash.new([Any])`

Makes an empty Hash. `Hash.new(default)` makes one whose `h[k]` gives `default` for a missing key, as Ruby's does. That one object is shared: changing the Array of `Hash.new(Any[])` through `h["x"]` is visible under every key, and nothing is stored in the Hash. Ruby's block form `Hash.new { |h, k| ... }` does not exist (a static error). `Hash.fetch` ignores the default.

```ruby
counts = Hash.new(0)
counts["x"] += 1
counts["x"] += 1
p(counts)                          # => {"x" => 2}
p(counts["none"])                  # => 0
p(Hash.new["none"])                # => nil
d = Hash.new(Any[])
Array.push(d["a"], 1)
p(d["b"])                          # => [1]
p(d)                               # => {}
```

```ruby error
h = Hash.new { |hh, k| 0 }         # !> Hash.new does not take a block
```

## default, set_default

`Hash.default(x)`

`Hash.set_default(x, Any)`

`default` is the value `h[k]` gives for a missing key (the default of `Hash.new(default)`; nil when there is none). `set_default(h, v)` changes it to `v` and returns `v` (Ruby's `h.default = v`). The checker adds the default's type to the type of `h[k]`.

```ruby
h = Hash.new(0)
p(Hash.default(h))                 # => 0
p(Hash.default(Hash.new))            # => nil
Hash.set_default(h, 5)
p(h["q"])                          # => 5
```

## []

`Hash.[](x, Any)`

The function form of `h[k]`. Returns the value under key `k`, or the default (normally nil) when there is none. That nil is reported only at `--strict=3`, as `index-nil`. The key may be of any type; no value is a static problem. For a Hash made by `Hash.new(default)` the default's type joins the result, so `h[k] + 1` on `Hash.new(0)` passes even at level 3.

```ruby
h = Hash.of("a" => 1, [1, 2] => "t")
p(h["a"])                          # => 1
p(h[[1, 2]])                       # => "t"
p(h["zz"])                         # => nil
p(Hash.[](h, "a"))                 # => 1
```

## []=, store

`Hash.[]=(x, Any, Any)`

`Hash.store(x, Any, Any)`

`h[k] = v` and its function form. Binds key `k` to value `v` and returns `v`. A new key goes to the end; an existing key keeps its position and only its value changes. A value that cannot be a key is a `TypeError`. The checker adds the types of `k` and `v` to the Hash's key and value types.

```ruby
h = Hash.of("a" => 1)
h["b"] = 2
p(Hash.store(h, "a", 10))          # => 10
p(h)                               # => {"a" => 10, "b" => 2}
```

```ruby error
Hash.store(Hash.new, /x/, 1)         # !> TypeError: Hash.store: Regexp cannot be a Hash key or Set element
```

## fetch

`Hash.fetch(x, Any, [Any])`

Returns the value under key `k`. When there is none, returns the third argument `default` if given, and otherwise raises `KeyError` (as Ruby's; there is no block form). The default of `Hash.new(default)` is not used. The result type is the union of the value type and the type of `default`, so it is never nil unless a value is: used instead of `h[k]`, it avoids the `index-nil` report of `--strict=3` too.

```ruby
h = Hash.of("a" => 1)
p(Hash.fetch(h, "a"))              # => 1
p(Hash.fetch(h, "zz", 0))          # => 0
p(Hash.fetch(h, "a") + 1)          # => 2
```

```ruby error
p(Hash.fetch(Hash.of("a" => 1), "b")) # !> KeyError: Hash.fetch: key not found: "b"
```

## dig

`Hash.dig(x, Any, *Any)`

Digs through nested containers with the keys in order, as Ruby's `dig`: `dig(h, k)` is `h[k]`, and each further key indexes the value found so far (a Hash by key, an Array or a Tuple by position). The result is the value reached, or nil as soon as a key is missing on the way (for a miss in a Hash made by `Hash.new(default)` the default is given, as by `h[k]`, and the checker adds the default's type to the result). That nil is the nil of a miss, as with `h[k]`: using it unchecked is reported only at `--strict=3`, as `index-nil`; level 2 does not report it. The checker follows the keys through the types: a key applied to a value that is not a Hash, an Array, or a Tuple (an Integer, a String, a Record) is a `type` problem statically (`the value must be Array|Hash|Tuple, but is Integer`). A key of the wrong kind for the container reached (a String for an Array) is a `TypeError` at run time (`an index into Array must be Integer, got String`).

```ruby
h = Hash.of("a" => Hash.of("b" => Any[10, 20]))
p(Hash.dig(h, "a", "b", 1))        # => 20
p(Hash.dig(h, "a", "zz", 1))       # => nil
p(Hash.dig(h, "zz"))               # => nil
t = Hash.of("t" => [1, "x"])
p(Hash.dig(t, "t", 1))             # => "x"
p(Hash.dig(t, "t", 5))             # => nil
p(Hash.dig(Hash.new(0), "q") + 1)  # => 1
x = Hash.dig(h, "a", "b", 0)
if x
  p(x + 1)                         # => 11
end
```

```ruby error
p(Hash.dig(Hash.of("a" => 1), "a", "b"))   # !> Hash.dig: the value must be Array|Hash|Tuple, but is Integer
```

## fetch_values

`Hash.fetch_values(x, *Any)`

An Array of the values under the listed keys, in that order. A missing key raises `KeyError` (the default is not used). The element type is the value type; it is never nil.

```ruby
h = Hash.of("a" => 1, "b" => 2)
p(Hash.fetch_values(h, "b", "a"))  # => [2, 1]
```

```ruby error
p(Hash.fetch_values(Hash.of("a" => 1), "a", "zz"))   # !> KeyError: Hash.fetch_values: key not found: "zz"
```

## values_at

`Hash.values_at(x, *Any)`

An Array of the values under the listed keys, in that order. A missing key gives the default (normally nil) at its position. The element type is `value | nil`, so using an element unchecked is reported at level 2.

```ruby
h = Hash.of("a" => 1, "b" => 2)
p(Hash.values_at(h, "a", "zz"))    # => [1, nil]
p(Hash.values_at(Hash.new(9), "x"))   # => [9]
```

## key?, has_key?, include?, member?

`Hash.key?(x, Any)`

`Hash.has_key?(x, Any)`

`Hash.include?(x, Any)`

`Hash.member?(x, Any)`

True when key `k` is present, even when its value is nil (this tells it apart from an `h[k]` that gives nil). The four are the same operation.

```ruby
h = Hash.of("a" => nil)
p(Hash.key?(h, "a"))               # => true
p(Hash.include?(h, "b"))           # => false
p(h["a"])                          # => nil
```

## value?, has_value?

`Hash.value?(x, Any)`

`Hash.has_value?(x, Any)`

True when some value is `==` to `v`.

```ruby
h = Hash.of("a" => 1, "b" => 2)
p(Hash.value?(h, 2))               # => true
p(Hash.has_value?(h, 3))           # => false
```

## key

`Hash.key(x, Any)`

The first key whose value equals `v`, or nil (reported at level 2).

```ruby
h = Hash.of("a" => 1, "b" => 2, "c" => 2)
p(Hash.key(h, 2))                  # => "b"
p(Hash.key(h, 9))                  # => nil
```

## assoc, rassoc

`Hash.assoc(x, Any)`

`Hash.rassoc(x, Any)`

`assoc(h, k)` is the pair `[k, v]` under key `k`; `rassoc(h, v)` is the first pair `[k, v]` whose value is `v`. Both are Tuples, or nil (reported at level 2).

```ruby
h = Hash.of("a" => 1, "b" => 2)
p(Hash.assoc(h, "a"))              # => ["a", 1]
p(Hash.rassoc(h, 2))               # => ["b", 2]
p(Hash.assoc(h, "zz"))             # => nil
```

## length, size

`Hash.length(x)`

`Hash.size(x)`

The number of entries (an Integer).

```ruby
p(Hash.size(Hash.of("a" => 1, "b" => 2)))   # => 2
p(Hash.length(Hash.new))                   # => 0
```

## empty?

`Hash.empty?(x)`

True when there are no entries.

```ruby
p(Hash.empty?(Hash.new))             # => true
p(Hash.empty?(Hash.new(0)))        # => true
p(Hash.empty?(Hash.of("a" => 1)))     # => false
```

## count

`Hash.count(x) [{ }]`

Without a block, the number of entries (as `size`); with a block, the number of entries for which the block, given `[k, v]`, returns true (an Integer).

```ruby
h = Hash.of("a" => 1, "b" => 2, "c" => 3)
p(Hash.count(h))                   # => 3
p(Hash.count(h) { |k, v| v >= 2 })   # => 2
```

## keys

`Hash.keys(x)`

A new Array of the keys in insertion order. Its element type is the key type. It is not a typed Array, so values of other types can be pushed onto it.

```ruby
h = Hash.of("b" => 2, "a" => 1)
p(Hash.keys(h))                    # => ["b", "a"]
p(Hash.keys(Hash.new))               # => []
```

## values

`Hash.values(x)`

A new Array of the values in insertion order. Its element type is the value type.

```ruby
h = Hash.of("b" => 2, "a" => 1)
p(Hash.values(h))                  # => [2, 1]
```

## to_a

`Hash.to_a(x)`

A new Array of the entries as `[k, v]` Tuples, in insertion order. The inner Arrays of Ruby's `to_a` are Tuples in Sake.

```ruby
h = Hash.of(1 => "x", 2 => "y")
p(Hash.to_a(h))                    # => [[1, "x"], [2, "y"]]
Array.each(Hash.to_a(h)) { |k, v| p(k) }   # => 1
                                           # => 2
```

## flatten

`Hash.flatten(x)`

A new Array of the keys and values alternating, `[k1, v1, k2, v2, ...]`. As Ruby's `flatten` at depth 1: an Array or Tuple value is not opened (there is no depth argument). The element type is the union of the key and value types.

```ruby
p(Hash.flatten(Hash.of(1 => "x", 2 => "y")))        # => [1, "x", 2, "y"]
p(Hash.flatten(Hash.of("a" => Any[1, 2])))        # => ["a", [1, 2]]
```

## to_h

`Hash.to_h(x)`

The Hash itself (as Ruby's `Hash#to_h`; not a copy). There is no block form. For a copy use `Hash.dup`.

```ruby
h = Hash.of("a" => 1)
p(Hash.to_h(h))                    # => {"a" => 1}
p(Kernel.equal?(Hash.to_h(h), h))  # => true
```

## dup

`Hash.dup(x)`

A new Hash with the same entries (a shallow copy: the key and value objects are shared). The default is kept. A `store` or `delete` on one does not affect the other.

```ruby
h = Hash.new(0)
Hash.store(h, "a", 1)
h2 = Hash.dup(h)
Hash.store(h2, "b", 2)
p(h)                               # => {"a" => 1}
p(h2)                              # => {"a" => 1, "b" => 2}
p(h2["zz"])                        # => 0
```

## first

`Hash.first(x)`

The first inserted entry as a Tuple `[k, v]`, or nil when the Hash is empty (reported at level 2). Ruby's `first(n)` form does not exist; the first n entries are `Hash.take`.

```ruby
p(Hash.first(Hash.of("a" => 1, "b" => 2)))   # => ["a", 1]
p(Hash.first(Hash.new))                     # => nil
```

## take, drop

`Hash.take(x, Integer)`

`Hash.drop(x, Integer)`

`take(h, n)` is the first n entries, `drop(h, n)` the rest after the first n, as an Array of `[k, v]` Tuples. The Hash does not change. n may exceed the number of entries. A negative n is an `ArgumentError`.

```ruby
h = Hash.of("a" => 1, "b" => 2, "c" => 3)
p(Hash.take(h, 2))                 # => [["a", 1], ["b", 2]]
p(Hash.drop(h, 2))                 # => [["c", 3]]
p(Hash.drop(h, 5))                 # => []
```

```ruby error
p(Hash.take(Hash.of("a" => 1), -1))   # !> ArgumentError: Hash.take: attempt to take negative size
```

## each, each_pair

`Hash.each(x) { }`

`Hash.each_pair(x) { }`

Calls the block for each entry in insertion order and returns the Hash itself. The block receives the Tuple `[k, v]` (`|k, v|` takes it apart). The block is required (Ruby's blockless form returning an Enumerator does not exist).

```ruby
h = Hash.of("a" => 1, "b" => 2)
Hash.each(h) { |k, v| p(k + Integer.to_s(v)) }   # => "a1"
                                                 # => "b2"
Hash.each_pair(h) { |kv| p(kv) }                 # => ["a", 1]
                                                 # => ["b", 2]
r = Hash.each(h) { |k, v| nil }
p(Kernel.equal?(r, h))                           # => true
```

## each_key

`Hash.each_key(x) { }`

Calls the block for each key and returns the Hash itself. The block receives only the key.

```ruby
Hash.each_key(Hash.of("a" => 1, "b" => 2)) { |k| p(k) }   # => "a"
                                                       # => "b"
```

## each_value

`Hash.each_value(x) { }`

Calls the block for each value and returns the Hash itself. The block receives only the value.

```ruby
Hash.each_value(Hash.of("a" => 1, "b" => 2)) { |v| p(v * 10) }   # => 10
                                                              # => 20
```

## each_with_object

`Hash.each_with_object(x, Any) { }`

Calls the block for each entry while carrying `memo` along, and returns `memo` at the end. The block receives two things, the Tuple `[k, v]` and `memo`, so it is written `|(k, v), memo|`. The result type is the type of `memo`.

```ruby
h = Hash.of("a" => 1, "b" => 2)
ks = Hash.each_with_object(h, Any[]) { |(k, v), acc| Array.push(acc, k) }
p(ks)                              # => ["a", "b"]
```

## map

`Hash.map(x) { }`

Calls the block with `[k, v]` for each entry and returns a new Array of the results. As in Ruby the result is an Array, not a Hash (for a Hash use `transform_values` or `to_h`).

```ruby
h = Hash.of("a" => 1, "b" => 2)
p(Hash.map(h) { |k, v| k + Integer.to_s(v) })   # => ["a1", "b2"]
p(Hash.map(h) { |kv| kv })                      # => [["a", 1], ["b", 2]]
```

## flat_map

`Hash.flat_map(x) { }`

Calls the block for each entry and returns a new Array of all the Arrays it returned, joined. The block must return an **Array or a Tuple** (a Tuple's elements are joined in like an Array's); a scalar is a `TypeError` at run time (`the block must return an Array or a Tuple, got Integer`; Ruby keeps a non-Array result as one element).

```ruby
h = Hash.of("a" => 1, "b" => 2)
p(Hash.flat_map(h) { |k, v| Any[k, v] })   # => ["a", 1, "b", 2]
p(Hash.flat_map(h) { |k, v| [k, v] })        # => ["a", 1, "b", 2]
```

```ruby error
Hash.flat_map(Hash.of("a" => 1)) { |k, v| v }   # !> TypeError: Hash.flat_map: the block must return an Array or a Tuple, got Integer
```

## filter_map

`Hash.filter_map(x) { }`

Calls the block for each entry and returns a new Array of the results other than nil and false.

```ruby
h = Hash.of("a" => 1, "b" => 2, "c" => 3)
p(Hash.filter_map(h) { |k, v| v > 1 ? k : nil })   # => ["b", "c"]
```

## select, filter

`Hash.select(x) { }`

`Hash.filter(x) { }`

A new Hash of the entries for which the block returns true (a Hash, as in Ruby). The original does not change. The key and value types are those of the original.

```ruby
h = Hash.of("a" => 1, "b" => 2, "c" => 3)
p(Hash.select(h) { |k, v| v > 1 })   # => {"b" => 2, "c" => 3}
p(Hash.filter(h) { |k, v| k == "a" })   # => {"a" => 1}
p(h)                                 # => {"a" => 1, "b" => 2, "c" => 3}
```

## reject

`Hash.reject(x) { }`

A new Hash without the entries for which the block returns true. The original does not change.

```ruby
h = Hash.of("a" => 1, "b" => 2, "c" => 3)
p(Hash.reject(h) { |k, v| v > 1 })   # => {"a" => 1}
```

## partition

`Hash.partition(x) { }`

The entries for which the block returns true and those for which it returns false, each as an Array of `[k, v]` Tuples, in a two-element Tuple `[true ones, false ones]`. Take it with multiple assignment.

```ruby
h = Hash.of("a" => 1, "b" => 2, "c" => 3)
odd, even = Hash.partition(h) { |k, v| Integer.odd?(v) }
p(odd)                             # => [["a", 1], ["c", 3]]
p(even)                            # => [["b", 2]]
```

## group_by

`Hash.group_by(x) { }`

Calls the block for each entry and returns a new Hash from each block result to the Array of the `[k, v]` Tuples that gave it. The block result must be a value that can be a key.

```ruby
h = Hash.of("a" => 1, "b" => 2, "c" => 3)
g = Hash.group_by(h) { |k, v| v % 2 }
p(g)                               # => {1 => [["a", 1], ["c", 3]], 0 => [["b", 2]]}
p(Hash.fetch(g, 0))                # => [["b", 2]]
```

## find, detect

`Hash.find(x) { }`

`Hash.detect(x) { }`

The first entry for which the block returns true, as a Tuple `[k, v]`, or nil. At level 2, passing that nil on to multiple assignment or an index is reported, so check with `if`.

```ruby
h = Hash.of("a" => 1, "b" => 2)
kv = Hash.find(h) { |k, v| v == 2 }
if kv
  k, v = kv
  p(k)                             # => "b"
end
p(Hash.detect(h) { |k, v| v == 9 })   # => nil
```

```ruby error
kv = Hash.find(Hash.of("a" => 1)) { |k, v| v == 1 }
k, v = kv                          # !> multiple assignment: argument 1 may be nil
```

## min_by, max_by

`Hash.min_by(x) { }`

`Hash.max_by(x) { }`

The entry for which the block result is smallest or largest, as a Tuple `[k, v]`, or nil for an empty Hash. That nil is the nil of a miss (as `Array.min_by`): using it unchecked is reported only at `--strict=3`. The block results must compare with each other: a block whose result type mixes types that cannot be compared (Integer and String) is rejected statically as a `type` problem, and one whose result may be nil as a `nil` problem; when the checker cannot see it (`Float.NAN`) it is an `ArgumentError` at run time.

```ruby
h = Hash.of("a" => 3, "b" => 1, "c" => 2)
p(Hash.min_by(h) { |k, v| v })     # => ["b", 1]
p(Hash.max_by(h) { |k, v| v })     # => ["a", 3]
p(Hash.max_by(Hash.new) { |k, v| v })   # => nil
```

```ruby error
Hash.min_by(Hash.of("a" => 1, "b" => "x")) { |k, v| v }   # !> Hash.min_by: elements compared in order may be (Integer, String), which cannot be compared
```

## sort_by

`Hash.sort_by(x) { }`

The entries in ascending order of the block result, as an Array of `[k, v]` Tuples (an Array, not a Hash, as in Ruby). The block results must compare with each other: a result type that mixes types that cannot be compared is rejected statically (`type`), as with `min_by`. For descending order negate in the block or use `Array.reverse`. A Tuple result sorts in dictionary order ([Tuple](Tuple.md)).

```ruby
h = Hash.of("a" => 3, "b" => 1, "c" => 2)
p(Hash.sort_by(h) { |k, v| v })    # => [["b", 1], ["c", 2], ["a", 3]]
p(Hash.sort_by(h) { |k, v| -v })   # => [["a", 3], ["c", 2], ["b", 1]]
```

```ruby error
Hash.sort_by(Hash.of("a" => 1, "b" => "x")) { |k, v| v }   # !> Hash.sort_by: elements compared in order may be (Integer, String), which cannot be compared
```

## any?, all?, none?

`Hash.any?(x) { }`

`Hash.all?(x) { }`

`Hash.none?(x) { }`

Whether the block returns true for at least one entry (`any?`), for every entry (`all?`), or for none (`none?`). The block is required (Ruby's blockless forms do not exist and are a static error; for emptiness use `empty?`). On an empty Hash `any?` is false and `all?` and `none?` are true.

```ruby
h = Hash.of("a" => 1, "b" => 2)
p(Hash.any?(h) { |k, v| v > 1 })   # => true
p(Hash.all?(h) { |k, v| v > 1 })   # => false
p(Hash.none?(h) { |k, v| v > 5 })  # => true
```

```ruby error
p(Hash.any?(Hash.of("a" => 1)))       # !> Hash.any? requires a block
```

## one?

`Hash.one?(x) { }`

True when the block returns true for exactly one entry. The block is required.

```ruby
h = Hash.of("a" => 1, "b" => 2)
p(Hash.one?(h) { |k, v| v == 2 })  # => true
p(Hash.one?(h) { |k, v| v > 0 })   # => false
```

## sum

`Hash.sum(x, [Integer|Float|Rational|Complex]) { }`

Calls the block for each entry and adds the results to `init` (0 when omitted). The block is required (Ruby's blockless form does not exist), and `init` must be a number (a String is a `type` problem statically; to join strings use `reduce`). A block result that is not a number is a `TypeError` at run time (`String can't be coerced into Integer`). The result type follows from `init` and the numeric type of the block results.

```ruby
h = Hash.of("a" => 1, "b" => 2)
p(Hash.sum(h) { |k, v| v })        # => 3
p(Hash.sum(h, 0.5) { |k, v| v })   # => 3.5
p(Hash.sum(Hash.new) { |k, v| 1 })   # => 0
```

```ruby error
Hash.sum(Hash.of("a" => "x"), "") { |k, v| v }   # !> argument 2 must be Integer|Float|Rational|Complex, but is String
```

## reduce, inject

`Hash.reduce(x, Any) { }`

`Hash.inject(x, Any) { }`

Starting from `init`, calls the block as `|acc, (k, v)|` for each entry, feeds the result in as the next `acc`, and returns the last result. The initial value is required (Ruby's form without one does not exist). The result type is the union of the types of `init` and of the block results.

```ruby
h = Hash.of("a" => 1, "b" => 2)
p(Hash.reduce(h, 0) { |acc, (k, v)| acc + v })    # => 3
p(Hash.inject(h, "") { |acc, (k, v)| acc + k })   # => "ab"
```

## transform_values

`Hash.transform_values(x) { }`

A new Hash whose values are the block applied to each value. The block receives only the value. The keys stay, and the original does not change. The result's value type is the block's result type, so this is the form for a conversion that changes the type (unlike the in-place `transform_values!`, it does not make a union with the old type).

```ruby
h = Hash.of("a" => 1, "b" => 2)
p(Hash.transform_values(h) { |v| Integer.to_s(v) })   # => {"a" => "1", "b" => "2"}
p(h)                                                 # => {"a" => 1, "b" => 2}
```

## transform_keys

`Hash.transform_keys(x) { }`

A new Hash whose keys are the block applied to each key. The block receives only the key, and its result must be a value that can be a key (`TypeError`). When two keys map to the same result, the later entry's value stays. The original does not change. Ruby's Hash-argument form (`transform_keys(a: :b)`) does not exist.

```ruby
h = Hash.of("a" => 1, "b" => 2)
p(Hash.transform_keys(h) { |k| String.to_sym(k) })   # => {a: 1, b: 2}
p(Hash.transform_keys(Hash.of("a" => 1, "A" => 2)) { |k| String.downcase(k) })   # => {"a" => 2}
```

## transform_values!

`Hash.transform_values!(x) { }`

Replaces each value with the block's result and returns the Hash itself. The checker gives a Hash one value type, so a conversion that changes the type (Integer to String) makes the Hash's value type the union `Integer | String`, and operations that fit only one of them are reported from then on as `type` (partial). To change the type, make a new Hash with `transform_values`.

```ruby
h = Hash.of("a" => 1, "b" => 2)
r = Hash.transform_values!(h) { |v| v * 10 }
p(h)                               # => {"a" => 10, "b" => 20}
p(Kernel.equal?(r, h))             # => true
```

```ruby error
h = Hash.of("a" => 1)
Hash.transform_values!(h) { |v| Integer.to_s(v) }   # !> argument 1 must be Integer, but can be String
```

## transform_keys!

`Hash.transform_keys!(x) { }`

Replaces each key with the block's result and returns the Hash itself. The result must be a value that can be a key (`TypeError`). As with `transform_values!`, a conversion that changes the type makes the key type a union, and an operation for one of the types, in `Array.each(Hash.keys(h))` say, is a `type` problem. To change the type use `transform_keys`.

```ruby
h = Hash.of("a" => 1, "b" => 2)
Hash.transform_keys!(h) { |k| String.upcase(k) }
p(h)                               # => {"A" => 1, "B" => 2}
```

```ruby error
h = Hash.of("a" => 1)
Hash.transform_keys!(h) { |k| String.to_sym(k) }   # !> argument 1 must be String, but can be Symbol
```

## merge

`Hash.merge(x, Hash)`

A new Hash with the entries of both; for a shared key the second argument's value wins. Neither changes. The second argument must be a Hash; passing a Record (`{a: 2}`) is a `type` problem statically. Ruby's block form (deciding the value on a conflict) does not exist. The result's key and value types are the unions of both.

```ruby
a = Hash.of("a" => 1, "b" => 2)
b = Hash.of("b" => 20, "c" => 3)
p(Hash.merge(a, b))                # => {"a" => 1, "b" => 20, "c" => 3}
p(a)                               # => {"a" => 1, "b" => 2}
```

```ruby error
p(Hash.merge(Hash.of("a" => 1), {a: 2}))   # !> Hash.merge: argument 2 must be Hash, but is {a: Integer}
```

## merge!, update

`Hash.merge!(x, Hash)`

`Hash.update(x, Hash)`

Writes the second argument's entries into the first Hash (overwriting shared keys) and returns the first Hash itself. The checker adds the second argument's key and value types to the first's. There is no block form.

```ruby
h = Hash.of("a" => 1)
Hash.merge!(h, Hash.of("b" => 2))
p(Hash.update(h, Hash.of("a" => 10))) # => {"a" => 10, "b" => 2}
p(h)                               # => {"a" => 10, "b" => 2}
```

## replace

`Hash.replace(x, Hash)`

Replaces the whole contents of the first Hash with the second's entries and returns the first Hash itself.

```ruby
h = Hash.of("a" => 1, "b" => 2)
p(Hash.replace(h, Hash.of("z" => 26)))   # => {"z" => 26}
p(h)                                  # => {"z" => 26}
```

## compact

`Hash.compact(x)`

A new Hash without the entries whose value is nil. nil leaves the result's value type.

```ruby
h = Hash.of("a" => 1, "b" => nil)
p(Hash.compact(h))                 # => {"a" => 1}
p(h)                               # => {"a" => 1, "b" => nil}
```

## compact!

`Hash.compact!(x)`

Removes the entries whose value is nil in place and returns the Hash itself, or nil when nothing was removed (as Ruby's); using the result unchecked is reported at level 2.

```ruby
h = Hash.of("a" => 1, "b" => nil)
p(Hash.compact!(h))                # => {"a" => 1}
p(Hash.compact!(h))                # => nil
```

```ruby error
p(Hash.size(Hash.compact!(Hash.of("a" => 1))))   # !> Hash.size: argument 1 may be nil
```

## slice

`Hash.slice(x, *Any)`

A new Hash of the entries under the listed keys only. Missing keys are ignored; the order is the order listed.

```ruby
h = Hash.of("a" => 1, "b" => 2, "c" => 3)
p(Hash.slice(h, "c", "a", "zz"))   # => {"c" => 3, "a" => 1}
```

## except

`Hash.except(x, *Any)`

A new Hash without the entries under the listed keys. Missing keys are ignored.

```ruby
h = Hash.of("a" => 1, "b" => 2, "c" => 3)
p(Hash.except(h, "a", "zz"))       # => {"b" => 2, "c" => 3}
```

## invert

`Hash.invert(x)`

A new Hash with the values as keys and the keys as values. The values must be able to be keys (`TypeError`); when a value occurs more than once, the later entry stays. The result's key and value types are the original's value and key types.

```ruby
p(Hash.invert(Hash.of("a" => 1, "b" => 2)))        # => {1 => "a", 2 => "b"}
p(Hash.invert(Hash.of("a" => 1, "b" => 1)))        # => {1 => "b"}
```

```ruby error
Hash.invert(Hash.of("a" => /x/))      # !> TypeError: Hash.invert: Regexp cannot be a Hash key or Set element
```

## delete

`Hash.delete(x, Any)`

Removes the entry under key `k` and returns its value, or nil when there is none, which is reported at level 2 (the default is not used). Ruby's block form does not exist.

```ruby
h = Hash.of("a" => 1, "b" => 2)
p(Hash.delete(h, "a"))             # => 1
p(Hash.delete(h, "zz"))            # => nil
p(h)                               # => {"b" => 2}
```

```ruby error
h = Hash.of("a" => 1)
p(Hash.delete(h, "a") + 1)         # !> the operands may be nil
```

## delete_if

`Hash.delete_if(x) { }`

Removes in place the entries for which the block returns true and returns the Hash itself (never nil, even when nothing was removed; that is the difference from `reject!`).

```ruby
h = Hash.of("a" => 1, "b" => 2, "c" => 3)
p(Hash.delete_if(h) { |k, v| v > 1 })   # => {"a" => 1}
p(Hash.delete_if(h) { |k, v| false })   # => {"a" => 1}
```

## keep_if

`Hash.keep_if(x) { }`

Keeps in place only the entries for which the block returns true and returns the Hash itself (never nil, even when nothing changed; that is the difference from `select!`).

```ruby
h = Hash.of("a" => 1, "b" => 2, "c" => 3)
p(Hash.keep_if(h) { |k, v| v > 1 })   # => {"b" => 2, "c" => 3}
```

## select!, filter!

`Hash.select!(x) { }`

`Hash.filter!(x) { }`

Keep in place only the entries for which the block returns true and return the Hash itself, or nil when nothing was removed (as Ruby's); using the result unchecked is reported at level 2. The form that always returns the Hash is `keep_if`.

```ruby
h = Hash.of("a" => 1, "b" => 2, "c" => 3)
p(Hash.select!(h) { |k, v| v > 1 })   # => {"b" => 2, "c" => 3}
p(Hash.filter!(h) { |k, v| v > 1 })   # => nil
p(h)                                  # => {"b" => 2, "c" => 3}
```

## reject!

`Hash.reject!(x) { }`

Removes in place the entries for which the block returns true and returns the Hash itself, or nil when nothing was removed (reported at level 2). The form that always returns the Hash is `delete_if`.

```ruby
h = Hash.of("a" => 1, "b" => 2, "c" => 3)
p(Hash.reject!(h) { |k, v| v > 2 })   # => {"a" => 1, "b" => 2}
p(Hash.reject!(h) { |k, v| v > 5 })   # => nil
```

## shift

`Hash.shift(x)`

Removes the first inserted entry and returns it as a Tuple `[k, v]`, or nil when the Hash is empty (the default is not used; reported at level 2).

```ruby
h = Hash.of("a" => 1, "b" => 2)
p(Hash.shift(h))                   # => ["a", 1]
p(h)                               # => {"b" => 2}
Hash.shift(h)
p(Hash.shift(h))                   # => nil
```

## clear

`Hash.clear(x)`

Removes all entries and returns the Hash itself (now empty). The default is kept.

```ruby
h = Hash.of("a" => 1)
p(Hash.clear(h))                   # => {}
p(Hash.empty?(h))                  # => true
```

## ==, !=

`Hash.==(x, Any)`

`Hash.!=(x, Any)`

True when the two Hashes have the same set of keys and the values under each key are `==` (`!=` is the negation). Insertion order does not matter, and neither does the default. A right operand that is not a Hash (nil, an Integer, an Array) is never equal, with the function form as with the operator: `Hash.==(h, nil)` is false. A Record `{a: 1}` and `Hash.of(a: 1)` differ in type and are not equal.

```ruby
a = Hash.of("a" => 1, "b" => 2)
p(a == Hash.of("b" => 2, "a" => 1))   # => true
p(a != Hash.of("a" => 1))             # => true
p(Hash.==(a, Hash.new))              # => false
p(Hash.==(a, nil))                 # => false
p(Hash.!=(a, 1))                   # => true
p(Hash.of(a: 1) == {a: 1})            # => false
```

# Record

A Record is a set of (field name, value) pairs, made by the literal `{x: 1, y: "a"}` ([Values and types](../03-values.md)). In Ruby that spelling is a Hash; in Sake it is a **Record, not a Hash**. A Hash is made by `Hash.of(x: 1)` ([Hash](Hash.md)). `{}` (empty) and `{"a" => 1}` (the `=>` form) are rejected statically.

A Record's type is its set of (field, type) pairs, written `{x: Integer, y: String}`. Records with the same set have the same type. Field order is not part of the type: `{y: 2, x: 1}` prints as `{x: 1, y: 2}`. A Record is never a value of a class (classes are told apart by name, Record types by structure; see [Structs](../07-classes.md)).

Fields are read with a pattern: `r => {x:, y: name}` binds the local `x` to field `x` and `name` to field `y`. Listing only some of the fields is allowed, and `case r in {x:} ... end` is the same pattern. Naming a field the Record does not have is a `type` problem statically (a `KeyError` at run time), and so is matching a value that is not a Record (a `TypeError` at run time). Writing to a field (`r.x = v`, `r[:x] = v`) does not exist yet (both are rejected statically; `Indexable.[]=` does not take a Record).

The operators on a Record are `==` and `!=` (true when the field sets are the same and each value is equal). There is no index `r[:x]`.

The three operations of this chapter treat a Record as data (for code that walks any Record). Their argument type is `Any`; anything but a Record is a `TypeError` at run time. The fields come out in field-name order (the order they print in).

## keys

`Record.keys(Any)`

A new Array of the field names as Symbols. The order is field-name order, not the order written in the literal.

```ruby
r = {x: 1, y: "a"}
p(Record.keys(r))                  # => [:x, :y]
p(Record.keys({y: 2, x: 1}))       # => [:x, :y]
```

```ruby error
p(Record.keys(Hash.of(x: 1)))         # !> TypeError: Record.keys: argument 1 must be a Record, got Hash
```

## values

`Record.values(Any)`

A new Array of the field values, in the order of `keys`. It is independent of the Record, and the Array can grow. Its element type is the union of the field types.

```ruby
r = {x: 1, y: "a"}
vs = Record.values(r)
p(vs)                              # => [1, "a"]
Array.push(vs, 3)
p(r)                               # => {x: 1, y: "a"}
```

## to_h

`Record.to_h(Any)`

A new Hash with the field names as Symbol keys. To the checker its key type is Symbol and its value type is the union of the field types, so for a Record whose fields differ in type, using `h[:x]` directly in arithmetic is a `type` problem (a Hash has only one value type). When the fields share a type it is an ordinary Hash. A nested Record stays a Record as a value.

```ruby
r = {x: 1, y: 2}
h = Record.to_h(r)
p(h)                               # => {x: 1, y: 2}
p(Hash.fetch(h, :x) + Hash.fetch(h, :y))   # => 3
p(Record.to_h({name: "a", pos: {x: 1}}))   # => {name: "a", pos: {x: 1}}
```

```ruby error
h = Record.to_h({x: 1, y: "a"})
p(Hash.fetch(h, :x) + 1)           # !> which the left operand's type does not support
```

# Set

A Set is a collection of elements without duplicates. `Set.of(1, 2)` makes a Set, and `Set.new(xs)` one of an Array's elements, as Ruby's `Set.new([1, 2])`. Elements are told apart by the rules of Hash keys: Integer, Float, String, Symbol, true, false, nil, Time, and Tuples, Records, Arrays, Hashes, Sets and values of a class made of these can be elements; a Regexp, a Range, or a value of a class that defines its own equality (`==` or `Comparable`) is a `TypeError` at run time. Whether two values are the same element is decided as Ruby's Hash does, by `hash` and `eql?`, so `1` and `1.0` are two elements. Elements keep the order they were added in, and iteration follows it. A String used as an element is copied in, so changing the original String afterwards does not change the Set.

The checker follows the element type of a Set per construction site and widens it on writes such as `Set.add`. The block of an operation that takes one receives values of that element type.

The operators on Sets are `|` (union), `&` (intersection), `-` (difference), `==` and `!=`. `^`, `+`, `<`, `<=` and the like do not apply; subset tests are `Set.subset?` and its relatives. The entries `Set.|(x, y)` and so on below are the function forms of the operators ([Operators and indexing](../05-operators.md)). Most operations that pick elements out (`select`, `map`, `sort`, `to_a`, ...) return an **Array**, not a Set; this differs from Ruby's `Set#select`, which returns a Set.

## of

`Set.of(*Any)`

Makes a Set of the listed elements. Duplicate elements collapse into one. A value that cannot be an element (a Regexp, a Range, a class instance with its own equality) is a `TypeError` at run time. `Set.new` is the empty Set; elements added later with `Set.add` widen its element type.

```ruby
s = Set.of(1, 2, 2, 3)
p(s)                          # => Set[1, 2, 3]
p(Set.new)                      # => Set[]
p(Set.size(Set.of(1, 1.0)))      # => 2
```

```ruby error
p(Set.of(/a/))                   # !> TypeError: Set.of: Regexp cannot be a Hash key or Set element
```

## new

`Set.new([Array])`

Makes a Set of the elements of an Array, as Ruby's `Set.new([1, 2])`; `Set.new` with no argument is the empty Set. Duplicates collapse, and a value that cannot be an element is a `TypeError`.

```ruby
p(Set.new(Any[3, 1, 3]))         # => Set[3, 1]
p(Set.new)                       # => Set[]
```

## Set[]

`Set[*Any]`

Makes an Array of Sets, as `T[...]` does for every T: each element must be a Set, checked on every write.

```ruby
ss = Set[Set.of(1), Set.new]
p(ss)                            # => [Set[1], Set[]]
```

## length, size

`Set.length(x)`

`Set.size(x)`

The number of elements (an Integer).

```ruby
p(Set.size(Set.of(1, 2, 3)))     # => 3
p(Set.length(Set.new))          # => 0
```

## empty?

`Set.empty?(x)`

True when the Set has no elements.

```ruby
p(Set.empty?(Set.new))          # => true
p(Set.empty?(Set.of(1)))         # => false
```

## include?, member?

`Set.include?(x, Any)`

`Set.member?(x, Any)`

True when the value is an element. Elements compare as Hash keys do (`hash` and `eql?`), so a Set holding `1` does not hold `1.0`. Asking for a value of a type other than the Set's element type is not a static problem; the answer is false.

```ruby
s = Set.of(1, 2)
p(Set.include?(s, 1))         # => true
p(Set.include?(s, 1.0))       # => false
p(Set.member?(s, "a"))        # => false
p(Set.include?(Set.of([1, 2]), [1, 2]))   # => true
```

## add

`Set.add(x, Any)`

Adds the value as an element and returns the Set itself (in place). A value already present changes nothing. A value that cannot be an element is a `TypeError` at run time. The type of the added value joins the Set's element type.

```ruby
s = Set.of(1)
p(Set.add(s, 2))              # => Set[1, 2]
p(Set.add(s, 2))              # => Set[1, 2]
p(s)                          # => Set[1, 2]
```

```ruby error
Set.add(Set.of(1), 1..2)         # !> TypeError: Set.add: Range cannot be a Hash key or Set element
```

## add?

`Set.add?(x, Any)`

As `add`, but returns **nil** when the value was already present (the Set itself when it was newly added). The result is a Set or nil, so `--strict` (level 2) reports using it unchecked. Use it to ask "is this the first time I see this value?".

```ruby
s = Set.of(1)
p(Set.add?(s, 2))             # => Set[1, 2]
p(Set.add?(s, 2))             # => nil
```

```ruby error
s = Set.of(1)
x = Set.add?(s, 2)
p(Set.size(x))                # !> argument 1 may be nil
```

## delete

`Set.delete(x, Any)`

Removes the value from the elements and returns the Set itself (in place). A value that is not present changes nothing. As Ruby's.

```ruby
s = Set.of(1, 2, 3)
p(Set.delete(s, 2))           # => Set[1, 3]
p(Set.delete(s, 99))          # => Set[1, 3]
```

## delete?

`Set.delete?(x, Any)`

As `delete`, but returns **nil** when the value was not present (the Set itself when it was removed). `--strict` (level 2) reports using the result unchecked.

```ruby
s = Set.of(1, 2)
p(Set.delete?(s, 2))          # => Set[1]
p(Set.delete?(s, 2))          # => nil
```

## merge

`Set.merge(x, Set)`

Adds every element of the other Set and returns the Set itself (in place). The argument must be a Set (an Array is a `type` problem statically; Ruby's `merge` takes any enumerable). For a new Set use `|` or `union`.

```ruby
s = Set.of(1, 2)
p(Set.merge(s, Set.of(2, 3)))    # => Set[1, 2, 3]
p(s)                          # => Set[1, 2, 3]
```

```ruby error
Set.merge(Set.of(1), Any[2])   # !> argument 2 must be Set, but is Array
```

## subtract

`Set.subtract(x, Any)`

Removes every element contained in the argument and returns the Set itself (in place). The argument is a Set, an Array, a Tuple, or a Range; the checker does not check it (the signature says `Any`), and anything else is a `TypeError` at run time (`argument 2 must be Set, Array, Tuple, or Range, got Integer`). For a new Set use `-` or `difference`.

```ruby
s = Set.of(1, 2, 3, 4, 5)
p(Set.subtract(s, Set.of(1)))    # => Set[2, 3, 4, 5]
p(Set.subtract(s, Any[2]))  # => Set[3, 4, 5]
p(Set.subtract(s, [3]))       # => Set[4, 5]
p(Set.subtract(s, 4..4))      # => Set[5]
p(s)                          # => Set[5]
```

```ruby error
Set.subtract(Set.of(1, 2), 1)    # !> TypeError: Set.subtract: argument 2 must be Set, Array, Tuple, or Range, got Integer
```

## replace

`Set.replace(x, Set)`

Replaces all elements with those of the other Set and returns the Set itself (in place). The other Set's element type joins this Set's.

```ruby
s = Set.of(1, 2)
p(Set.replace(s, Set.of(7, 8)))  # => Set[7, 8]
p(s)                          # => Set[7, 8]
```

## clear

`Set.clear(x)`

Removes all elements and returns the now empty Set itself.

```ruby
s = Set.of(1, 2)
p(Set.clear(s))               # => Set[]
p(Set.empty?(s))              # => true
```

## |, union

`Set.|(x, Any)`

`Set.union(x, Set)`

The union: a **new** Set with the elements of both. `Set.|` is the function form of `a | b`. The right operand must be a Set; an Array or other value is a `type` problem statically. The result's element type is the union of both element types.

```ruby
a = Set.of(1, 2)
b = Set.of(2, 3)
p(a | b)                      # => Set[1, 2, 3]
p(Set.union(a, b))            # => Set[1, 2, 3]
p(a)                          # => Set[1, 2]
```

```ruby error
p(Set.of(1) | Any[2])          # !> Bitwise.|: the operands are (Set@L1[Integer], Array@L1[Integer])
```

## &, intersection

`Set.&(x, Any)`

`Set.intersection(x, Set)`

The intersection: a **new** Set with only the elements present in both. `Set.&` is the function form of `a & b`. The right operand must be a Set.

```ruby
a = Set.of(1, 2, 3)
b = Set.of(2, 3, 4)
p(a & b)                      # => Set[2, 3]
p(Set.intersection(a, b))     # => Set[2, 3]
```

## -, difference

`Set.-(x, Any)`

`Set.difference(x, Set)`

The difference: a **new** Set with the elements of the left operand that are not in the right one. `Set.-` is the function form of `a - b`. The right operand must be a Set.

```ruby
a = Set.of(1, 2, 3)
b = Set.of(2)
p(a - b)                      # => Set[1, 3]
p(Set.difference(a, b))       # => Set[1, 3]
p(a)                          # => Set[1, 2, 3]
```

## ==, !=

`Set.==(x, Any)`

`Set.!=(x, Any)`

True when the two Sets have the same elements, in any order (`!=` is the negation). A right operand that is not a Set (an Array, nil) is never equal.

```ruby
p(Set.of(1, 2) == Set.of(2, 1))     # => true
p(Set.of(1, 2) == Set.of(1))        # => false
p(Set.of(1) != Set.of(1))           # => false
p(Set.of(1, 2) == Any[1, 2])   # => false
```

## subset?, superset?

`Set.subset?(x, Set)`

`Set.superset?(x, Set)`

`subset?` is true when every element of x is in the argument Set; `superset?` is the reverse. Equal Sets answer true to both. The argument must be a Set (an Array is a `type` problem statically). Ruby's `<=` and `>=` operators do not apply to Sets; use these functions.

```ruby
a = Set.of(1, 2)
b = Set.of(1, 2, 3)
p(Set.subset?(a, b))          # => true
p(Set.subset?(a, a))          # => true
p(Set.superset?(b, a))        # => true
p(Set.superset?(a, b))        # => false
```

```ruby error
p(Set.of(1) <= Set.of(1, 2))        # !> Comparable.<=: the operands are (Set@L1[Integer], Set@L1[Integer])
```

## proper_subset?, proper_superset?

`Set.proper_subset?(x, Set)`

`Set.proper_superset?(x, Set)`

Proper subset tests: as `subset?` and `superset?`, but equal Sets answer false (Ruby's `<` and `>`).

```ruby
a = Set.of(1, 2)
b = Set.of(1, 2, 3)
p(Set.proper_subset?(a, b))   # => true
p(Set.proper_subset?(a, a))   # => false
p(Set.proper_superset?(b, a)) # => true
```

## disjoint?, intersect?

`Set.disjoint?(x, Set)`

`Set.intersect?(x, Set)`

`intersect?` is true when the two Sets share at least one element; `disjoint?` is its negation.

```ruby
p(Set.intersect?(Set.of(1, 2), Set.of(2, 3)))   # => true
p(Set.disjoint?(Set.of(1, 2), Set.of(2, 3)))    # => false
p(Set.disjoint?(Set.of(1), Set.of(9)))          # => true
```

## each, each_entry

`Set.each(x) { }`

`Set.each_entry(x) { }`

Passes the elements to the block one at a time, in the order they were added, and returns the Set itself. The block is required (there is no block-less form giving an Enumerator as in Ruby).

```ruby
s = Set.of(3, 1, 2)
Set.each(s) { |x| puts(x) }
# => 3
# => 1
# => 2
p(Set.each_entry(s) { |x| x })   # => Set[3, 1, 2]
```

## each_with_index

`Set.each_with_index(x) { }`

Passes each element and its position (from 0) to the block and returns the Set itself.

```ruby
Set.each_with_index(Set.of("a", "b")) { |x, i| puts("#{i}: #{x}") }
# => 0: a
# => 1: b
```

## each_with_object

`Set.each_with_object(x, Any) { }`

Passes each element and the second argument to the block, and returns that second argument. The usual form passes an Array or Hash to accumulate into.

```ruby
a = Set.each_with_object(Set.of(1, 2), Any[]) { |x, acc| Array.push(acc, x * 2) }
p(a)                          # => [2, 4]
```

## reverse_each

`Set.reverse_each(x) { }`

Passes the elements to the block in the reverse of the order they were added, and returns the Set itself.

```ruby
Set.reverse_each(Set.of(1, 2, 3)) { |x| puts(x) }
# => 3
# => 2
# => 1
```

## each_slice, each_cons

`Set.each_slice(x, Integer) { }`

`Set.each_cons(x, Integer) { }`

`each_slice` cuts the elements into Arrays of n (the last may be shorter) and passes each to the block; `each_cons` passes each run of n consecutive elements as an Array, moving one element at a time. Both return the Set itself. An n of 0 or less is an `ArgumentError`.

```ruby
s = Set.of(1, 2, 3, 4)
Set.each_slice(s, 3) { |a| p(a) }
# => [1, 2, 3]
# => [4]
Set.each_cons(s, 3) { |a| p(a) }
# => [1, 2, 3]
# => [2, 3, 4]
```

```ruby error
Set.each_slice(Set.of(1), 0) { |a| p(a) }   # !> ArgumentError: Set.each_slice: invalid slice size
```

## cycle

`Set.cycle(x, Integer) { }`

Passes the sequence of elements to the block n times over and returns **nil**. The count is required (there is no endless `cycle` as in Ruby). An n of 0 or less does nothing.

```ruby
Set.cycle(Set.of(1, 2), 2) { |x| print(x) }
puts("")
# => 1212
p(Set.cycle(Set.of(1), 0) { |x| p(x) })   # => nil
```

## map, collect

`Set.map(x) { }`

`Set.collect(x) { }`

The **Array** of the block's results for each element (not a Set: in element order, duplicates kept). Its element type is the type of the block's result.

```ruby
p(Set.map(Set.of(1, 2, 3)) { |x| x * 10 })        # => [10, 20, 30]
p(Set.collect(Set.of(1, 2, 3)) { |x| x % 2 })     # => [1, 0, 1]
```

## map!, collect!

`Set.map!(x) { }`

`Set.collect!(x) { }`

Replaces each element with the block's result and returns the Set itself (in place). Results that coincide leave fewer elements. The block's result type joins the element type. A result that cannot be an element is a `TypeError` at run time.

```ruby
s = Set.of(1, 2, 3)
p(Set.map!(s) { |x| x % 2 })  # => Set[1, 0]
p(s)                          # => Set[1, 0]
```

## flat_map, collect_concat

`Set.flat_map(x) { }`

`Set.collect_concat(x) { }`

The block returns an **Array or a Tuple** for each element, and the result is the one Array of all of them joined. A block result that is neither is a `TypeError` at run time (`the block must return an Array or a Tuple, got Integer`; the value is not kept as is, as Ruby would).

```ruby
p(Set.flat_map(Set.of(1, 2)) { |x| Any[x, x * 10] })   # => [1, 10, 2, 20]
p(Set.flat_map(Set.of(1, 2)) { |x| [x, x * 10] })        # => [1, 10, 2, 20]
```

```ruby error
Set.flat_map(Set.of(1)) { |x| x }    # !> TypeError: Set.flat_map: the block must return an Array or a Tuple, got Integer
```

## select, filter, find_all

`Set.select(x) { }`

`Set.filter(x) { }`

`Set.find_all(x) { }`

The **Array** of the elements for which the block is truthy (Ruby's `Set#select` returns a Set; in Sake it is an Array). For a Set, narrow in place with `select!`, or turn the Array back with `Array.to_set`.

```ruby
s = Set.of(1, 2, 3, 4)
p(Set.select(s) { |x| Integer.even?(x) })      # => [2, 4]
p(Set.filter(s) { |x| x > 3 })                 # => [4]
p(Set.find_all(s) { |x| x < 0 })               # => []
```

## reject

`Set.reject(x) { }`

The **Array** of the elements for which the block is falsy (the reverse of `select`).

```ruby
p(Set.reject(Set.of(1, 2, 3, 4)) { |x| Integer.even?(x) })   # => [1, 3]
```

## select!, filter!, reject!

`Set.select!(x) { }`

`Set.filter!(x) { }`

`Set.reject!(x) { }`

`select!` and `filter!` remove the elements for which the block is falsy, `reject!` those for which it is truthy, in place. They return the Set itself when something was removed and **nil when nothing changed** (as Ruby's). `--strict` (level 2) reports using the result unchecked. The forms that always return the Set are `keep_if` and `delete_if`.

```ruby
s = Set.of(1, 2, 3, 4)
p(Set.select!(s) { |x| x > 1 })    # => Set[2, 3, 4]
p(Set.select!(s) { |x| x > 1 })    # => nil
p(Set.reject!(s) { |x| x > 3 })    # => Set[2, 3]
p(Set.filter!(s) { |x| x < 9 })    # => nil
p(s)                               # => Set[2, 3]
```

## keep_if, delete_if

`Set.keep_if(x) { }`

`Set.delete_if(x) { }`

`keep_if` keeps only the elements for which the block is truthy; `delete_if` removes those (in place). Both always return the Set itself.

```ruby
s = Set.of(1, 2, 3, 4)
p(Set.keep_if(s) { |x| x > 1 })        # => Set[2, 3, 4]
p(Set.delete_if(s) { |x| x > 3 })      # => Set[2, 3]
p(Set.delete_if(s) { |x| x > 9 })      # => Set[2, 3]
```

## filter_map

`Set.filter_map(x) { }`

Applies the block to each element and collects the results that are neither nil nor false, as an Array. The result's element type has nil removed.

```ruby
p(Set.filter_map(Set.of(1, 2, 3, 4)) { |x| Integer.even?(x) ? x * 10 : nil })   # => [20, 40]
```

## partition

`Set.partition(x) { }`

The Tuple `[yes, no]` of the Array of elements for which the block is truthy and the Array of the others.

```ruby
ev, od = Set.partition(Set.of(1, 2, 3, 4)) { |x| Integer.even?(x) }
p(ev)                         # => [2, 4]
p(od)                         # => [1, 3]
```

## group_by

`Set.group_by(x) { }`

A Hash from each block result to the Array of the elements that gave it. A block result that cannot be a key is a `TypeError` at run time.

```ruby
p(Set.group_by(Set.of(1, 2, 3, 4)) { |x| x % 2 })    # => {1 => [1, 3], 0 => [2, 4]}
```

## classify

`Set.classify(x) { }`

The same grouping as `group_by`, but the Hash's values are **Sets** rather than Arrays (Ruby's `Set#classify`).

```ruby
p(Set.classify(Set.of(1, 2, 3, 4)) { |x| x % 2 })    # => {1 => Set[1, 3], 0 => Set[2, 4]}
```

## tally

`Set.tally(x)`

A Hash from each element to its count. A Set has no duplicates, so every value is 1 (the operation comes from Array and exists for uniformity).

```ruby
p(Set.tally(Set.of("a", "b")))   # => {"a" => 1, "b" => 1}
```

## chunk_while, slice_when

`Set.chunk_while(x) { }`

`Set.slice_when(x) { }`

Pass each pair of adjacent elements to the block; `chunk_while` keeps a run together while the block is truthy, `slice_when` cuts where it is truthy. The result is an Array of Arrays.

```ruby
s = Set.of(1, 2, 4, 5, 7)
p(Set.chunk_while(s) { |a, b| b == a + 1 })   # => [[1, 2], [4, 5], [7]]
p(Set.slice_when(s) { |a, b| b > a + 1 })     # => [[1, 2], [4, 5], [7]]
```

## slice_before, slice_after

`Set.slice_before(x) { }`

`Set.slice_after(x) { }`

An Array of Arrays cut just before (`slice_before`) or just after (`slice_after`) each element for which the block is truthy.

```ruby
s = Set.of(1, 2, 3, 4)
p(Set.slice_before(s) { |x| x == 3 })   # => [[1, 2], [3, 4]]
p(Set.slice_after(s) { |x| x == 2 })    # => [[1, 2], [3, 4]]
```

## find, detect

`Set.find(x) { }`

`Set.detect(x) { }`

The first element for which the block is truthy. When no element is, the result is **nil**, and `--strict` (level 2) reports using it unchecked.

```ruby
s = Set.of(1, 2, 3)
p(Set.find(s) { |x| x > 1 })      # => 2
p(Set.detect(s) { |x| x > 9 })    # => nil
```

```ruby error
x = Set.find(Set.of(1)) { |v| v > 0 }
p(x + 1)                          # !> the operands may be nil
```

## find_index

`Set.find_index(x) { }`

The position (an Integer, from 0 in the order added) of the first element for which the block is truthy, or **nil** when there is none (reported at `--strict` level 2).

```ruby
s = Set.of("a", "b", "c")
p(Set.find_index(s) { |x| x == "c" })   # => 2
p(Set.find_index(s) { |x| x == "z" })   # => nil
```

## any?, all?, none?, one?

`Set.any?(x) { }`

`Set.all?(x) { }`

`Set.none?(x) { }`

`Set.one?(x) { }`

True when the block is truthy for at least one element, for all of them, for none, or for exactly one. The block is required (Ruby's block-less forms do not exist; emptiness is `empty?`).

```ruby
s = Set.of(1, 2, 3)
p(Set.any?(s) { |x| x > 2 })      # => true
p(Set.all?(s) { |x| x > 2 })      # => false
p(Set.none?(s) { |x| x > 9 })     # => true
p(Set.one?(s) { |x| x == 1 })     # => true
```

```ruby error
p(Set.any?(Set.of(1)))               # !> Set.any? requires a block
```

## count

`Set.count(x) [{ }]`

Without a block, the number of elements; with one, the number of elements for which the block is truthy (an Integer).

```ruby
s = Set.of(1, 2, 3)
p(Set.count(s))                   # => 3
p(Set.count(s) { |x| x > 1 })     # => 2
```

## sum

`Set.sum(x, [Integer|Float|Rational|Complex])`

The sum of the elements: the initial value (0 by default) plus each element in turn. Elements and the initial value must be numbers: a String or other initial value is a `type` problem statically, and an element that is not a number is a `TypeError` at run time (`String can't be coerced into Integer`; Ruby's `sum("")` for joining Strings does not exist; use `join`). The empty Set gives the initial value.

```ruby
p(Set.sum(Set.of(1, 2, 3)))          # => 6
p(Set.sum(Set.of(1, 2), 0.5))        # => 3.5
p(Set.sum(Set.new))                 # => 0
```

```ruby error
p(Set.sum(Set.of("a", "b"), ""))     # !> argument 2 must be Integer|Float|Rational|Complex, but is String
```

## reduce, inject

`Set.reduce(x, Any) { }`

`Set.inject(x, Any) { }`

Folds the elements: starting from the initial value, the block receives the accumulated value and an element. The initial value is **required** (Ruby's form that starts from the first element does not exist). The result type follows from the initial value and the block's result.

```ruby
p(Set.reduce(Set.of(1, 2, 3), 0) { |acc, x| acc + x })     # => 6
p(Set.inject(Set.of(1, 2, 3), 1) { |acc, x| acc * x })     # => 6
```

```ruby error
p(Set.reduce(Set.of(1)) { |a, b| a + b })   # !> wrong number of arguments for Set.reduce (given 1, expected 2)
```

## min, max

`Set.min(x)`

`Set.max(x)`

The smallest or largest element. The elements must be comparable with each other: the checker rejects a Set whose element type mixes types that cannot be compared (`Set.of(1, "a")`) statically as a `type` problem, and an element that may be nil as a `nil` problem. When the checker cannot see it (`Set.of(1.0, Float.NAN)`), it is an `ArgumentError` at run time (`cannot compare the elements`). The empty Set gives **nil**, the nil of a miss, as with `Array.min`: using it unchecked is reported only at `--strict=3`.

```ruby
s = Set.of(3, 1, 2)
p(Set.min(s))                     # => 1
p(Set.max(s))                     # => 3
p(Set.max(Set.new))                 # => nil
```

```ruby error
p(Set.min(Set.of(1, "a")))           # !> Set.min: elements compared in order may be (Integer, String), which cannot be compared
```

```ruby error
p(Set.min(Set.of(1.0, Float.NAN)))   # !> ArgumentError: Set.min: cannot compare the elements
```

## minmax

`Set.minmax(x)`

The Tuple `[min, max]`. The empty Set gives `[nil, nil]`; those are the nil of a miss, reported only at `--strict=3` when an element is used unchecked. The comparison rules are those of `min` and `max`, static check included.

```ruby
lo, hi = Set.minmax(Set.of(3, 1, 2))
p([lo, hi])                       # => [1, 3]
p(Set.minmax(Set.new))              # => [nil, nil]
```

## min_by, max_by

`Set.min_by(x) { }`

`Set.max_by(x) { }`

The element for which the block's result is smallest or largest. The block's results must be comparable with each other: a block whose result type mixes types that cannot be compared is rejected statically (`type`), as with `min`. The empty Set gives **nil**, the nil of a miss (reported only at `--strict=3`).

```ruby
s = Set.of("bb", "a", "ccc")
p(Set.min_by(s) { |x| String.size(x) })   # => "a"
p(Set.max_by(s) { |x| String.size(x) })   # => "ccc"
p(Set.max_by(Set.new) { |x| x })            # => nil
```

```ruby error
p(Set.min_by(Set.of(1, 2)) { |x| x == 1 ? 1 : "a" })   # !> Set.min_by: elements compared in order may be (Integer, String), which cannot be compared
```

## minmax_by

`Set.minmax_by(x) { }`

The Tuple of the elements with the smallest and the largest block result. The empty Set gives `[nil, nil]` (the nil of a miss, level 3). The block's results are checked as for `min_by`.

```ruby
p(Set.minmax_by(Set.of(1, 2, 3)) { |x| -x })   # => [3, 1]
```

## sort

`Set.sort(x)`

The Array of the elements in ascending order. The elements must be comparable with each other: a Set whose element type mixes types that cannot be compared is rejected statically (`type`), one whose elements may be nil as a `nil` problem; when the checker cannot see it (`Set.of(1.0, Float.NAN)`), `ArgumentError` at run time (`cannot compare the elements`).

```ruby
p(Set.sort(Set.of(3, 1, 2)))         # => [1, 2, 3]
```

```ruby error
p(Set.sort(Set.of(1, "a")))          # !> Set.sort: elements compared in order may be (Integer, String), which cannot be compared
```

## sort_by

`Set.sort_by(x) { }`

The Array of the elements in ascending order of the block's results. A Tuple result sorts in dictionary order. The block's results must be comparable with each other; a result type that mixes types that cannot be compared is rejected statically (`type`).

```ruby
p(Set.sort_by(Set.of("bb", "a", "ccc")) { |x| String.size(x) })   # => ["a", "bb", "ccc"]
p(Set.sort_by(Set.of(1, 2, 3)) { |x| -x })                        # => [3, 2, 1]
```

```ruby error
p(Set.sort_by(Set.of(1, 2)) { |x| x == 1 ? 1 : "a" })   # !> Set.sort_by: elements compared in order may be (Integer, String), which cannot be compared
```

## first

`Set.first(x)`

The element added first. The empty Set gives **nil**, the nil of a miss as with `Array.first`: using it unchecked is reported only at `--strict=3`. There is no form with a count (use `take`).

```ruby
p(Set.first(Set.of(3, 1)))           # => 3
p(Set.first(Set.new))               # => nil
```

## take, drop

`Set.take(x, Integer)`

`Set.drop(x, Integer)`

The Array of the first n elements, or of the elements after the first n. A negative n is an `ArgumentError`.

```ruby
s = Set.of(1, 2, 3, 4)
p(Set.take(s, 2))                 # => [1, 2]
p(Set.drop(s, 2))                 # => [3, 4]
p(Set.take(s, 9))                 # => [1, 2, 3, 4]
```

```ruby error
p(Set.take(Set.of(1), -1))           # !> ArgumentError: Set.take: attempt to take negative size
```

## take_while, drop_while

`Set.take_while(x) { }`

`Set.drop_while(x) { }`

The Array of the leading elements while the block is truthy, or of the rest.

```ruby
s = Set.of(1, 2, 3, 1)
p(Set.take_while(s) { |x| x < 3 })    # => [1, 2]
p(Set.drop_while(s) { |x| x < 3 })    # => [3]
```

## to_a, entries

`Set.to_a(x)`

`Set.entries(x)`

A new Array of the elements in the order they were added. Changing the Array does not change the Set.

```ruby
s = Set.of(3, 1)
a = Set.to_a(s)
Array.push(a, 9)
p(a)                              # => [3, 1, 9]
p(s)                              # => Set[3, 1]
p(Set.entries(s))                 # => [3, 1]
```

## to_set

`Set.to_set(x)`

Returns the Set itself (not a copy). It exists for uniformity with `Array.to_set`.

```ruby
s = Set.of(1)
Set.add(Set.to_set(s), 2)
p(s)                              # => Set[1, 2]
```

## uniq

`Set.uniq(x)`

The Array of the elements. A Set has no duplicates, so it is the same as `to_a`.

```ruby
p(Set.uniq(Set.of(1, 2)))            # => [1, 2]
```

## compact

`Set.compact(x)`

The Array of the elements without nil.

```ruby
p(Set.compact(Set.of(1, nil, 2)))    # => [1, 2]
```

## flatten

`Set.flatten(x)`

A **new Set** with the element Sets opened up recursively (Ruby's `Set#flatten`). Array elements are not opened.

```ruby
p(Set.flatten(Set.of(1, Set.of(2, Set.of(3)))))   # => Set[1, 2, 3]
p(Set.flatten(Set.of(1, 2)))                # => Set[1, 2]
```

## zip

`Set.zip(x, *Array)`

The Array of Tuples pairing each element with the elements at the same position of each Array. A shorter Array contributes nil. The arguments must be Arrays (a Set is a `type` problem statically).

```ruby
p(Set.zip(Set.of(1, 2, 3), Any["a", "b"]))   # => [[1, "a"], [2, "b"], [3, nil]]
p(Set.zip(Set.of(1, 2)))                       # => [[1], [2]]
```

## join

`Set.join(x, [String])`

The String of the elements' `to_s` joined, with the separator ("" by default) between them. Array elements are joined through recursively.

```ruby
s = Set.of(1, 2, 3)
p(Set.join(s))                    # => "123"
p(Set.join(s, ", "))              # => "1, 2, 3"
```

# Time

A Time is one instant: the seconds since 1970-01-01 00:00:00 UTC (to rational precision) and the UTC offset it is shown in. There is no literal; `Time.now`, `Time.at(seconds)` and `Time.new(year, month, day, ...)` make one ([Values and types](../03-values.md)). The values are Ruby's Times, but where Ruby's `t.utc` and `t.localtime` change t itself, every operation in Sake returns a new Time and leaves its subject alone. Time has no typed Array `Time[]`; a sequence of Times is an `Any[...]`.

**Zones.** The `in:` keyword and the zone argument of `getlocal` and `new` are fixed UTC offsets: `"+09:00"`, `"-05:00"`, `"UTC"` or `"Z"`, a military letter `"A".."I"`, `"K".."Z"`, or an Integer number of seconds. A region name such as `"Asia/Tokyo"` is not accepted (`ArgumentError`). Without `in:` the local zone of the machine applies, so every example whose output must not depend on the machine passes `in:`. A Time made with `"UTC"` or `"Z"` is in UTC mode (`utc?` is true and `to_s` ends in `UTC`); one made with `"+00:00"` is an ordinary Time at offset 0 (`utc?` is false, `to_s` ends in `+0000`).

The operators on Times are `+` and `-` (with a number of seconds on the right, Integer, Float or Rational; `Time - Time` is the difference in seconds as a Float), `==`, `!=`, `<`, `<=`, `>`, `>=` and `<=>` (with a Time on the right). The entries `Time.+(x, y)` and so on below are their function forms ([Operators and indexing](../05-operators.md)). `Time.strftime(t, fmt)` and `Time.iso8601(t)` make Strings. Reading a Time from a String (Ruby's `Time.parse`, `Time.iso8601(s)`) is not built in.

## now

`Time.now([in: String|Integer])`

The current time (a Time). With `in:` it is shown at that offset, without it in the local zone. A bad zone is an `ArgumentError`.

```ruby
t = Time.now(in: "UTC")
p(Time.utc?(t))                        # => true
p(Time.year(t) >= 2026)                # => true
p(Time.utc_offset(Time.now(in: "+09:00")))   # => 32400
```

## at

`Time.at(Integer|Float|Rational|Time, [in: String|Integer])`

The Time `seconds` after 1970-01-01 00:00:00 UTC (Ruby's `Time.at`). An Integer, a Float or a Rational; a Rational keeps its precision. Given a Time, a Time of the same instant (and the same zone when `in:` is absent). `in:` picks the zone; a bad one is an `ArgumentError`.

```ruby
t = Time.at(0, in: "UTC")
p(t)                                   # => 1970-01-01 00:00:00 UTC
p(Time.at(1.5, in: "UTC"))             # => 1970-01-01 00:00:01.5 UTC
p(Time.subsec(Time.at(1r/3, in: "UTC")))   # => (1/3)
p(Time.at(t, in: "+09:00"))            # => 1970-01-01 09:00:00 +0900
p(Time.at(t) == t)                     # => true
```

```ruby error
Time.at(0, in: "Asia/Tokyo")           # !> ArgumentError: Time.at: "+HH:MM", "-HH:MM", "UTC" or "A".."I","K".."Z" expected for utc_offset: Asia/Tokyo
```

## new

`Time.new(Integer, [Integer], [Integer], [Integer], [Integer], [Integer|Float|Rational], [String|Integer], [in: String|Integer])`

A Time from year, month, day, hour, minute, second and zone (Ruby's `Time.new(y, m, d, h, min, s, zone)`). The year is required, the rest may be left out (month and day default to 1, hour, minute and second to 0). The second may be a Float or a Rational with a fraction. The zone is either the seventh argument or `in:`, not both (`ArgumentError`), and the local zone when neither is given. A month outside 1..12, an hour outside 0..23 and so on are an `ArgumentError` (`mon out of range`). Ruby's `Time.new` without arguments (the current time) does not exist here: use `Time.now`.

```ruby
t = Time.new(2024, 2, 29, 12, 30, 45, "+09:00")
puts(Time.to_s(t))                     # => 2024-02-29 12:30:45 +0900
puts(Time.to_s(Time.new(2024, in: "UTC")))         # => 2024-01-01 00:00:00 UTC
puts(Time.to_s(Time.new(2024, 3, 4, 5, in: "UTC")))   # => 2024-03-04 05:00:00 UTC
p(Time.subsec(Time.new(2024, 1, 1, 0, 0, 7.5, in: "Z")))   # => (1/2)
puts(Time.to_s(Time.new(2024, 3, 4, 5, 6, 7, 3600)))      # => 2024-03-04 05:06:07 +0100
```

```ruby error
Time.new(2024, 13, 1, in: "UTC")       # !> ArgumentError: Time.new: mon out of range
```

## year, month, mon, day, mday, hour, min, sec

`Time.year(x)`

`Time.month(x)`

`Time.mon(x)`

`Time.day(x)`

`Time.mday(x)`

`Time.hour(x)`

`Time.min(x)`

`Time.sec(x)`

The parts of the time (Integers), read at the Time's own offset: the year, the month (1..12; `mon` is an alias), the day of the month (1..31; `mday` is an alias), the hour (0..23), the minute (0..59), the second (0..60, without its fraction, see `subsec`).

```ruby
t = Time.new(2024, 2, 29, 12, 30, 45.5, in: "+09:00")
p([Time.year(t), Time.month(t), Time.day(t)])      # => [2024, 2, 29]
p([Time.hour(t), Time.min(t), Time.sec(t)])        # => [12, 30, 45]
p(Time.mon(t) == Time.month(t))                    # => true
p(Time.hour(Time.utc(t)))                          # => 3
```

## wday, yday

`Time.wday(x)`

`Time.yday(x)`

The day of the week (an Integer, Sunday 0 to Saturday 6) and the day of the year (January 1 is 1, December 31 of a leap year 366).

```ruby
t = Time.new(2024, 2, 29, in: "UTC")
p(Time.wday(t))                        # => 4
p(Time.yday(t))                        # => 60
p(Time.wday(Time.at(0, in: "UTC")))    # => 4
```

## sunday?, monday?, tuesday?, wednesday?, thursday?, friday?, saturday?

`Time.sunday?(x)`

`Time.monday?(x)`

`Time.tuesday?(x)`

`Time.wednesday?(x)`

`Time.thursday?(x)`

`Time.friday?(x)`

`Time.saturday?(x)`

True on that day of the week. `Time.wday(t) == 4` and `Time.thursday?(t)` say the same.

```ruby
t = Time.new(2024, 2, 29, in: "UTC")
p(Time.thursday?(t))                   # => true
p(Time.friday?(t))                     # => false
p(Time.sunday?(t + 3 * 86400))         # => true
```

## nsec, usec, subsec, tv_sec, tv_nsec, tv_usec

`Time.nsec(x)`

`Time.usec(x)`

`Time.subsec(x)`

`Time.tv_sec(x)`

`Time.tv_nsec(x)`

`Time.tv_usec(x)`

The part finer than a second. `nsec` (alias `tv_nsec`) is the nanoseconds (an Integer, 0..999999999), `usec` (alias `tv_usec`) the microseconds (an Integer, 0..999999), `subsec` the fraction of the second as it is (the Integer 0, or a Rational). `tv_sec` is `to_i`, the whole seconds since 1970.

```ruby
t = Time.at(1700000000.123456789r, in: "UTC")
p(Time.nsec(t))                        # => 123456789
p(Time.usec(t))                        # => 123456
p(Time.subsec(Time.at(1.5, in: "UTC")))    # => (1/2)
p(Time.subsec(Time.at(2, in: "UTC")))      # => 0
p(Time.tv_sec(t))                      # => 1700000000
p(Time.tv_nsec(t) == Time.nsec(t))     # => true
```

## to_i, to_f, to_r

`Time.to_i(x)`

`Time.to_f(x)`

`Time.to_r(x)`

The seconds since 1970-01-01 00:00:00 UTC as an Integer (the fraction dropped), a Float, or an exact Rational. The zone plays no part. They invert `Time.at`.

```ruby
t = Time.at(1.5, in: "UTC")
p(Time.to_i(t))                        # => 1
p(Time.to_f(t))                        # => 1.5
p(Time.to_r(t))                        # => (3/2)
p(Time.to_i(Time.at(100, in: "+09:00")))   # => 100
```

## to_a

`Time.to_a(x)`

Ruby's `t.to_a`: the ten-element Tuple `[sec, min, hour, day, month, year, wday, yday, isdst, zone]`. The last element is `Time.zone`: `"UTC"` for a UTC Time, nil for a Time at a fixed offset such as `in: "+09:00"`. Its type is `String | nil`, so using it unchecked as a String is a `nil` problem under `--strict` (level 2); the other nine are Integers and a Boolean.

```ruby
p(Time.to_a(Time.at(0, in: "UTC")))        # => [0, 0, 0, 1, 1, 1970, 4, 1, false, "UTC"]
p(Time.to_a(Time.at(0, in: "+09:00")))     # => [0, 0, 9, 1, 1, 1970, 4, 1, false, nil]
sec, min, hour, day, month, year, wday, yday, isdst, zone = Time.to_a(Time.at(0, in: "UTC"))
p(zone ? String.size(zone) : 0)            # => 3
```

```ruby error
zone = Time.to_a(Time.at(0, in: "+09:00"))[9]
p(String.size(zone))                       # !> String.size: argument 1 may be nil
```

## to_s

`Time.to_s(x)`

The String `"2024-02-29 12:30:45 +0900"` (Ruby's `t.to_s`). A Time in UTC mode ends in `UTC`. The fraction of the second is not written (`p(t)` and `inspect` write it). `puts(t)` and `"#{t}"` use this form.

```ruby
t = Time.new(2024, 2, 29, 12, 30, 45.5, in: "+09:00")
puts(Time.to_s(t))                     # => 2024-02-29 12:30:45 +0900
puts(Time.utc(t))                      # => 2024-02-29 03:30:45 UTC
p(Time.at(0, in: "+00:00"))            # => 1970-01-01 00:00:00 +0000
```

## strftime

`Time.strftime(x, String)`

A String in the format of Ruby's `strftime`: `%Y` year, `%m` month, `%d` day, `%H`, `%M`, `%S` hour, minute, second, `%F` (`%Y-%m-%d`), `%T` (`%H:%M:%S`), `%z` (`+0900`), `%:z` (`+09:00`), `%Z` (the zone name, empty for a Time at a fixed offset), `%A`, `%a` weekday names, `%B`, `%b` month names, `%j` day of the year, `%s` seconds since 1970, `%L` milliseconds, `%N` nanoseconds (`%3N` for a digit count), `-` as in `%-d` for no zero padding, `%%` for `%`. An unknown directive is left as it is.

```ruby
t = Time.new(2024, 2, 29, 12, 30, 45, in: "+09:00")
puts(Time.strftime(t, "%Y-%m-%d %H:%M:%S %z"))   # => 2024-02-29 12:30:45 +0900
puts(Time.strftime(t, "%F %T %:z"))             # => 2024-02-29 12:30:45 +09:00
puts(Time.strftime(t, "%A %-d %B, day %j"))     # => Thursday 29 February, day 060
puts(Time.strftime(t, "%s|%L|%Z|%%"))           # => 1709177445|000||%
puts(Time.strftime(Time.utc(t), "%Z %z"))       # => UTC +0000
```

## iso8601

`Time.iso8601(x, [Integer])`

The ISO 8601 String `2024-02-29T12:30:45+09:00` (Ruby's `t.iso8601`, `xmlschema`). A Time in UTC mode ends in `Z`. With `n`, the fraction of the second is written with `n` digits (default 0). It does not parse (Ruby's class method `Time.iso8601(s)`).

```ruby
t = Time.new(2024, 2, 29, 12, 30, 45.5, in: "+09:00")
puts(Time.iso8601(t))                  # => 2024-02-29T12:30:45+09:00
puts(Time.iso8601(t, 3))               # => 2024-02-29T12:30:45.500+09:00
puts(Time.iso8601(Time.utc(t)))        # => 2024-02-29T03:30:45Z
```

## asctime, ctime

`Time.asctime(x)`

`Time.ctime(x)`

The form of C's `asctime`, `"Thu Feb 29 12:30:45 2024"` (Ruby's `t.asctime` and `t.ctime`, which are the same). The zone is not written.

```ruby
t = Time.new(2024, 2, 29, 12, 30, 45, in: "+09:00")
puts(Time.asctime(t))                  # => Thu Feb 29 12:30:45 2024
p(Time.ctime(t) == Time.asctime(t))    # => true
```

## utc, getutc, gmtime, getgm, gm

`Time.utc(Time|Integer, [Integer], [Integer], [Integer], [Integer], [Integer|Float|Rational])`

`Time.getutc(x)`

`Time.gmtime(x)`

`Time.getgm(x)`

`Time.gm(Integer, [Integer], [Integer], [Integer], [Integer], [Integer|Float|Rational])`

Given a Time, a new Time showing the same instant in UTC mode (Ruby's `t.getutc`). The four are the same operation, and none changes its subject as Ruby's `t.utc` and `t.gmtime` do. The result has `utc?` true, `zone` `"UTC"`, and is `==` to the original. Given a year (and the parts after it, as `new`), `Time.utc` and `Time.gm` make that time in UTC (Ruby's class methods `Time.utc(y, m, d, h, min, s)` and `Time.gm`); a Time with more arguments is an `ArgumentError`.

```ruby
t = Time.new(2024, 2, 29, 12, 30, 45, in: "+09:00")
u = Time.utc(t)
puts(Time.to_s(u))                     # => 2024-02-29 03:30:45 UTC
puts(Time.to_s(t))                     # => 2024-02-29 12:30:45 +0900
p(Time.utc?(u))                        # => true
puts(Time.utc(2024, 2, 29, 3, 30, 45)) # => 2024-02-29 03:30:45 UTC
puts(Time.gm(2024))                    # => 2024-01-01 00:00:00 UTC
p(u == t)                              # => true
p(Time.getutc(t) == Time.gmtime(t) && Time.getgm(t) == u)   # => true
```

## localtime, getlocal

`Time.localtime(x, [String|Integer])`

`Time.getlocal(x, [String|Integer])`

A new Time showing the same instant at another offset (Ruby's `t.getlocal(zone)`). The zone is `"+09:00"`, `"UTC"`, an Integer of seconds and so on (see above); without it, the machine's local zone. The two are the same operation, and neither changes its subject as Ruby's `t.localtime` does. With `"UTC"` the result is in UTC mode.

```ruby
t = Time.at(1700000000, in: "+09:00")
puts(Time.to_s(Time.getlocal(t, "-05:00")))   # => 2023-11-14 17:13:20 -0500
puts(Time.to_s(Time.localtime(t, 3600)))      # => 2023-11-14 23:13:20 +0100
p(Time.utc?(Time.getlocal(t, "UTC")))         # => true
p(Time.getlocal(t) == t)                      # => true
puts(Time.to_s(t))                            # => 2023-11-15 07:13:20 +0900
```

## utc?, gmt?

`Time.utc?(x)`

`Time.gmt?(x)`

True for a Time in UTC mode (made with `"UTC"` or `"Z"`, or by `Time.utc`). A Time made with `"+00:00"` is at offset 0 but gives false. `gmt?` is an alias.

```ruby
p(Time.utc?(Time.at(0, in: "UTC")))    # => true
p(Time.utc?(Time.at(0, in: "+00:00"))) # => false
p(Time.gmt?(Time.utc(Time.at(0, in: "+09:00"))))   # => true
```

## zone

`Time.zone(x)`

The zone's name (a String) or nil: `"UTC"` in UTC mode, the abbreviation of the local zone for a local Time (`"JST"` and the like, depending on the machine), and nil for a Time made with a fixed offset such as `"+09:00"`, which has no name. The result is `String | nil`, so passing it unchecked to a String operation is stopped by `--strict` (level 2). For the offset itself use `utc_offset`.

```ruby
p(Time.zone(Time.at(0, in: "UTC")))    # => "UTC"
p(Time.zone(Time.at(0, in: "+09:00"))) # => nil
```

## utc_offset, gmt_offset, gmtoff

`Time.utc_offset(x)`

`Time.gmt_offset(x)`

`Time.gmtoff(x)`

The offset from UTC in seconds (an Integer: 32400 for `+09:00`, 0 for UTC). The three are aliases.

```ruby
t = Time.at(0, in: "+09:00")
p(Time.utc_offset(t))                  # => 32400
p(Time.gmt_offset(Time.at(0, in: "-05:00")))   # => -18000
p(Time.gmtoff(Time.utc(t)))            # => 0
```

## dst?, isdst

`Time.dst?(x)`

`Time.isdst(x)`

True during daylight saving time (Ruby's `t.dst?`). Always false for a Time at a fixed offset or in UTC; only a Time in the local zone can give true. `isdst` is an alias.

```ruby
p(Time.dst?(Time.at(0, in: "UTC")))    # => false
p(Time.isdst(Time.at(0, in: "+09:00")))    # => false
```

## round, floor, ceil

`Time.round(x, [Integer])`

`Time.floor(x, [Integer])`

`Time.ceil(x, [Integer])`

A new Time with the fraction of the second rounded to `n` digits (default 0), Ruby's `t.round(n)`: `round` to the nearest, `floor` down, `ceil` up. A negative `n` is an `ArgumentError`.

```ruby
t = Time.at(1700000000.123456789r, in: "UTC")
puts(Time.iso8601(Time.round(t, 2), 9))    # => 2023-11-14T22:13:20.120000000Z
puts(Time.iso8601(Time.floor(t, 1), 9))    # => 2023-11-14T22:13:20.100000000Z
puts(Time.iso8601(Time.ceil(t), 9))        # => 2023-11-14T22:13:21.000000000Z
p(Time.round(Time.at(1.5, in: "UTC")) == Time.at(2, in: "UTC"))   # => true
```

## +, -

`Time.+(x, Any)`

`Time.-(x, Any)`

`t + seconds` and `t - seconds` are new Times that many seconds (an Integer, Float or Rational) later or earlier, in t's zone. `t1 - t2` is the difference of two Times in seconds, a Float. `Time + Time` and a right operand that is not a number of seconds are `type` problems statically. There are no day or month units: a day is written `86400`.

```ruby
a = Time.at(100, in: "UTC")
p(a + 5)                               # => 1970-01-01 00:01:45 UTC
p(a - 1.5)                             # => 1970-01-01 00:01:38.5 UTC
p((a + 5) - a)                         # => 5.0
p(a - (a + 5))                         # => -5.0
p(Time.-(a, 1r/4) == Time.at(99.75, in: "UTC"))   # => true
d = Time.new(2024, 3, 10, in: "UTC") - Time.new(2024, 1, 1, in: "UTC")
p(Integer(d / 86400))                  # => 69
```

```ruby error
a = Time.at(0, in: "UTC")
p(a + a)                               # !> the operands are (Time, Time), which the left operand's type does not support
```

## ==, !=

`Time.==(x, Any)`

`Time.!=(x, Any)`

True for the same instant (`!=` is the negation). The zone is not compared: `Time.at(100, in: "UTC")` and `Time.at(100, in: "+09:00")` are equal. A right operand that is not a Time (nil, an Integer, ...) gives false for `==` and true for `!=`, in the operator form `t == v` and in the function form `Time.==(t, v)` alike: the function forms accept any value and never raise.

```ruby
a = Time.at(100, in: "UTC")
p(a == Time.at(100, in: "+09:00"))     # => true
p(a != a + 1)                          # => true
p(a == nil)                            # => false
p(Time.==(a, Time.at(100.0, in: "UTC")))   # => true
p(Time.==(a, 100))                     # => false
p(Time.!=(a, 100))                     # => true
```

## <, <=, >, >=

`Time.<(x, Any)`

`Time.<=(x, Any)`

`Time.>(x, Any)`

`Time.>=(x, Any)`

Compare two instants (the earlier is smaller). The right operand must be a Time; an Integer or the like is a `type` problem statically, in the function form `Time.<(t, 100)` too (unlike `==`, which just gives false).

```ruby
a = Time.at(100, in: "UTC")
b = a + 5
p(a < b)                               # => true
p(a <= a)                              # => true
p(b > a && b >= a)                     # => true
p(Time.<(b, a))                        # => false
```

```ruby error
a = Time.at(100, in: "UTC")
p(a < 100)                             # !> the operands are (Time, Integer), which the left operand's type does not support
```

## <=>

`Time.<=>(x, Any)`

The result of the comparison as -1, 0 or 1 (an Integer; two Times can always be compared, so it is never nil). A Time as the key of `Array.sort` or `Array.sort_by` sorts in time order.

```ruby
a = Time.at(100, in: "UTC")
p(a <=> a + 1)                         # => -1
p(a <=> a)                             # => 0
p(Time.<=>(a + 1, a))                  # => 1
xs = Array.sort(Any[a + 9, a, a + 3])
p(Array.map(xs) { |t| Time.to_i(t) })  # => [100, 103, 109]
```

# IO

IO is the type of open files and of the program's standard streams. Its values come from operations: `IO.stdin`, `IO.stdout`, and `IO.stderr` give the standard streams, `File.open(path, mode)` gives a file (Sake has no `$stdout` or `STDOUT`, see [Values and types](../03-values.md)). Files and streams share one type, so a function that takes an IO writes to either. There is no IO literal.

Ruby's `io.puts(x)` is written `IO.puts(io, x)`. Of Ruby's `IO` methods only those listed here exist. A failing read or write (a closed IO, an IO not opened for that direction, an error from the OS) is an `IOError` (Ruby's `Errno::ENOENT` and the like are all `IOError`, see [Exceptions](../08-exceptions.md)).

The only operators on IO are `==` and `!=` (equal when the two values are the same stream or the same result of `File.open`). An operation without arguments, `IO.stdout()`, can also be written `IO.stdout`.

## stdin, stdout, stderr

`IO.stdin()`

`IO.stdout()`

`IO.stderr()`

The program's standard input, standard output, and standard error as IO values. Every call gives the same value: `IO.stdout == IO.stdout` is true. `Kernel.puts` and `Kernel.p` write to `IO.stdout`; `Kernel.gets` reads from `IO.stdin`. Writing to `IO.stderr` flushes stdout first, so the lines of the two streams keep their order. Closing a standard stream with `IO.close` is allowed but rarely useful: afterwards every write to it (`p`, `puts`, `IO.write`) is an `IOError` (`closed stream`), which can be rescued; `IO.stderr` keeps working.

```ruby
out = IO.stdout
IO.puts(out, "hello")            # => hello
p(IO.stdout == out)              # => true
p(IO.stdout == IO.stderr)        # => false
p(IO.stdin)                      # => #<IO:<STDIN>>
```

```ruby error
IO.close(IO.stdout)
puts("x")                        # !> IOError: Kernel.puts: closed stream
```

## puts

`IO.puts(x, *Any)`

Writes each argument to `x` as `Kernel.puts` does and returns nil. One line per argument (a newline is added unless the text ends with one); an Array or Tuple is flattened to one line per element, an empty Array is an empty line, and no arguments give one empty line. Values other than Strings are written as `Kernel.to_s` shows them (nil as an empty line, a Symbol as its name). A closed or read-only IO is an `IOError`.

```ruby
out = IO.stdout
IO.puts(out, "a", 1)             # => a
                                 # => 1
IO.puts(out, [1, Any[2, 3]])   # => 1
                                 # => 2
                                 # => 3
IO.puts(out, "x\n")              # => x
p(IO.puts(out, :s))              # => s
                                 # => nil
```

## print

`IO.print(x, *Any)`

Writes the `to_s` of each argument to `x` without newlines and returns nil (Ruby's `io.print`). An Array is written as `[1, 2]`, nil as an empty string.

```ruby
out = IO.stdout
IO.print(out, "a", 1, :s, "\n")  # => a1s
IO.print(out, Any[1, 2], "\n") # => [1, 2]
```

## write

`IO.write(x, String)`

Writes the string to `x` as it is and returns the number of bytes written (an Integer). No newline is added. The argument must be a String; another type is a `type` problem statically (unlike `puts` and `print`, nothing is converted).

```ruby
p(IO.write(IO.stdout, "ab\n"))   # => ab
                                 # => 3
p(IO.write(IO.stdout, "日本\n")) # => 日本
                                 # => 7
```

```ruby error
IO.write(IO.stdout, 1)           # !> IO.write: argument 2 must be String, but is Integer
```

## flush

`IO.flush(x)`

Writes out buffered output and returns `x`. `IO.write` and `IO.puts` to a file from `File.open` are buffered; the data reaches the file on `flush` or `close`.

```ruby
f = File.open("log.txt", "w")
IO.puts(f, "a")
p(File.read("log.txt"))          # => ""
IO.flush(f)
p(File.read("log.txt"))          # => "a\n"
IO.close(f)
```

## gets

`IO.gets(x)`

The next line including its newline (Ruby's `io.gets`), or nil at the end. The result is `String | nil`, so `--strict` (level 2) reports using it unchecked as a `nil` problem: test it first, as `if line` or `while (line = IO.gets(io))`. A write-only IO is an `IOError`.

```ruby
io = IO.stdin
line = IO.gets(io)
if line
  p(String.to_i(line))           # => 3
end
while (l = IO.gets(io))
  p(l)                           # => "1 2\n"
end
p(IO.gets(io))                   # => nil
```

```ruby error
line = IO.gets(IO.stdin)
p(String.upcase(line))           # !> argument 1 may be nil
```

## read

`IO.read(x, [Integer])`

`IO.read(io)` reads everything from the current position to the end and returns a String (at the end it is the empty string `""`, never nil). `IO.read(io, n)` reads up to `n` bytes (bytes, not characters) and returns nil when the end has been reached (with `n` 0 it is always `""`). Only this two-argument form is `String | nil` and checked at level 2. An IO not opened for reading is an `IOError`.

```ruby
io = IO.stdin
p(IO.read(io, 2))                # => "3\n"
p(IO.read(io))                   # => "1 2\n"
p(IO.read(io))                   # => ""
p(IO.read(io, 1))                # => nil
```

## getc

`IO.getc(x)`

The next character as a String, or nil at the end (`String | nil`, level 2).

```ruby
io = IO.stdin
p(IO.getc(io))                   # => "3"
p(IO.getc(io))                   # => "\n"
IO.read(io)
p(IO.getc(io))                   # => nil
```

## readlines

`IO.readlines(x)`

The rest, from the current position to the end, as an Array of Strings, one per line (each keeps its newline). At the end it is an empty Array.

```ruby
p(IO.readlines(IO.stdin))        # => ["3\n", "1 2\n"]
p(IO.readlines(IO.stdin))        # => []
```

## each_line

`IO.each_line(x) { }`

Passes each line (a String with its newline) from the current position to the end to the block and returns `x`. The block's value is not used. As Ruby's `io.each_line`; the block is required.

```ruby
r = IO.each_line(IO.stdin) { |l| p(String.chomp(l)) }   # => "3"
                                                         # => "1 2"
p(r == IO.stdin)                 # => true
```

## eof?

`IO.eof?(x)`

True when the read position is at the end. A write-only or closed IO is an `IOError`.

```ruby
io = IO.stdin
p(IO.eof?(io))                   # => false
IO.read(io)
p(IO.eof?(io))                   # => true
```

## seek

`IO.seek(x, Integer, [Integer|Symbol])`

Moves the read/write position and returns 0 (Ruby's `io.seek`). The second argument is the offset, the third the origin, the start of the file (`0`) when omitted: `0`/`:SET` counts from the start, `1`/`:CUR` from the current position, `2`/`:END` from the end (with a negative offset). Only these three Symbols exist; another Symbol is an `ArgumentError`. A stream that cannot seek (`IO.stdin` as a pipe) is an `IOError`.

```ruby
File.write("a.txt", "hello\n")
f = File.open("a.txt")
IO.seek(f, 2)
p(IO.read(f, 2))                 # => "ll"
IO.seek(f, -2, :END)
p(IO.read(f))                    # => "o\n"
p(IO.seek(f, 1, :SET))           # => 0
p(IO.getc(f))                    # => "e"
IO.close(f)
```

```ruby error
File.write("a.txt", "hello\n")
f = File.open("a.txt")
IO.seek(f, 1, :FOO)              # !> ArgumentError: IO.seek: unknown whence: :FOO (0/1/2 or :SET/:CUR/:END)
```

## pos

`IO.pos(x)`

The current position (bytes from the start, an Integer). A stream that cannot seek is an `IOError`.

```ruby
File.write("a.txt", "日本\n")
f = File.open("a.txt")
p(IO.pos(f))                     # => 0
IO.getc(f)
p(IO.pos(f))                     # => 3
IO.close(f)
```

## rewind

`IO.rewind(x)`

Moves the position back to the start and returns 0, to read the file again.

```ruby
File.write("a.txt", "one\n")
f = File.open("a.txt")
p(IO.gets(f))                    # => "one\n"
p(IO.rewind(f))                  # => 0
p(IO.gets(f))                    # => "one\n"
IO.close(f)
```

## size

`IO.size(x)`

The size of the open file in bytes (an Integer), including writes not yet flushed. An IO that is not a file (a stream such as `IO.stdin`) is an `IOError` (`not a file`).

```ruby
f = File.open("a.txt", "w")
IO.write(f, "abc")
p(IO.size(f))                    # => 3
IO.close(f)
```

```ruby error
IO.size(IO.stdin)                # !> IOError: IO.size: not a file
```

## truncate

`IO.truncate(x, Integer)`

Cuts the file to `n` bytes and returns 0. A file not opened for writing is an `IOError`.

```ruby
File.write("a.txt", "hello")
r = File.open("a.txt", "r+") { |f| IO.truncate(f, 3) }
p(r)                             # => 0
p(File.read("a.txt"))            # => "hel"
```

```ruby error
File.write("a.txt", "hello")
f = File.open("a.txt")
IO.truncate(f, 1)                # !> IOError: IO.truncate: not opened for writing
```

## close

`IO.close(x)`

Closes the IO and returns nil. Buffered output is written out. Closing a closed IO does nothing (as in Ruby). Reading or writing a closed IO is an `IOError`. The block form of `File.open` closes the file after the block, so `close` is needed only for a file opened by hand.

```ruby
f = File.open("a.txt", "w")
IO.write(f, "x")
p(IO.close(f))                   # => nil
p(IO.close(f))                   # => nil
p(File.read("a.txt"))            # => "x"
```

```ruby error
f = File.open("a.txt", "w")
IO.close(f)
IO.write(f, "x")                 # !> IOError: IO.write: closed stream
```

## closed?

`IO.closed?(x)`

True when the IO is closed. The IO passed to the block of `File.open` is closed after the block.

```ruby
kept = IO.stdout
File.open("a.txt", "w") { |f| kept = f }
p(IO.closed?(kept))              # => true
p(IO.closed?(IO.stdout))         # => false
```

## tty?

`IO.tty?(x)`

True when the IO is a terminal (Ruby's `io.tty?`); false for a file or a pipe. Under the checker the standard streams are pipes, so it is false there too. The terminal operations below (`winsize`, `raw`, `noecho`, `getch`) work only when this is true.

```ruby
f = File.open("a.txt", "w")
p(IO.tty?(f))                    # => false
IO.close(f)
```

## winsize

`IO.winsize(x)`

The size of the terminal that `x` is connected to, as the Tuple `[rows, columns]` (Integer, Integer; Ruby's `io.winsize` of `io/console`). Take it apart with `rows, cols = IO.winsize(IO.stdout)`. An IO that is not a terminal (a file, a pipe, the standard streams under the checker) is an `IOError` (`not a terminal`). In a terminal:

```
rows, cols = IO.winsize(IO.stdout)
p([rows, cols])                  # [24, 80] for an 80x24 terminal
```

Under the checker stdin is a pipe, so only the error can be shown:

```ruby
begin
  IO.winsize(IO.stdin)
rescue IOError => e
  p(Exception.message(e))        # => "not a terminal"
end
```

## raw, noecho

`IO.raw(x) { }`

`IO.noecho(x) { }`

Run the block with the terminal of `x` in raw mode (`raw`: a key is delivered at once, without waiting for a line, and is not echoed) or with echo off (`noecho`: lines are still read as lines, but what is typed is not shown), restore the terminal afterwards (also when the block raises), and return the block's value (Ruby's `io.raw { }` and `io.noecho { }`). The result has the block's type. The block is required and takes no parameters. An IO that is not a terminal is an `IOError` (`not a terminal`). In a terminal, a password is read without showing it, and one key without waiting for Enter:

```
pw = IO.noecho(IO.stdin) { IO.gets(IO.stdin) }   # "secret\n" after typing secret and Enter
k = IO.raw(IO.stdin) { IO.getch(IO.stdin) }      # "x" as soon as x is pressed
```

```ruby
begin
  IO.noecho(IO.stdin) { IO.gets(IO.stdin) }
rescue IOError => e
  p(Exception.message(e))        # => "not a terminal"
end
```

## getch

`IO.getch(x)`

Reads one key from the terminal of `x` and returns it as a String, without waiting for Enter and without echo (Ruby's `io.getch`). The result is `String | nil` (nil at the end of input), so `--strict` (level 2) reports using it unchecked as a `nil` problem. An IO that is not a terminal is an `IOError` (`not a terminal`); to read a byte from a pipe or a file use `IO.getc`. In a terminal:

```
k = IO.getch(IO.stdin)           # "y" as soon as y is pressed
```

```ruby
begin
  IO.getch(IO.stdin)
rescue IOError => e
  p(Exception.message(e))        # => "not a terminal"
end
```

## ==, !=

`IO.==(x, Any)`

`IO.!=(x, Any)`

Two IO values are equal when they are the same stream or the same result of `File.open` (`!=` is the negation). Opening the same path twice gives two different values. A value that is not an IO is never equal.

```ruby
f = File.open("a.txt", "w")
g = File.open("a.txt", "w")
p(f == f)                        # => true
p(f == g)                        # => false
p(IO.stdout != IO.stdin)         # => true
IO.close(f)
IO.close(g)
```

# File

File is the group of operations that work on a file by its path (a String). There is no value of type File: `File.open` returns an `IO` ([IO](IO.md)), and the other operations take a path and read, write, or query the file on the spot. They have the names of Ruby's class methods, `File.read(path)` and so on; what Ruby does with instance methods (`file.read`) is an `IO` operation ([Values and types](../03-values.md), [Built-ins](../09-builtins.md)).

A failing system call (a missing path, permissions, reading a directory; Ruby's `Errno::ENOENT` and the like) is an `IOError` with Ruby's message ([Exceptions](../08-exceptions.md)). The path operations (`basename`, `join`, `expand_path`, ...) do not look at the file system. The predicates (`exist?`, ...) never fail; they return false.

File has no operators. The examples run in an empty directory and create files there.

## open

`File.open(String, [String], [Integer]) [{ }]`

Opens the file and returns an `IO`. The second argument is the mode, `"r"` when omitted (as Ruby's: `"r"` read, `"w"` write (truncating), `"a"` append, `"r+"` read and write, `"w+"`, `"a+"`, and `"b"` for binary). The third argument is the permission bits for a newly created file (`0o600`; an existing file is not changed). With a block, the `IO` is passed to the block, the file is closed after it, and **the block's value** is returned (as in Ruby). Without a block, close it with `IO.close`. A missing or unreadable file is an `IOError`; an unknown mode is an `ArgumentError`.

```ruby
f = File.open("a.txt", "w")
IO.puts(f, "one")
IO.close(f)
n = File.open("a.txt", "a") { |io| IO.write(io, "two\n") }
p(n)                                # => 4
p(File.open("a.txt") { |io| IO.readlines(io) })   # => ["one\n", "two\n"]
File.open("p.txt", "w", 0o600) { |io| nil }
p(File.exist?("p.txt"))             # => true
```

```ruby error
File.open("nope.txt")               # !> IOError: File.open: No such file or directory
```

## read

`File.read(String)`

The whole file as a String. A missing file or a directory is an `IOError`.

```ruby
File.write("a.txt", "hello\n")
p(File.read("a.txt"))               # => "hello\n"
```

```ruby error
File.read("nope.txt")               # !> IOError: File.read: No such file or directory
```

## readlines

`File.readlines(String)`

The whole file as an Array of Strings, one per line (each keeps its newline). An empty file gives an empty Array.

```ruby
File.write("a.txt", "a\nb\n")
p(File.readlines("a.txt"))          # => ["a\n", "b\n"]
```

## write

`File.write(String, String)`

Creates the file (or replaces its content) with the string and returns the number of bytes written (an Integer). It does not append (append with `File.open(path, "a")`). The second argument must be a String; another type is a `type` problem statically.

```ruby
p(File.write("a.txt", "日本"))      # => 6
p(File.write("a.txt", "x"))         # => 1
p(File.read("a.txt"))               # => "x"
```

```ruby error
File.write("a.txt", 1)              # !> File.write: argument 2 must be String, but is Integer
```

## exist?

`File.exist?(String)`

True when a file or directory is at the path. It never fails.

```ruby
File.write("a.txt", "")
p(File.exist?("a.txt"))             # => true
p(File.exist?("."))                 # => true
p(File.exist?("nope"))              # => false
```

## file?, directory?, symlink?

`File.file?(String)`

`File.directory?(String)`

`File.symlink?(String)`

True for a regular file, a directory, or a symbolic link. `file?` and `directory?` follow links and look at the target. A missing path is false.

```ruby
File.write("a.txt", "")
File.symlink("a.txt", "l1.txt")
p(File.file?("a.txt"))              # => true
p(File.file?("."))                  # => false
p(File.directory?("."))             # => true
p(File.symlink?("l1.txt"))           # => true
p(File.symlink?("a.txt"))           # => false
p(File.file?("nope"))               # => false
```

## zero?, empty?

`File.zero?(String)`

`File.empty?(String)`

True when the file exists and has size 0 (the two are the same, as in Ruby). A missing path is false.

```ruby
File.write("e.txt", "")
File.write("a.txt", "x")
p(File.zero?("e.txt"))              # => true
p(File.empty?("a.txt"))             # => false
p(File.zero?("nope"))               # => false
```

## readable?, writable?, executable?

`File.readable?(String)`

`File.writable?(String)`

`File.executable?(String)`

True when this process can read, write, or execute the path. A missing path is false.

```ruby
File.write("a.txt", "")
p(File.readable?("a.txt"))          # => true
p(File.writable?("a.txt"))          # => true
p(File.executable?("a.txt"))        # => false
File.chmod(0o755, "a.txt")
p(File.executable?("a.txt"))        # => true
```

## size

`File.size(String)`

The size of the file in bytes (an Integer). A missing path is an `IOError`.

```ruby
File.write("a.txt", "日本")
p(File.size("a.txt"))               # => 6
```

```ruby error
File.size("nope")                   # !> IOError: File.size: No such file or directory
```

## mtime, atime

`File.mtime(String)`

`File.atime(String)`

The time of the last modification or the last access, as a Time. A missing path is an `IOError`.

```ruby
File.write("a.txt", "")
File.utime(Time.at(0), Time.at(60), "a.txt")
p(Time.to_i(File.atime("a.txt")))   # => 0
p(Time.to_i(File.mtime("a.txt")))   # => 60
p(File.mtime("a.txt") <= Time.now)  # => true
```

## ftype

`File.ftype(String)`

The kind as a String: `"file"`, `"directory"`, `"link"` (the link itself; not followed), `"characterSpecial"`, and the others as in Ruby. A missing path is an `IOError`.

```ruby
File.write("a.txt", "")
File.symlink("a.txt", "l2.txt")
p(File.ftype("a.txt"))              # => "file"
p(File.ftype("."))                  # => "directory"
p(File.ftype("l2.txt"))              # => "link"
```

## identical?

`File.identical?(String, String)`

True when the two paths name the same file (links are followed; a hard link counts as the same). False when either is missing.

```ruby
File.write("a.txt", "")
File.write("b.txt", "")
File.symlink("a.txt", "l3.txt")
p(File.identical?("a.txt", "l3.txt"))   # => true
p(File.identical?("a.txt", "b.txt"))   # => false
p(File.identical?("a.txt", "nope"))    # => false
```

## rename

`File.rename(String, String)`

Moves (renames) the file and returns 0. An existing file at the new path is replaced. A missing source is an `IOError`.

```ruby
File.write("a.txt", "x")
p(File.rename("a.txt", "b.txt"))    # => 0
p(File.exist?("a.txt"))             # => false
p(File.read("b.txt"))               # => "x"
```

```ruby error
File.rename("nope", "b.txt")        # !> IOError: File.rename: No such file or directory
```

## delete, unlink

`File.delete(String)`

`File.unlink(String, *String)`

Remove files. `delete` takes one path and returns 1; `unlink` takes one or more and returns how many it removed (an Integer). A directory cannot be removed this way (`Dir.rmdir`). A missing path is an `IOError` (Ruby's `Errno::ENOENT`).

```ruby
File.write("a.txt", "")
File.write("b.txt", "")
File.write("c.txt", "")
p(File.delete("a.txt"))             # => 1
p(File.unlink("b.txt", "c.txt"))    # => 2
p(File.exist?("c.txt"))             # => false
```

```ruby error
File.delete("nope")                 # !> IOError: File.delete: No such file or directory
```

## symlink, link

`File.symlink(String, String)`

`File.link(String, String)`

`symlink(target, link)` makes a symbolic link, `link(target, link)` a hard link; both return 0. An existing `link` path is an `IOError`.

```ruby
File.write("a.txt", "x")
p(File.symlink("a.txt", "s.txt"))   # => 0
p(File.link("a.txt", "h.txt"))      # => 0
p(File.read("s.txt"))               # => "x"
p(File.symlink?("h.txt"))           # => false
p(File.identical?("a.txt", "h.txt"))   # => true
```

```ruby error
File.write("a.txt", "")
File.symlink("a.txt", "a.txt")      # !> IOError: File.symlink: File exists
```

## readlink

`File.readlink(String)`

The path a symbolic link points to, as a String (as written; not resolved). A path that is not a link is an `IOError`.

```ruby
File.write("a.txt", "")
File.symlink("a.txt", "l4.txt")
p(File.readlink("l4.txt"))           # => "a.txt"
```

```ruby error
File.write("a.txt", "")
File.readlink("a.txt")              # !> IOError: File.readlink: Invalid argument
```

## chmod

`File.chmod(Integer, *String)`

Sets the permission bits to `mode` (`0o644`, ...) and returns the number of files changed; 0 with no paths. A missing path is an `IOError`.

```ruby
File.write("a.txt", "")
File.write("b.sh", "")
p(File.chmod(0o755, "a.txt", "b.sh"))   # => 2
p(File.executable?("b.sh"))         # => true
p(File.chmod(0o644))                # => 0
```

## utime

`File.utime(Time|Nil, Time|Nil, *String)`

`utime(atime, mtime, *paths)`: sets the access and modification times and returns the number of files changed. nil means now. A missing path is an `IOError`.

```ruby
File.write("a.txt", "")
p(File.utime(Time.at(10), Time.at(20), "a.txt"))   # => 1
p(Time.to_i(File.atime("a.txt")))   # => 10
p(Time.to_i(File.mtime("a.txt")))   # => 20
File.utime(nil, nil, "a.txt")
p(File.mtime("a.txt") > Time.at(20))   # => true
```

## basename

`File.basename(String, [String])`

The last component of the path. A second argument names an extension (`".rb"`) to strip; `".*"` strips any extension. The file system is not consulted.

```ruby
p(File.basename("/a/b/c.rb"))       # => "c.rb"
p(File.basename("/a/b/c.rb", ".rb"))   # => "c"
p(File.basename("c.tar.gz", ".*"))  # => "c.tar"
p(File.basename("/a/b/"))           # => "b"
```

## dirname

`File.dirname(String, [Integer])`

The directory part, without the last component. A second argument `n` goes up `n` levels (1 when omitted; 0 gives the path unchanged). Without a directory part it is `"."`.

```ruby
p(File.dirname("/a/b/c.rb"))        # => "/a/b"
p(File.dirname("/a/b/c.rb", 2))     # => "/a"
p(File.dirname("c.rb"))             # => "."
```

## extname

`File.extname(String)`

The extension of the last component, with its dot; `""` when there is none. A name that starts with a dot (`.bashrc`) has no extension.

```ruby
p(File.extname("c.tar.gz"))         # => ".gz"
p(File.extname("c"))                # => ""
p(File.extname(".bashrc"))          # => ""
```

## split

`File.split(String)`

The two-element Tuple `[dirname, basename]` (String, String). In Ruby it is an Array; in Sake it is a Tuple, taken apart with `dir, base = File.split(p)`.

```ruby
dir, base = File.split("/a/b/c.rb")
p(dir)                              # => "/a/b"
p(base)                             # => "c.rb"
p(File.split("c.rb"))               # => [".", "c.rb"]
```

## join

`File.join(*String|Array)`

The parts joined with `/`; doubled `/` are collapsed. The arguments are Strings or Arrays of Strings (nested Arrays are flattened); with no arguments it is `""`. Another type is a `type` problem statically.

```ruby
p(File.join("a", "b", "c.rb"))      # => "a/b/c.rb"
p(File.join("a/", "/b"))            # => "a/b"
p(File.join(Any["a", "b"], "c"))  # => "a/b/c"
p(File.join())                      # => ""
```

```ruby error
File.join(1)                        # !> File.join: argument 1 must be String|Array, but is Integer
```

## expand_path, absolute_path

`File.expand_path(String, [String])`

`File.absolute_path(String, [String])`

Make a relative path absolute. The second argument is the base directory, the current directory (`Dir.pwd`) when omitted. `.` and `..` are resolved, but the file system is not consulted and links are not followed. They differ on `~`: `expand_path` expands `~` to the home directory (`~user` for an unknown user is an `ArgumentError`), `absolute_path` leaves it as it is (as in Ruby).

```ruby
p(File.expand_path("/x/../y"))      # => "/y"
p(File.expand_path("b", "/a"))      # => "/a/b"
p(File.absolute_path("b", "/a"))    # => "/a/b"
p(File.expand_path("a") == File.join(Dir.pwd, "a"))   # => true
p(File.expand_path("~") == Dir.home)                  # => true
p(File.absolute_path("~") == File.join(Dir.pwd, "~")) # => true
```

## absolute_path?

`File.absolute_path?(String)`

True when the path is absolute (starts with `/`). The file system is not consulted.

```ruby
p(File.absolute_path?("/a/b"))      # => true
p(File.absolute_path?("a/b"))       # => false
```

## realpath

`File.realpath(String, [String])`

The real absolute path with every symbolic link followed. The second argument is the base directory for a relative path. The path must exist; a missing one is an `IOError` (the difference from `expand_path`).

```ruby
File.write("a.txt", "")
File.symlink("a.txt", "l5.txt")
p(File.realpath("l5.txt") == File.join(Dir.pwd, "a.txt"))   # => true
p(File.realpath("l5.txt", Dir.pwd) == File.realpath("./a.txt"))   # => true
```

```ruby error
File.realpath("nope")               # !> IOError: File.realpath: No such file or directory
```

# Dir

Dir is the group of operations that work on a directory by its path (a String). There is no value of type Dir (no `Dir.open`, no `Dir` object as in Ruby); every operation has the name and the result of Ruby's class method of the same name ([Built-ins](../09-builtins.md)). Listings come back as Arrays of Strings.

A failing system call (a missing directory, removing a directory that is not empty; Ruby's `Errno::ENOENT` and the like) is an `IOError` with Ruby's message ([Exceptions](../08-exceptions.md)). `exist?` never fails and returns false; `glob` returns an empty Array when nothing matches.

Dir has no operators. The examples run in an empty directory and create directories there.

## children, entries

`Dir.children(String)`

`Dir.entries(String)`

The names (names only, not paths) in the directory, as an Array. `children` leaves out `.` and `..`; `entries` includes them. The order is the OS's and not defined, so `Array.sort` before relying on it. A missing directory is an `IOError`.

```ruby
Dir.mkdir("d1")
File.write("d1/a.txt", "")
File.write("d1/b.rb", "")
p(Array.sort(Dir.children("d1")))   # => ["a.txt", "b.rb"]
p(Array.sort(Dir.entries("d1")))    # => [".", "..", "a.txt", "b.rb"]
```

```ruby error
Dir.children("nope")                # !> IOError: Dir.children: No such file or directory
```

## each_child

`Dir.each_child(String) { }`

Passes each name of `children` to the block in turn and returns nil (Ruby returns the Dir; Sake returns nil). The block's value is not used. The order is undefined, as for `children`. A missing directory is an `IOError`.

```ruby
Dir.mkdir("d2")
File.write("d2/x", "")
File.write("d2/y", "")
names = Any[]
r = Dir.each_child("d2") { |n| Array.push(names, n) }
p(Array.sort(names))                # => ["x", "y"]
p(r)                                # => nil
```

## glob

`Dir.glob(String|Array, [base: String])`

The paths that match the pattern, as an Array (Ruby's `Dir.glob`, with `*`, `?`, `[...]`, `{a,b}`, and `**/`). The pattern is a String or an Array of Strings (a path matching any of them). The result is sorted, as Ruby's, and a name that starts with a dot does not match `*`. With the keyword `base:` the search starts in that directory and the result holds **relative names**. When nothing matches, or the `base:` directory is missing, the result is an empty Array; it does not fail.

```ruby
Dir.mkdir("d3")
File.write("d3/a.txt", "")
File.write("d3/b.rb", "")
Dir.mkdir("d3/sub")
File.write("d3/sub/c.txt", "")
p(Dir.glob("d3/*.rb"))              # => ["d3/b.rb"]
p(Dir.glob("d3/**/*.txt"))          # => ["d3/a.txt", "d3/sub/c.txt"]
p(Dir.glob(Any["d3/*.txt", "d3/*.rb"]))   # => ["d3/a.txt", "d3/b.rb"]
p(Dir.glob("*", base: "d3"))        # => ["a.txt", "b.rb", "sub"]
p(Dir.glob("nope/*"))               # => []
```

## exist?

`Dir.exist?(String)`

True when a directory is at the path. A file or a missing path is false; it never fails.

```ruby
Dir.mkdir("d4")
File.write("f4", "")
p(Dir.exist?("d4"))                 # => true
p(Dir.exist?("f4"))                 # => false
p(Dir.exist?("nope"))               # => false
```

## empty?

`Dir.empty?(String)`

True when the directory holds nothing but `.` and `..`. A missing directory is an `IOError`.

```ruby
Dir.mkdir("d5")
p(Dir.empty?("d5"))                 # => true
File.write("d5/a", "")
p(Dir.empty?("d5"))                 # => false
```

## mkdir

`Dir.mkdir(String, [Integer])`

Creates one directory and returns 0. The second argument is the permission bits (`0o700`; when omitted, `0o777` reduced by the umask). A missing parent, or an existing entry of the same name, is an `IOError` (there is no counterpart of `mkdir -p`).

```ruby
p(Dir.mkdir("d6"))                  # => 0
p(Dir.mkdir("d6/sub", 0o700))       # => 0
p(Dir.exist?("d6/sub"))             # => true
```

```ruby error
Dir.mkdir("d6b")
Dir.mkdir("d6b")                    # !> IOError: Dir.mkdir: File exists
```

## rmdir, unlink

`Dir.rmdir(String)`

`Dir.unlink(String)`

Remove an empty directory and return 0 (the two are the same, as in Ruby). A directory that is not empty, or a missing path, is an `IOError`. Files are removed with `File.delete`.

```ruby
Dir.mkdir("d7")
p(Dir.rmdir("d7"))                  # => 0
p(Dir.exist?("d7"))                 # => false
Dir.mkdir("d7")
p(Dir.unlink("d7"))                 # => 0
```

```ruby error
Dir.mkdir("d7b")
File.write("d7b/a", "")
Dir.rmdir("d7b")                    # !> IOError: Dir.rmdir: Directory not empty
```

## pwd

`Dir.pwd()`

The absolute path of the current directory. Sake has no `Dir.chdir`, so it does not change while the program runs.

```ruby
p(File.absolute_path?(Dir.pwd))     # => true
p(Dir.pwd == File.expand_path("."))   # => true
```

## home

`Dir.home([String])`

The absolute path of the home directory; with a user name, that user's home. An unknown user is an `ArgumentError`.

```ruby
p(Dir.home == ENV.fetch("HOME"))    # => true
p(File.expand_path("~") == Dir.home)   # => true
```

```ruby error
Dir.home("no_such_user_xyz")        # !> ArgumentError: Dir.home: user no_such_user_xyz doesn't exist
```

## tmpdir

`Dir.tmpdir()`

The absolute path of the directory for temporary files (`$TMPDIR`, or `/tmp`).

```ruby
p(Dir.exist?(Dir.tmpdir))           # => true
p(File.absolute_path?(Dir.tmpdir))  # => true
```

## mktmpdir

`Dir.mktmpdir([String], [String]) [{ }]`

Creates a new temporary directory. The first argument is a prefix for its name (`"d"` when omitted), the second the directory to create it in (`Dir.tmpdir` when omitted). Without a block it returns the path (a String) and removing it is the caller's job (`Dir.rmdir` only while it is empty). With a block, the path is passed to the block, the directory is removed **with its contents** after the block, and the block's value is returned (as Ruby's `tmpdir`). A missing parent directory is an `IOError`.

```ruby
t = Dir.mktmpdir("pfx", ".")
p(String.start_with?(File.basename(t), "pfx"))   # => true
p(Dir.exist?(t))                    # => true
Dir.rmdir(t)
kept = ""
v = Dir.mktmpdir { |dir| File.write(File.join(dir, "f"), "1"); kept = dir; 42 }
p(v)                                # => 42
p(Dir.exist?(kept))                 # => false
```

# ENV

ENV is the group of operations that read and write the process's environment variables. Ruby's `ENV["X"]` is written `ENV.get("X")`, and `ENV["X"] = v` is `ENV.set("X", v)` (in Sake `ENV` is not a value and cannot be indexed, see [Built-ins](../09-builtins.md)). Names and values are Strings.

A change is seen by this process and by the child processes started afterwards with `Open3` or `Kernel.system`. `ENV.to_h` is a copy; changing it does not change the environment, and `ENV.replace(h)` puts a whole Hash back as the environment.

ENV has no operators. The examples use names that start with `SAKE_DOC_` and remove them at the end.

## get

`ENV.get(String)`

The variable's value (a String), or nil when it is not set (Ruby's `ENV["X"]`). The result is `String | nil`, so `--strict` (level 2) reports using it unchecked as a `nil` problem. For a variable that must be present use `fetch`.

```ruby
ENV.set("SAKE_DOC_A", "1")
p(ENV.get("SAKE_DOC_A"))            # => "1"
p(ENV.get("SAKE_DOC_NOPE"))         # => nil
v = ENV.get("SAKE_DOC_A")
if v
  p(String.to_i(v) + 1)             # => 2
end
ENV.delete("SAKE_DOC_A")
```

```ruby error
home = ENV.get("HOME")
p(String.size(home))                # !> argument 1 may be nil
```

## fetch

`ENV.fetch(String, [String])`

The variable's value (a String). When it is not set, the second argument is returned if given, else it is a `KeyError` (Ruby's `ENV.fetch`; there is no block form). The result is never nil.

```ruby
ENV.set("SAKE_DOC_B", "x")
p(ENV.fetch("SAKE_DOC_B"))          # => "x"
p(ENV.fetch("SAKE_DOC_NOPE", "dflt"))   # => "dflt"
ENV.delete("SAKE_DOC_B")
```

```ruby error
ENV.fetch("SAKE_DOC_NOPE")          # !> KeyError: ENV.fetch: key not found: "SAKE_DOC_NOPE"
```

## set

`ENV.set(String, String)`

Sets the variable (replacing an existing value) and returns the value (Ruby's `ENV["X"] = v`). The value must be a String; another type is a `type` problem statically (removing is `delete`). An empty name, or one containing `=`, is refused by the OS: an `ArgumentError` with the OS's message (`Invalid argument - setenv()`), rescuable.

```ruby
p(ENV.set("SAKE_DOC_C", "1"))       # => "1"
p(ENV.set("SAKE_DOC_C", "2"))       # => "2"
out, status = Open3.capture2("sh", "-c", "echo $SAKE_DOC_C")
p(out)                              # => "2\n"
ENV.delete("SAKE_DOC_C")
```

```ruby error
ENV.set("SAKE_DOC_C", 1)            # !> ENV.set: argument 2 must be String, but is Integer
```

```ruby error
ENV.set("", "v")                    # !> ArgumentError: ENV.set: Invalid argument - setenv()
```

## key?

`ENV.key?(String)`

True when the variable is set (also when its value is the empty string).

```ruby
ENV.set("SAKE_DOC_D", "")
p(ENV.key?("SAKE_DOC_D"))           # => true
p(ENV.key?("SAKE_DOC_NOPE"))        # => false
ENV.delete("SAKE_DOC_D")
```

## delete

`ENV.delete(String)`

Removes the variable and returns the value it had (a String), or nil when it was not set (`String | nil`, level 2).

```ruby
ENV.set("SAKE_DOC_E", "1")
p(ENV.delete("SAKE_DOC_E"))         # => "1"
p(ENV.delete("SAKE_DOC_E"))         # => nil
p(ENV.key?("SAKE_DOC_E"))           # => false
```

## keys

`ENV.keys()`

The names of all set variables as an Array of Strings, in no defined order.

```ruby
ENV.set("SAKE_DOC_F", "1")
p(Array.include?(ENV.keys, "SAKE_DOC_F"))   # => true
p(Array.include?(ENV.keys, "SAKE_DOC_NOPE"))   # => false
ENV.delete("SAKE_DOC_F")
```

## to_h

`ENV.to_h()`

A copy of the whole environment as a Hash (String to String). Being a copy, changing the Hash does not change the environment, and a later `ENV.set` does not show in the Hash.

```ruby
ENV.set("SAKE_DOC_G", "1")
h = ENV.to_h
p(h["SAKE_DOC_G"])                  # => "1"
h["SAKE_DOC_H"] = "2"
p(ENV.key?("SAKE_DOC_H"))           # => false
ENV.delete("SAKE_DOC_G")
p(Hash.key?(h, "SAKE_DOC_G"))       # => true
```

## replace

`ENV.replace(Hash)`

Replaces the whole environment with the Hash's pairs and returns nil (Ruby's `ENV.replace`): every variable not in the Hash is removed, every pair is set. Keys and values must be Strings: the checker rejects a Hash whose key or value type is anything else (`the value must be String, but is Integer`, `argument key must be String, but is :a`), also through a function's parameter. Where it cannot see the types (a program run with `--strict=0`), a non-String key or value is a `TypeError` at run time, `keys and values must be String, got String => Integer`, raised before anything changes, so the environment is untouched. The typical use is to restore a copy taken with `ENV.to_h`.

```ruby
saved = ENV.to_h
p(ENV.replace(Hash.of("SAKE_DOC_R" => "1")))   # => nil
p(ENV.keys)                         # => ["SAKE_DOC_R"]
p(ENV.get("SAKE_DOC_R"))            # => "1"
ENV.replace(saved)
p(ENV.key?("SAKE_DOC_R"))           # => false
p(ENV.to_h == saved)                # => true
```

```ruby error
ENV.replace(Hash.of("SAKE_DOC_R" => 1))   # !> ENV.replace: the value must be String, but is Integer [type]
```

# Process

Process is the group of operations that read information about this process and its clocks. Only Ruby's `Process.pid` and `Process.clock_gettime` exist; there is no `fork`, `spawn`, `wait`, or `kill` (child processes are started with [Open3](Open3.md) and `Kernel.system`). Ruby's constant `Process::CLOCK_MONOTONIC` is the operation `Process.CLOCK_MONOTONIC` in Sake (as `Math.PI` is: Sake has no value constants, see [Built-ins](../09-builtins.md)).

Process has no operators. The examples print only values that do not depend on the environment.

## pid

`Process.pid()`

The ID of this process (a positive Integer). A child started with `Open3` sees it as `$PPID`.

```ruby
p(Process.pid > 0)                  # => true
out, status = Open3.capture2("sh", "-c", "echo $PPID")
p(String.to_i(out) == Process.pid)  # => true
```

## clock_gettime

`Process.clock_gettime(Integer, [Symbol])`

The current value of the clock `clock` (the value of one of the three operations below), Ruby's `Process.clock_gettime`. To measure elapsed time use `Process.CLOCK_MONOTONIC`: it never goes back when the wall clock is adjusted. The second argument is a unit Symbol, as Ruby's, and decides the result's type: without it or with `:float_second` (the default), `:float_millisecond`, `:float_microsecond` the result is a Float; with `:second`, `:millisecond`, `:microsecond`, `:nanosecond` it is an Integer. The checker reads the unit when it is written as a literal Symbol; a unit held in a variable gives `Integer | Float`, which a `case`/`in` has to split before an Integer or Float operation takes it. An unknown unit (`unexpected unit: bogus`) and an unknown clock number (`Invalid argument - clock_gettime(999)`) are an `ArgumentError`.

```ruby
t0 = Process.clock_gettime(Process.CLOCK_MONOTONIC)
t1 = Process.clock_gettime(Process.CLOCK_MONOTONIC)
p(t1 >= t0)                         # => true
p(Process.clock_gettime(Process.CLOCK_REALTIME) > 1.0e9)   # => true
ms = Process.clock_gettime(Process.CLOCK_MONOTONIC, :millisecond)
p(ms > 0)                           # => true
p(Integer.to_s(ms) == "#{ms}")      # => true
f = Process.clock_gettime(Process.CLOCK_MONOTONIC, :float_millisecond)
p(Float.floor(f) >= 0)              # => true
```

```ruby error
Process.clock_gettime(Process.CLOCK_MONOTONIC, :bogus)   # !> ArgumentError: Process.clock_gettime: unexpected unit: bogus
```

```ruby error
Process.clock_gettime(999)          # !> ArgumentError: Process.clock_gettime: Invalid argument - clock_gettime(999)
```

```ruby error
unit = :millisecond
p(Integer.to_s(Process.clock_gettime(Process.CLOCK_MONOTONIC, unit)))   # !> Integer.to_s: argument 1 must be Integer, but can be Float
```

## CLOCK_REALTIME, CLOCK_MONOTONIC, CLOCK_PROCESS_CPUTIME_ID

`Process.CLOCK_REALTIME()`

`Process.CLOCK_MONOTONIC()`

`Process.CLOCK_PROCESS_CPUTIME_ID()`

The clock numbers (Integers) for `clock_gettime`. `CLOCK_REALTIME` is the wall clock (seconds since 1970-01-01 UTC, the value of `Time.now`); `CLOCK_MONOTONIC` has an arbitrary origin and never goes back (for elapsed time); `CLOCK_PROCESS_CPUTIME_ID` is the CPU time this process has used. The numbers themselves are the OS's (0, 1, and 2 on Linux). Ruby's other clocks (`CLOCK_THREAD_CPUTIME_ID`, ...) do not exist.

```ruby
p(Process.CLOCK_REALTIME != Process.CLOCK_MONOTONIC)   # => true
wall = Process.clock_gettime(Process.CLOCK_REALTIME)
p(Float.abs(wall - Time.to_f(Time.now)) < 1.0)         # => true
p(Process.clock_gettime(Process.CLOCK_PROCESS_CPUTIME_ID) >= 0.0)   # => true
```

# Open3

Open3 is the group of operations that run a child process and collect its output and exit status (`capture2`, `capture2e`, and `capture3` of Ruby's `open3` library; they are built in because a Sake program has no other way to start a child process. For just true/false without the output use `Kernel.system`, see [Built-ins](../09-builtins.md)).

Ruby's result is an Array `[out, status]` with a `Process::Status`; in Sake it is a **Tuple** and the exit status an **Integer** (the exit code, or -1 when the child was killed by a signal). Take it apart with multiple assignment, `out, status = Open3.capture2(...)`. A command that is not found or cannot start is an `IOError`. As in Ruby, a single argument is interpreted by the shell; with two or more, the first is the program and the rest its arguments (no shell). No standard input can be given (there is no `stdin_data:`).

Open3 has no operators.

## capture2

`Open3.capture2(String, *String)`

Runs the command and returns the Tuple `[stdout, status]` (String, Integer). The child's stderr goes to this process's stderr.

```ruby
out, status = Open3.capture2("echo", "hi")
p(out)                              # => "hi\n"
p(status)                           # => 0
out2, status2 = Open3.capture2("sh", "-c", "echo $0; exit 3")
p(out2)                             # => "sh\n"
p(status2)                          # => 3
```

```ruby error
Open3.capture2("no_such_command_xyz")   # !> IOError: Open3.capture2: No such file or directory - no_such_command_xyz
```

## capture2e

`Open3.capture2e(String, *String)`

Runs the command and returns `[output, status]` (String, Integer) with stdout and stderr merged into one String (Ruby's `capture2e`).

```ruby
out, status = Open3.capture2e("sh", "-c", "echo err >&2; echo out; exit 2")
p(out)                              # => "err\nout\n"
p(status)                           # => 2
```

## capture3

`Open3.capture3(String, *String)`

Runs the command and returns the Tuple `[stdout, stderr, status]` (String, String, Integer).

```ruby
out, err, status = Open3.capture3("sh", "-c", "echo err >&2; echo out; exit 2")
p(out)                              # => "out\n"
p(err)                              # => "err\n"
p(status)                           # => 2
p(Open3.capture3("true"))           # => ["", "", 0]
killed = Open3.capture3("sh", "-c", "kill -9 $$")
p(killed[2])                        # => -1
```

# Zlib

Zlib is the group of operations that compress and decompress a String in the zlib and gzip formats (Ruby's `Zlib.deflate`, `inflate`, `gzip`, and `gunzip`; built in for speed, see [Built-ins](../09-builtins.md)). There is no compression level and no stream form (no `Zlib::Deflate` class).

Both the compressed and the decompressed result are binary Strings (encoding `ASCII-8BIT`). Decompressing a UTF-8 string gives the same bytes in a different encoding, so the result is not `==` to the original: pass it through `String.force_encoding(s, "UTF-8")` to use it as text. Corrupt input, or input of the other format (gzip data given to `inflate`), is an `ArgumentError` with zlib's message (`incorrect header check`, ...); there is no exception type for Ruby's `Zlib::DataError` and the like.

Zlib has no operators.

## deflate, inflate

`Zlib.deflate(String)`

`Zlib.inflate(String)`

`deflate` returns the String compressed in the zlib format (RFC 1950); `inflate` returns the original String. The empty string can be compressed and decompressed, but `inflate("")` is an `ArgumentError` (`buffer error`).

```ruby
s = String.*("hello ", 100)
z = Zlib.deflate(s)
p(String.bytesize(z) < String.bytesize(s))   # => true
p(Zlib.inflate(z) == s)                      # => true
p(String.encoding(Zlib.inflate(z)))          # => "ASCII-8BIT"
j = Zlib.inflate(Zlib.deflate("日本"))
p(j == "日本")                               # => false
p(String.force_encoding(j, "UTF-8") == "日本")   # => true
```

```ruby error
Zlib.inflate("not compressed")      # !> ArgumentError: Zlib.inflate: incorrect header check
```

## gzip, gunzip

`Zlib.gzip(String)`

`Zlib.gunzip(String)`

`gzip` returns the String compressed in the gzip format (RFC 1952, the format of the `gzip` command and of `.gz` files; the first two bytes are `0x1f 0x8b`); `gunzip` returns the original String. The result of `deflate` cannot be given to `gunzip`, nor that of `gzip` to `inflate` (`ArgumentError`).

```ruby
g = Zlib.gzip("hello")
p(String.getbyte(g, 0))             # => 31
p(String.getbyte(g, 1))             # => 139
p(Zlib.gunzip(g))                   # => "hello"
File.write("a.gz", g)
out, status = Open3.capture2("sh", "-c", "gunzip -c a.gz")
p(out)                              # => "hello"
```

```ruby error
Zlib.gunzip(Zlib.deflate("x"))      # !> ArgumentError: Zlib.gunzip:
```

# Thread

Thread is the type of a thread that runs a block concurrently. `Thread.new { ... }` makes one; `Thread.value(t)` or `Thread.join(t)` waits for its end. It is Ruby's `Thread` (a Ruby thread), but only the operations listed here exist: no `Thread#[]`, `status`, `priority`, or `Thread.pass`. There is no Thread literal; values come from `Thread.new` and `Thread.current` ([Values and types](../03-values.md), [Built-ins](../09-builtins.md)).

- **Variables.** A block shares the variables around it, but for a thread the variables of the blocks around `Thread.new` (their parameters and locals) and the thread block's own are copied when the thread starts; the function's (or the top level's) variables stay shared. A shared counter is updated inside `Mutex.synchronize` ([Mutex](Mutex.md)).
- **Leaving the block.** `break` and `return` out of a thread block are static errors (the block runs after `Thread.new` has returned); `next v` ends it with `v`.
- **Errors.** An exception inside a thread is not reported there; it is raised in the caller of `Thread.value` or `Thread.join`. A thread that ended with an error that no `Thread.value` / `Thread.join` read is reported on stderr when the program ends (the exit status is not changed): `PATH: a thread ended with an error that no Thread.value / Thread.join read:` followed by the error's report, indented. Every thread stops when the main program ends; one that is still running then is not reported.
- **Checking.** The checker runs the block once, at `Thread.new`, and makes its value the type of `Thread.value`. Interleavings are not analyzed; each operation still checks its arguments at run time.

The only operators on Threads are `==` and `!=` (equal for the same thread).

## new

`Thread.new() { }`

Starts the block in a new thread and returns the Thread at once. The block's value is the thread's value (`Thread.value`). The block is required and takes no parameters.

```ruby
t = Thread.new { 1 + 2 }
p(t)                                # => #<Thread>
p(Thread.value(t))                  # => 3
ws = Array.map(Any[1, 2, 3]) { |i| Thread.new { i * 10 } }
p(Array.map(ws) { |w| Thread.value(w) })   # => [10, 20, 30]
```

```ruby error
t = Thread.new { return 1 }         # !> `return` outside a function
```

## value

`Thread.value(x)`

Waits for the thread to end and returns the block's value (Ruby's `t.value`), the same value on every call. When the thread ended with an exception, that exception is raised here (on every call). A thread stopped by `Thread.kill` has the value nil. The result has the block's type; a block that may return nil gets the `nil` check. A thread cannot wait for itself (`Thread.current`): that is a `ThreadError` (`Target thread must not be current thread`).

```ruby
t = Thread.new { "done" }
p(Thread.value(t))                  # => "done"
p(Thread.value(t))                  # => "done"
e = Thread.new { raise ArgumentError, "boom" }
begin
  Thread.value(e)
rescue ArgumentError => err
  p(Exception.message(err))         # => "boom"
end
```

```ruby error
t = Thread.new { raise ArgumentError, "boom" }
Thread.value(t)                     # !> ArgumentError: boom
```

```ruby error
Thread.value(Thread.current)        # !> ThreadError: Thread.value: Target thread must not be current thread
```

When the program ends without any `value` or `join` having read a thread that raised, the error is reported on stderr (the program's exit status stays 0). The checker requires an empty stderr, so this is shown as it appears in a terminal:

```
t = Thread.new { raise ArgumentError, "boom" }
Thread.join(Thread.new { 1 })
p(1)                                # 1, then on stderr:
                                    # ex.sake: a thread ended with an error that no Thread.value / Thread.join read:
                                    #   ex.sake:1: in <main>: ArgumentError: boom
```

## join

`Thread.join(x, [Integer|Float|Rational])`

Waits for the thread to end and returns the thread. With a number of seconds as the second argument, it stops waiting after that time and returns nil when the thread is still running (the thread goes on). Only the two-argument form is `Thread | nil`, checked at level 2: `if Thread.join(t, 5)` tells whether the thread finished in time (`Timeout.timeout` of `sakelib` is built this way). The thread's exception is raised here, as with `value`. A thread cannot wait for itself (`ThreadError`).

```ruby
q = Queue.new
t = Thread.new { Queue.pop(q) }
p(Thread.join(t, 0.01))             # => nil
p(Thread.alive?(t))                 # => true
Queue.push(q, :go)
p(Thread.join(t) == t)              # => true
p(Thread.join(t, 1) == t)           # => true
```

## alive?

`Thread.alive?(x)`

True while the thread is running (has not ended).

```ruby
q = Queue.new
t = Thread.new { Queue.pop(q) }
p(Thread.alive?(t))                 # => true
Queue.push(q, 1)
Thread.join(t)
p(Thread.alive?(t))                 # => false
p(Thread.alive?(Thread.current))    # => true
```

## current

`Thread.current()`

The running thread (the main program's, or one made by `Thread.new`). All values for the same thread are `==`.

```ruby
me = Thread.current
p(Thread.current == me)             # => true
t = Thread.new { Thread.current }
p(Thread.value(t) == t)             # => true
p(Thread.value(t) == me)            # => false
```

## kill

`Thread.kill(x)`

Stops the thread where it is and returns the thread (Ruby's `t.kill`). Its `ensure` clauses run. A killed thread's `value` is nil and its `alive?` false. A thread that has already ended is left alone.

```ruby
q = Queue.new
t = Thread.new { Queue.pop(q) }
p(Thread.kill(t) == t)              # => true
Thread.join(t)
p(Thread.alive?(t))                 # => false
p(Thread.value(t))                  # => nil
```

## raise

`Thread.raise(x, String)`

Raises a `RuntimeError` with the message in the thread, where it is, and returns the thread. Unless the thread rescues it, the thread ends with it and the caller of `value` or `join` gets that `RuntimeError` (`Timeout.timeout` of `sakelib` interrupts a block this way). A thread that has already ended is left alone. In a program that calls `Thread.raise`, the checker counts `Thread.value` and `Thread.join` as raising `RuntimeError`, so `rescue RuntimeError => e` around them is accepted (in a program without `Thread.raise` it is a `rescue` problem: the body never raises it).

```ruby
q = Queue.new
t = Thread.new { Queue.pop(q) }
p(Thread.raise(t, "stop") == t)     # => true
begin
  Thread.value(t)
rescue RuntimeError => e
  p(e)                              # => #<RuntimeError: stop>
end
begin
  Thread.join(t)
rescue RuntimeError => e
  p(Exception.message(e))           # => "stop"
end
done = Thread.new { 1 }
Thread.join(done)
Thread.raise(done, "late")
p(Thread.value(done))               # => 1
```

## ==, !=

`Thread.==(x, Any)`

`Thread.!=(x, Any)`

Two values are equal when they are the same thread (`!=` is the negation). A value that is not a Thread is never equal.

```ruby
t = Thread.new { 1 }
p(t == t)                           # => true
p(t != Thread.new { 2 })            # => true
p(t == 1)                           # => false
```

# Mutex

Mutex is the type of a lock for mutual exclusion between threads (Ruby's `Thread::Mutex`). `Mutex.new` makes one; `Mutex.synchronize(m) { ... }` holds it for the block. It is for updating state that threads share (a function's or the top level's variables, the contents of an Array or Hash) from several threads ([Thread](Thread.md), [Built-ins](../09-builtins.md)). There is no literal.

The lock is not reentrant: a `lock` by the thread that holds it is a `ThreadError` (`deadlock; recursive locking`). An `unlock` by a thread that does not hold it is a `ThreadError` too. `ThreadError` is a built-in exception that can be rescued ([Exceptions](../08-exceptions.md)).

The only operators on Mutexes are `==` and `!=` (equal for the same Mutex).

## new

`Mutex.new()`

A new Mutex that nobody holds.

```ruby
m = Mutex.new
p(m)                                # => #<Mutex>
p(Mutex.locked?(m))                 # => false
```

## synchronize

`Mutex.synchronize(x) { }`

Takes the lock, runs the block, releases the lock when the block ends (also by an exception), and returns the block's value. While another thread holds the lock it waits. Usually this form is all that is needed. Nesting `synchronize` on a Mutex the thread already holds is a `ThreadError` (`deadlock; recursive locking`), as with `lock`; it can be rescued, and the outer `synchronize` releases the lock on the way out.

```ruby
m = Mutex.new
count = 0
ths = Array.map(Any[1, 2, 3, 4]) { |i|
  Thread.new { Array.each(Array.new(100, 0)) { |_| Mutex.synchronize(m) { count += 1 } } }
}
Array.each(ths) { |t| Thread.join(t) }
p(count)                            # => 400
r = Mutex.synchronize(m) { p(Mutex.locked?(m)); "val" }   # => true
p(r)                                # => "val"
p(Mutex.locked?(m))                 # => false
begin
  Mutex.synchronize(m) { Mutex.synchronize(m) { 1 } }
rescue ThreadError => e
  p(Exception.message(e))           # => "deadlock; recursive locking"
end
p(Mutex.locked?(m))                 # => false
```

```ruby error
m = Mutex.new
Mutex.synchronize(m) { Mutex.synchronize(m) { 1 } }   # !> ThreadError: Mutex.synchronize: deadlock; recursive locking
```

## lock

`Mutex.lock(x)`

Takes the lock and returns the Mutex, waiting while another thread holds it. A `ThreadError` when this thread already holds it. It pairs with `unlock`; `synchronize` is the usual form, since it cannot forget to release on an exception.

```ruby
m = Mutex.new
p(Mutex.lock(m) == m)               # => true
p(Mutex.locked?(m))                 # => true
Mutex.unlock(m)
```

```ruby error
m = Mutex.new
Mutex.lock(m)
Mutex.lock(m)                       # !> ThreadError: Mutex.lock: deadlock; recursive locking
```

## unlock

`Mutex.unlock(x)`

Releases the lock and returns the Mutex. A Mutex this thread does not hold (held by nobody, or by another thread) is a `ThreadError`.

```ruby
m = Mutex.new
Mutex.lock(m)
p(Mutex.unlock(m) == m)             # => true
p(Mutex.locked?(m))                 # => false
```

```ruby error
m = Mutex.new
Mutex.unlock(m)                     # !> ThreadError: Mutex.unlock: Attempt to unlock a mutex which is not locked
```

## try_lock

`Mutex.try_lock(x)`

Takes the lock and returns true when it is free; returns false without waiting when someone holds it (also when this thread holds it; no error).

```ruby
m = Mutex.new
p(Mutex.try_lock(m))                # => true
p(Mutex.try_lock(m))                # => false
other = Thread.new { Mutex.try_lock(m) }
p(Thread.value(other))              # => false
Mutex.unlock(m)
p(Mutex.try_lock(m))                # => true
```

## locked?

`Mutex.locked?(x)`

True when any thread holds the lock.

```ruby
m = Mutex.new
p(Mutex.locked?(m))                 # => false
Mutex.lock(m)
p(Mutex.locked?(m))                 # => true
p(Thread.value(Thread.new { Mutex.locked?(m) }))   # => true
```

## owned?

`Mutex.owned?(x)`

True when the current thread holds the lock; false when another thread does.

```ruby
m = Mutex.new
p(Mutex.owned?(m))                  # => false
Mutex.lock(m)
p(Mutex.owned?(m))                  # => true
p(Thread.value(Thread.new { Mutex.owned?(m) }))    # => false
```

## ==, !=

`Mutex.==(x, Any)`

`Mutex.!=(x, Any)`

Equal for the same Mutex (`!=` is the negation). Every `Mutex.new` is a different value.

```ruby
m = Mutex.new
p(m == m)                           # => true
p(m == Mutex.new)                   # => false
p(m != Mutex.new)                   # => true
```

# Queue

Queue is a thread-safe first-in, first-out queue for passing values between threads (Ruby's `Thread::Queue`). `Queue.push(q, x)` adds at the end; `Queue.pop(q)` takes from the front, waiting until someone pushes when the queue is empty. `Queue.close(q)` says that nothing more will come, and `pop` on a closed, empty Queue is nil. This is the producer-consumer pattern ([Thread](Thread.md), [Built-ins](../09-builtins.md)). There is no literal and no size limit (no `SizedQueue`).

**Element type.** To the checker, a Queue's element type is the union of everything pushed to it. The type of `Queue.pop` is that plus nil, and `--strict` (level 2) reports using it unchecked as a `nil` problem: `while (x = Queue.pop(q))` is the usual form.

The only operators on Queues are `==` and `!=` (equal for the same Queue).

## new

`Queue.new()`

A new, empty Queue.

```ruby
q = Queue.new
p(q)                                # => #<Queue>
p(Queue.empty?(q))                  # => true
```

## push

`Queue.push(x, Any)`

Adds the value at the end and returns the Queue. Any value can be pushed, nil too, but a nil cannot be told from "closed and empty" by `pop`. A push to a closed Queue is an `IOError` (Ruby's `ClosedQueueError`).

```ruby
q = Queue.new
p(Queue.push(q, 1) == q)            # => true
Queue.push(q, "two")
p(Queue.size(q))                    # => 2
```

```ruby error
q = Queue.new
Queue.close(q)
Queue.push(q, 1)                    # !> IOError: Queue.push: push to a closed Queue
```

## pop

`Queue.pop(x, [Integer|Float|Rational])`

Takes the value at the front and returns it. When the Queue is empty it waits until someone pushes or the Queue is closed; closed and empty, it returns nil without waiting. With a number of seconds as the second argument it returns nil when nothing arrives within that time (0 does not wait). The result is always "the element type | nil", checked at level 2.

```ruby
q = Queue.new
Queue.push(q, 1)
Queue.push(q, 2)
p(Queue.pop(q))                     # => 1
p(Queue.pop(q, 5))                  # => 2
p(Queue.pop(q, 0.01))               # => nil
workers = Array.map(Any[1, 2]) { |i|
  Thread.new { total = 0; while (x = Queue.pop(q)); total += x; end; total }
}
Array.each(Any[1, 2, 3, 4, 5, 6]) { |i| Queue.push(q, i) }
Queue.close(q)
p(Array.sum(Array.map(workers) { |w| Thread.value(w) }))   # => 21
```

```ruby error
q = Queue.new
Queue.push(q, 1)
p(Queue.pop(q) + 1)                 # !> the operands may be nil
```

## close

`Queue.close(x)`

Closes the Queue and returns it. Afterwards `push` is an `IOError`, `pop` returns the remaining values in order and then nil, and every waiting `pop` wakes up with nil. Closing again does nothing.

```ruby
q = Queue.new
Queue.push(q, 1)
p(Queue.close(q) == q)              # => true
p(Queue.pop(q))                     # => 1
p(Queue.pop(q))                     # => nil
```

## closed?

`Queue.closed?(x)`

True when the Queue has been closed.

```ruby
q = Queue.new
p(Queue.closed?(q))                 # => false
Queue.close(q)
p(Queue.closed?(q))                 # => true
```

## size

`Queue.size(x)`

The number of values currently in the Queue (an Integer).

```ruby
q = Queue.new
p(Queue.size(q))                    # => 0
Queue.push(q, :a)
Queue.push(q, :b)
p(Queue.size(q))                    # => 2
Queue.pop(q)
p(Queue.size(q))                    # => 1
```

## empty?

`Queue.empty?(x)`

True when nothing is in the Queue.

```ruby
q = Queue.new
p(Queue.empty?(q))                  # => true
Queue.push(q, 1)
p(Queue.empty?(q))                  # => false
```

## ==, !=

`Queue.==(x, Any)`

`Queue.!=(x, Any)`

Equal for the same Queue (`!=` is the negation). Every `Queue.new` is a different value.

```ruby
q = Queue.new
p(q == q)                           # => true
p(q == Queue.new)                   # => false
p(q != Queue.new)                   # => true
```

# Socket

Socket is the type of a TCP connection. Values come from `Socket.connect(host, port)` (plain), `Socket.connect_ssl(host, port)` (TLS), and, on the server side, `TCPServer.accept` ([TCPServer](TCPServer.md)). All are the same type `Socket` and are read and written with the same operations (Ruby's `TCPSocket` and `OpenSSL::SSL::SSLSocket`, see [Built-ins](../09-builtins.md)). The reading operations have the shape of `IO`'s (`gets`, `read`), but a Socket is not an `IO` and cannot be passed to `IO.puts` and the like.

A network failure (cannot connect, connection lost, name not resolved) is an `IOError` (Ruby's `Errno::ECONNREFUSED`, `SocketError`, `OpenSSL::SSL::SSLError`, and the like, all in one; see [Exceptions](../08-exceptions.md)). How long a read or write waits is set by `set_timeout`; past it, an `IOError`. The Strings that `gets` and `read` return are binary (encoding `ASCII-8BIT`); pass them through `String.force_encoding(s, "UTF-8")` to use them as text. There is no UDP and no Unix domain socket.

The only operators on Sockets are `==` and `!=` (equal for the same connection). The examples start a `TCPServer` on port 0 of `127.0.0.1` and connect from a Thread of the same program.

## connect

`Socket.connect(String, Integer, [Integer|Float|Rational])`

Connects by TCP to `port` of `host` (a name or an IP address) and returns a Socket. The third argument is how many seconds to wait for the connection; past it, an `IOError` (when omitted, the OS's default applies). A refused connection is an `IOError` too, and so is a name that cannot be resolved (Ruby's `Socket::ResolutionError`; the message is the resolver's, `getaddrinfo(3): Name or service not known` or the like).

```ruby
srv = TCPServer.new("127.0.0.1", 0)
port = TCPServer.port(srv)
client = Thread.new {
  s = Socket.connect("127.0.0.1", port, 5)
  Socket.write(s, "ping\n")
  reply = Socket.gets(s)
  Socket.close(s)
  reply
}
conn = TCPServer.accept(srv)
p(Socket.gets(conn))                # => "ping\n"
Socket.write(conn, "pong\n")
Socket.close(conn)
p(Thread.value(client))             # => "pong\n"
TCPServer.close(srv)
```

```ruby error
Socket.connect("127.0.0.1", 1)      # !> IOError: Socket.connect: Connection refused
```

```ruby error
Socket.connect("no-such-host.invalid", 80)   # !> IOError: Socket.connect: getaddrinfo
```

## connect_ssl

`Socket.connect_ssl(String, Integer, [Integer|Float|Rational])`

Connects by TCP to `port` of `host`, performs the TLS handshake over it, and returns a Socket. The peer's certificate is verified against the OS's trust store, and `host` is used for SNI and the host name check (Ruby's `OpenSSL::SSL::SSLSocket` with `VERIFY_PEER`). The third argument is how many seconds to wait for the TCP connection. A failed connection and a failed handshake (a peer that does not speak TLS, a certificate that cannot be verified) are both an `IOError`. `close` closes TLS and TCP. An example that reaches an outside host cannot run under the checker, so only its shape is shown:

```
s = Socket.connect_ssl("example.com", 443, 10)
Socket.write(s, "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n")
p(Socket.gets(s))                   # "HTTP/1.0 200 OK\r\n" or the like
Socket.close(s)
```

Against a server that does not speak TLS the handshake is an `IOError`:

```ruby error
srv = TCPServer.new("127.0.0.1", 0)
port = TCPServer.port(srv)
t = Thread.new { c = TCPServer.accept(srv); Socket.close(c) }
Socket.connect_ssl("127.0.0.1", port)   # !> IOError: Socket.connect_ssl:
```

## set_timeout

`Socket.set_timeout(x, Integer|Float|Rational|Nil)`

Sets how many seconds the following `gets`, `read`, and `write` wait, and returns the Socket (Ruby's `IO#timeout=`). Past the time, an `IOError` (`Blocking operation timed out!`). nil restores no limit (the default).

```ruby
srv = TCPServer.new("127.0.0.1", 0)
s = Socket.connect("127.0.0.1", TCPServer.port(srv))
p(Socket.set_timeout(s, 0.05) == s)   # => true
begin
  Socket.gets(s)
rescue IOError => e
  p(Exception.message(e))           # => "Blocking operation timed out!"
end
Socket.set_timeout(s, nil)
Socket.close(s)
TCPServer.close(srv)
```

## gets

`Socket.gets(x)`

The next line including its newline, or nil once the peer has closed its writing side (`close_write` or `close`) and the data is used up. The result is `String | nil`; `--strict` (level 2) reports using it unchecked as a `nil` problem. Past the `set_timeout` time, an `IOError`.

```ruby
srv = TCPServer.new("127.0.0.1", 0)
port = TCPServer.port(srv)
t = Thread.new {
  s = Socket.connect("127.0.0.1", port)
  Socket.write(s, "a\nb\n")
  Socket.close(s)
}
c = TCPServer.accept(srv)
while (line = Socket.gets(c))
  p(line)                           # => "a\n"
                                    # => "b\n"
end
p(Socket.gets(c))                   # => nil
Thread.join(t)
TCPServer.close(srv)
```

## read

`Socket.read(x, Integer)`

Reads exactly `n` bytes and returns them. It waits until `n` bytes have arrived; when the peer closes first it returns what is left, and nil when nothing is left (`String | nil`, level 2; with `n` 0 it is always `""`). A negative `n` is an `ArgumentError`. Unlike Ruby's `read` there is no form without a length (read to the end).

```ruby
srv = TCPServer.new("127.0.0.1", 0)
port = TCPServer.port(srv)
t = Thread.new {
  s = Socket.connect("127.0.0.1", port)
  Socket.write(s, "hello")
  Socket.close(s)
}
c = TCPServer.accept(srv)
p(Socket.read(c, 2))                # => "he"
p(Socket.read(c, 10))               # => "llo"
p(Socket.read(c, 10))               # => nil
p(Socket.read(c, 0))                # => ""
Thread.join(t)
TCPServer.close(srv)
```

```ruby error
srv = TCPServer.new("127.0.0.1", 0)
s = Socket.connect("127.0.0.1", TCPServer.port(srv))
Socket.read(s, -1)                  # !> ArgumentError: Socket.read: negative size -1
```

## write

`Socket.write(x, String)`

Sends the string and returns the number of bytes sent (an Integer). No newline is added. A closed Socket, or one after `close_write`, is an `IOError`.

```ruby
srv = TCPServer.new("127.0.0.1", 0)
port = TCPServer.port(srv)
t = Thread.new {
  s = Socket.connect("127.0.0.1", port)
  n = Socket.write(s, "日本\n")
  Socket.close(s)
  n
}
c = TCPServer.accept(srv)
got = Socket.gets(c)
if got
  p(String.encoding(got))           # => "ASCII-8BIT"
  p(String.force_encoding(got, "UTF-8"))   # => "日本\n"
end
p(Thread.value(t))                  # => 7
TCPServer.close(srv)
```

```ruby error
srv = TCPServer.new("127.0.0.1", 0)
s = Socket.connect("127.0.0.1", TCPServer.port(srv))
Socket.close(s)
Socket.write(s, "x")                # !> IOError: Socket.write: closed stream
```

## close_write

`Socket.close_write(x)`

Closes only the writing side and returns nil (a TCP half-close). The peer's `gets` and `read` get nil once the data is used up, while this side can still read the peer's reply. It is for protocols of the form "send everything, then wait for the answer". A `write` afterwards is an `IOError`.

```ruby
srv = TCPServer.new("127.0.0.1", 0)
port = TCPServer.port(srv)
client = Thread.new {
  s = Socket.connect("127.0.0.1", port)
  Socket.write(s, "1 2 3")
  Socket.close_write(s)
  reply = Socket.gets(s)
  Socket.close(s)
  reply
}
c = TCPServer.accept(srv)
body = Socket.read(c, 100)
if body
  Socket.write(c, String.upcase(body) + "\n")
end
Socket.close(c)
p(Thread.value(client))             # => "1 2 3\n"
TCPServer.close(srv)
```

## close

`Socket.close(x)`

Closes the connection and returns nil (for TLS, the TCP connection too). Closing again does nothing. Reading or writing afterwards is an `IOError`.

```ruby
srv = TCPServer.new("127.0.0.1", 0)
s = Socket.connect("127.0.0.1", TCPServer.port(srv))
p(Socket.close(s))                  # => nil
p(Socket.close(s))                  # => nil
TCPServer.close(srv)
```

## ==, !=

`Socket.==(x, Any)`

`Socket.!=(x, Any)`

Two values are equal when they are the same Socket (`!=` is the negation). The two ends of one connection (the client's `connect` and the server's `accept`) are different Sockets. A value that is not a Socket is never equal; the function form gives false for it too (no error).

```ruby
srv = TCPServer.new("127.0.0.1", 0)
s = Socket.connect("127.0.0.1", TCPServer.port(srv))
c = TCPServer.accept(srv)
p(s == s)                           # => true
p(s == c)                           # => false
p(s != c)                           # => true
p(Socket.==(s, 1))                  # => false
Socket.close(s)
Socket.close(c)
TCPServer.close(srv)
```

# TCPServer

TCPServer is the type of a listening socket that accepts TCP connections (Ruby's `TCPServer`). `TCPServer.new(host, port)` opens the port; `TCPServer.accept(s)` waits for the next connection and returns it as a `Socket`. An accepted connection is read and written with the [Socket](Socket.md) operations ([Built-ins](../09-builtins.md)). There is no literal.

A failure (the port cannot be opened because it is in use or not permitted, `accept` on a closed server, ...) is an `IOError` ([Exceptions](../08-exceptions.md)).

The only operators on TCPServers are `==` and `!=` (equal for the same server). The examples listen on port 0 of `127.0.0.1` (the OS picks a free port) and connect from a Thread of the same program.

## new

`TCPServer.new(String, Integer)`

Starts listening on `port` at the address `host` and returns a TCPServer. `host` is `"127.0.0.1"` (local only), `"0.0.0.0"` (every interface), `"localhost"`, and so on; it cannot be omitted (there is no `TCPServer.new(port)` as in Ruby). With `port` 0 the OS picks a free port, which `TCPServer.port` reports. A port in use, or one not permitted (below 1024), is an `IOError`.

```ruby
srv = TCPServer.new("127.0.0.1", 0)
p(srv)                              # => #<TCPServer>
p(TCPServer.port(srv) > 0)          # => true
TCPServer.close(srv)
```

```ruby error
srv = TCPServer.new("127.0.0.1", 0)
TCPServer.new("127.0.0.1", TCPServer.port(srv))   # !> IOError: TCPServer.new: Address already in use
```

## port

`TCPServer.port(x)`

The port number being listened on (an Integer), to learn the actual port after `TCPServer.new` with 0. A closed server is an `IOError` (`closed stream`).

```ruby
srv = TCPServer.new("127.0.0.1", 0)
port = TCPServer.port(srv)
p(port >= 1024)                     # => true
p(TCPServer.port(srv) == port)      # => true
TCPServer.close(srv)
```

```ruby error
srv = TCPServer.new("127.0.0.1", 0)
TCPServer.close(srv)
TCPServer.port(srv)                 # !> IOError: TCPServer.port: closed stream
```

## accept

`TCPServer.accept(x)`

Waits for the next connection and returns it as a `Socket`. Connections queue up in the OS from `TCPServer.new` on, so a peer that connects before `accept` is called is not lost. A closed server is an `IOError`. There is no time limit, so connect from another Thread, or combine with `Thread.join(t, limit)` to bound the wait.

```ruby
srv = TCPServer.new("127.0.0.1", 0)
port = TCPServer.port(srv)
clients = Array.map(Any["a", "b"]) { |name|
  Thread.new {
    s = Socket.connect("127.0.0.1", port)
    Socket.write(s, name + "\n")
    reply = Socket.gets(s)
    Socket.close(s)
    reply
  }
}
served = Any[]
Array.each(clients) { |_|
  c = TCPServer.accept(srv)
  p(c)                              # => #<Socket>
                                    # => #<Socket>
  line = Socket.gets(c)
  if line
    Array.push(served, String.chomp(line))
    Socket.write(c, "hi " + line)
  end
  Socket.close(c)
}
replies = Array.compact(Array.map(clients) { |t| Thread.value(t) })   # Socket.gets may be nil: compact before sorting
p(Array.sort(replies))              # => ["hi a\n", "hi b\n"]
p(Array.sort(served))               # => ["a", "b"]
TCPServer.close(srv)
```

```ruby error
srv = TCPServer.new("127.0.0.1", 0)
TCPServer.close(srv)
TCPServer.accept(srv)               # !> IOError: TCPServer.accept: closed stream
```

## close

`TCPServer.close(x)`

Stops listening, releases the port, and returns nil. Sockets already returned by `accept` stay usable. Closing again does nothing.

```ruby
srv = TCPServer.new("127.0.0.1", 0)
port = TCPServer.port(srv)
p(TCPServer.close(srv))             # => nil
p(TCPServer.close(srv))             # => nil
again = TCPServer.new("127.0.0.1", port)
p(TCPServer.port(again) == port)    # => true
TCPServer.close(again)
```

## ==, !=

`TCPServer.==(x, Any)`

`TCPServer.!=(x, Any)`

Two values are equal when they are the same server (`!=` is the negation). Every `TCPServer.new` is a different value. A value that is not a TCPServer is never equal; the function form gives false for it too (no error).

```ruby
srv = TCPServer.new("127.0.0.1", 0)
other = TCPServer.new("127.0.0.1", 0)
p(srv == srv)                       # => true
p(srv == other)                     # => false
p(srv != other)                     # => true
p(TCPServer.==(srv, 1))             # => false
TCPServer.close(srv)
TCPServer.close(other)
```

# Differences from Ruby and what is not supported

Sake's syntax is Ruby's, but several Ruby spellings are static errors and have a Sake spelling in their place. This appendix gathers those replacements in one table and gives the reasons below it. When a program written the Ruby way gets a static error, look it up here first; each error's hint also gives the form on the Sake side of the table.

## From Ruby to Sake

Each row is the Ruby spelling (left), the Sake spelling (middle), and the reason in a few words (right). The sections under the table explain the reasons, with examples.

| Ruby | Sake | Why |
|---|---|---|
| `s.upcase` | `String.upcase(s)` | the type is written on the operation |
| `p.x`, `p.x = v` | `Point.x(p)`, `p.Point.x = v`; inside the functions, `@x`, `@x = v` | field reads and writes name their type too |
| `x.nil?` | `x == nil` | no calls on values |
| `def self.f` | `def f`; in a module, `module_function` | one name in a namespace is one function |
| `class B < A` (inheritance) | `class B < A` (copies A's definitions; no subtyping) | every call target is static |
| `attr_reader :x` | `attr_reader x` | field names are written bare |
| `def initialize(x) = @x = x` | `C.new(x)` stores the field; `initialize(c)` only checks and converts | the parameter is the new instance |
| `@items = []` (a default) | `@items = Any[]` inside `initialize` | fields have no default values |
| `Data.define` | `Struct.new` | classes are mutable |
| `[1, 2]` (a growable array) | `Any[1, 2]`; `[1, 2]` is a Tuple (fixed length) | a literal's shape fixes its type |
| `{a: 1}` (a Hash) | `Hash.of(a: 1)`; `{a: 1}` is a Record | same |
| `%w[a b]`, `%i[a b]` | `String["a", "b"]`, `Symbol[:a, :b]` | undecided (Tuple or Array) |
| `PI = 3.14` | `def pi = 3.14`; a table is `once { ... }` | no value constants |
| `Math::PI`, `ARGV`, `$stdout` | `Math.PI` (or `Math::PI`), `ARGV`, `IO.stdout` | read as operations |
| `case x when Integer` | `case x in Integer` | there is no `===` |
| `for x in xs` | `Array.each(xs) { \|x\| }` | iteration is an operation |
| `$1`, `$~` | `m = String.match(s, re)`, then `m[1]` | no global variables |
| `proc`, `lambda`, `->`, `&:sym`, `method(:f)` | none; a block is only passed, with `yield` or `&b` | blocks are second-class |
| `obj.send(:f)`, `define_method`, `method_missing`, `eval` | none | call targets are static |
| `g(**opts)` | none; pass the Hash positionally | an open design question (D11) |
| `rescue A` catching the subclasses of A | `rescue M`, a module the types include | `class B < A` is a copy |
| `1 + money` | define `coerce(m, other)` in Money | the left operand decides |

### The type is written on the operation

In Sake only operations name a type. A method call on a value such as `s.upcase` is a static error, and the checker's hint gives `String.upcase(s)` (for `s.strip.upcase`, the chain form `String.strip(s).upcase`; [Program structure and name resolution](02-program.md)). Field reads and writes are the same: `p.x` is `Point.x(p)` and `p.x = v` is `p.Point.x = v`. Inside a class's functions, `@x` means field `x` of the first parameter ([Classes](07-classes.md)).

```ruby
Point = Struct.new(:x, :y)
class Point
  def shift(pt, d)
    @x = @x + d          # Point.set_x(pt, Point.x(pt) + d)
    @x
  end
end
pt = Point.new(1, 2)
p(Point.x(pt))           # => 1
pt.Point.x = 5
p(Point.shift(pt, 1))    # => 6
p(pt)                    # => #<struct Point x=6, y=2>
```

`x.nil?` is a method call on a value too, so it gets the same error, with the hint `x == nil`.

### Classes

A type a program defines is a class. Declaring it with `attr_*` lines in `class C` and making it with the shorthand `Struct.new(:x, :y)` give the same thing. These are the points where it differs from a Ruby class ([Classes](07-classes.md)).

- **No `def self.f`.** Sake has no `self`; `def f` inside `class C` is `C.f` as it is. Ruby's class methods and instance methods both become the same `def f` in Sake. In a module, write `module_function`.
- **No inheritance.** `class B < A` is shorthand for writing A's definitions (fields, functions, includes) into B. Afterwards A and B are unrelated types, and passing a B to `A.f(b)` is a type error.
- **Field names are bare.** `attr_reader :x` is an error telling you to write `x` without the `:`.
- **`initialize` takes no value.** After `C.new(x)` has stored the fields, `def initialize(c)` runs with the new instance. Ruby's `def initialize(x) = @x = x` would store the instance in its own field, so it is a static error.
- **Defaults go in `initialize`.** `attr_reader items = Any[]` is an error; the first value is set inside `initialize` with `@items = Any[]`. `C.new` may then leave that field out.
- **No `Data.define`.** Sake's classes are mutable, so use `Struct.new`, the counterpart of Ruby's `Struct`.

```ruby
class A
  attr_reader x
  def double(a) = @x * 2
end
class B < A              # copies A's x and double into B
  attr_reader y
end
b = B.new(1, 2)
p(B.double(b))           # => 2
p(b)                     # => #<struct B x=1, y=2>
```

```ruby error
class Bag
  attr_reader name, items
  def initialize(x) = @items = x    # !> `@items = x` stores the new Bag in its own field: `x` is the instance, not a value
end
```

### Literals and constants

A literal's shape fixes its type ([Values and types](03-values.md)).

- **Ruby's `Array[1, 2]`, `Hash[...]` and `Set[...]` are Arrays of Arrays, of Hashes, of Sets** in Sake, as `T[...]` is an Array of T for every T. Make an Array of any values with `Any[...]`, a Hash with `Hash.of(k => v)` or `Hash.new`, a Set with `Set.of(a, b)` or `Set.new(xs)`.
- **`[1, 2]` is a Tuple**, of fixed length. A growable array is written `Any[1, 2]`, and the empty one `Any[]`. Passing a Tuple to `Array.push` is a type error whose hint says to write `Any[...]`.
- **`{a: 1}` is a Record**; `Hash.of(a: 1)` is a Hash. A Record's fields are read with a pattern, `r => {a:}`.
- **`%w[a b]` and `%i[a b]` are not supported.** Whether they are a Tuple or an Array is undecided. `String["a", "b"]` and `Symbol[:a, :b]` give Arrays with an element type.
- **No value constants.** `PI = 3.14` is a static error whose hint gives `def pi = 3.14`. A table computed once and kept is `def table = once { ... }` ([Functions and blocks](04-functions.md)). Only `Struct.new` and `Exception.new` may be assigned to a constant.
- **Built-in constants are operations.** Call them as `Math.PI`, `ARGV`, `IO.stdout`. A few built-in ones such as `Math::PI` are also read in Ruby's form. There are no global variables such as `$stdout`.

```ruby
t = [1, 2]
xs = Any[1, 2]
Array.push(xs, 3)
p(t, xs)                 # => [1, 2]
                         # => [1, 2, 3]
h = Hash.of(a: 1)
h[:b] = 2
p(h)                     # => {a: 1, b: 2}
def pi = 3.14
p(pi, Math.PI)           # => 3.14
                         # => 3.141592653589793
```

```ruby error
xs = [1, 2]
Array.push(xs, 3)        # !> Array.push: argument 1 must be Array, but is [Integer, Integer] [type]
```

### Control and iteration

- **No `case`/`when`; use `case`/`in`.** Ruby's `when` dispatches `===` on the receiver, and Sake has no `===`. `case x in Integer` branches on the type, and the checker looks at exhaustiveness ([Control flow and patterns](06-control.md)).
- **No `for`; iterate with an operation.** `Array.each(xs) { |x| ... }`, `Range.each(1..3) { |i| ... }`: the operation of the Array or Range hands out the elements ([Functions and blocks](04-functions.md)).
- **No `$1`, `$~`.** Keep the match in a variable: `m = String.match(s, re)`, then `m[1]` when `m` is not nil.

### Blocks and dynamic features

- **Blocks are second-class.** A block is passed to a function and called with `yield`, or passed on with `&b`; it cannot be stored in a variable. There is no `proc`, `lambda`, `->` or `method(:f)`, and `&:sym` cannot be passed ([Functions and blocks](04-functions.md)).
- **Call targets are static.** There is no `send`, `define_method`, `method_missing` or `eval`. `eval` is an error saying it defeats static analysis.
- **Passing keywords on.** `def f(**opts)` can collect them, but `g(**opts)` cannot pass them to the next function (`**` is not supported). Pass the Hash as a positional argument. This is open as design question D11.

### Exceptions

`rescue A, B => e` catches the listed types ([Exceptions and errors](08-exceptions.md)). Writing `class MyErr < StandardError` does not make MyErr a subtype of StandardError; the `<` is a copy, so `rescue A` does not catch a `class B < A`. To catch a group, include a module in the types and write `rescue M`. The built-in types keep Ruby's parents (`rescue IndexError` catches KeyError). The three forms `rescue => e`, `rescue StandardError => e` and `rescue Exception => e` keep Ruby's meaning of "any exception" and catch every rescuable exception.

```ruby
class MyErr < StandardError
end
begin
  raise MyErr, "boom"
rescue KeyError, IndexError => e
  p(1)
rescue MyErr => e
  p(Exception.message(e))    # => "boom"
end
```

### Operators

`a + b` is shorthand for `Arithmetic.+(a, b)`, resolved by the type of the left operand ([Operators and indexing](05-operators.md)). `1 + money` dispatches on Integer, so Money's `+` is never reached. With Ruby's protocol, a `coerce(m, other)` defined in Money that returns the Tuple `[left, right]` has the pair converted first, and then the operator runs.

```ruby
class Money
  include Arithmetic
  attr_reader cents
  def +(a, b) = Money.new(@cents + Money.cents(b))
  def coerce(m, other) = [Money.new(other * 100), m]
end
m = Money.new(50)
p(2 + m)                 # => #<struct Money cents=250>
```

## Not yet supported

Each of these is rejected statically with a "not supported" error. Most wait on a design decision.

- **Writing to Record fields.** `r[:a] = 2` is an error saying a Record cannot be indexed. Reading is by pattern, `r => {a:}`.
- **The type scope `Integer.(a + b)`.**
- **`case`/`when`.** Use `case`/`in`.
- **`%w[]`, `%i[]`.** Tuple or Array is undecided.
- **`for`.** Not planned for now. Iterate with an operation such as `Range.each(1..3) { |i| ... }`.
- **Other pattern forms.** `*rest` in a Tuple pattern, find patterns, pins `^x`, guards `in Integer if c`. The patterns that work are in [Control flow and patterns](06-control.md).
- **First-class blocks.** Storing a block, `proc`, `lambda`.
- **`g(**opts)`.** Passing collected keywords on.
- **`begin ... end while`.** Write the `while` first.

## Design material

- `DESIGN.md` (Japanese): the design notes, with the reasons behind each decision.
- `docs/comparison.md` (Japanese): what is new in Sake and what is not, axis by axis against Elm, Crystal, Rust, Elixir, TypeScript, Clojure and the type-inference literature.
- `TODO.md`: open items and design questions (D1 to D13).
- `experiments/`: each experiment with its method, results, and limits (README).
