Skip to content

Commit

Permalink
docs: Make documentation more beginer friendly
Browse files Browse the repository at this point in the history
  • Loading branch information
Iltotore committed Nov 10, 2022
1 parent df8c5cf commit 5420705
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 28 deletions.
25 changes: 20 additions & 5 deletions docs/_docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ ivy"io.github.iltotore::iron:version"
The following import is often used:

```scala
import io.github.iltotore.iron.*
import io.github.iltotore.iron.{given, *}
```

This import contains bases to make Iron work including:
Expand All @@ -43,14 +43,29 @@ Standard constraints are split in different objects stored in the package `io.gi
For example, you can import standard number-related constraints using:

```scala
import io.github.iltotore.iron.constraint.numeric.{*, given}
import io.github.iltotore.iron.constraint.numeric.{given, *}
```

When having multiple imports from Iron, this style is often used in Iron codebase or documentation:

```scala
import io.github.iltotore.iron.{given, *}, constraint.numeric.{given, *}
```

instead of:

```scala
import io.github.iltotore.iron.{given, *}
import io.github.iltotore.iron.constraint.numeric.{given, *}
```

**Note: Don't forget the `given` import. It imports `Constraint` implicit instances. See [Importing Given](http://dotty.epfl.ch/docs/reference/contextual/given-imports.html).**

## Going further
## Next steps

You can find the list of all standard constraints in the [[constraint package summary|io.github.iltotore.iron.constraint]].

Now that you know how to import Iron in your project, you should check the [references](reference/index.md), starting
from [Iron Type](reference/iron-type.md)
Now that you know how to import Iron in your project, you should check the [references](reference/index.md):
- [Iron Type](reference/iron-type.md): the core data type of Iron.
- [Constraint](reference/constraint.md): to create your own constraints.
- [Refinement Methods](reference/refinement.md): to use constraints and refine values.
25 changes: 20 additions & 5 deletions docs/_docs/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ This processus is called "type refinement".
## Why refined types matter

In production code bases, it is important to make sure that all values passed are valid or handled correctly.
Static typing like in Scala is useful to avoid using the wrong datatype.
However, despite strong static typing, some wrong values can bypass type checking.
Static typing like in Scala is useful to avoid using such issue.
However, despite strong static typing, some invalid values still type check.

Taking for example a User with an age:

Expand All @@ -38,13 +38,28 @@ To fix this caveat, you have to write an assertion/guard condition with the foll

Refined types solve both problems by ensuring that constraints are checked compile time or __explicitly__ at runtime.

```scala
//Note: the `given` import is important!
import io.github.iltotore.iron.{given, *}, constraint.numeric.{given, *}

case class User(age: Int :| Greater[0])

User(1) //Compiles
User("1") //Does not compile
User(-1) //Does not compile
User(-1.refine) //Compiles but fails at runtime. Useful for runtime checks such as form validation.
//See also `refineOption` and `refineEither`
```

## Use cases

Iron and refined types in general are useful in multiple cases:
- Mathematics
Iron and refined types in general are useful in many cases:
- Data validation (form, API...)
- Securing business data types
- Mathematics

## Getting started

See the [Getting started](getting-started.md) page to set up and start using Iron.
See the [Getting started](getting-started.md) page to set up and start using Iron.

See [references](reference/index.md) for details about the concepts of Iron.
20 changes: 20 additions & 0 deletions docs/_docs/reference/constraint.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,24 @@ Now testing the constraint:
```scala
var x: Int :| Greater[5] = 6
x = 3 //Compile-time error: Should be greater than 5
```

## Constraint aliases

In some cases, you can make your constraint out of existing ones.
For example, a "greater or equal" constraint is just the `Greater` union `StrictEqual`.

Like "classic" types, constraints can be aliased:

```scala
type GreaterEqual[V] = Greater[V] | StrictEqual[V]
```

you can use the `DescribedAs` constraint enables attaching a custom description to our alias:

```scala
//Allows to concatenate string types.
import io.github.iltotore.iron.compileTime.+

type GreaterEqual[V] = (Greater[V] | StrictEqual[V]) DescribedAs ("Should be greater or equal to " + V)
```
17 changes: 3 additions & 14 deletions docs/_docs/reference/implication.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ title: "Implication"

# Implication

Implications are a compile-time mechanism to allow casting from a refined type to another.
They are analogous to [logical implication](https://en.wikipedia.org/wiki/Material_conditional).
Implication is a compile-time mechanism to allow casting from a refined type to another.
It is analogous to [logical implication](https://en.wikipedia.org/wiki/Material_conditional).

Implications are represented in Iron by `Implication[C1, C2]` or its alias `C1 ==> C2`.
Implication is represented in Iron by `Implication[C1, C2]` or its alias `C1 ==> C2`.
It should be read as "C1 implies C2".

For example, the following code compiles due to [transitivity](https://en.wikipedia.org/wiki/Transitive_relation):
Expand Down Expand Up @@ -70,17 +70,6 @@ val x: Int :| Greater[1] = ???
val y: Int :| Not[Not[Greater[0]]] = x //(Greater[1] ==> Greater[0]) ==> (Greater[1] ==> Not[Not[Greater[0]]])
```

Dependencies are also used to create more "complex" implications. Taking an example from
[[any|io.github.iltotore.iron.constraint.any]]:

```scala
given [C1, C2, C3](using (C1 ==> C2) | (C1 ==> C3)): (C1 ==> Or[C2, C3]) = Implication()
```

**Note: `|` is not the `Or` constraint alias but Scala 3's union type.**

`C1` implies `C2 || C3` if C1 implies whether `C2` or `C3` (or both).

### Type operators

Scala 3 provides multiple compile-time constructs to help you to manipulate and test types.
Expand Down
2 changes: 1 addition & 1 deletion docs/_docs/reference/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: Reference

# Iron references

Find detailed documentation about Iron's main concepts.
Find detailed documentation about the main concepts of Iron.

## Data types

Expand Down
9 changes: 6 additions & 3 deletions docs/_docs/reference/iron-type.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ title: "Iron Type"
Refined types are represented in Iron by `IronType[A, C]` where:

- `A` is the base type
- `C` is the constraint (or "refinement") attached to `A`.
- `C` is the [constraint](constraint.md) (or "refinement") attached to `A`.

For example, `IronType[Int, Greater[0]]` represents an `Int` that should be greater than zero
(see [[Greater reference|io.github.iltotore.iron.constraint.numeric.Greater]]).

A more concise alias `:|` is often used instead: `Int :| Greater[0]` is equivalent to `IronType[Int, Greater[0]]`.
A more concise alias `A :| C` is often used instead. For instance, `Int :| Greater[0]` is equivalent to `IronType[Int, Greater[0]]`.
This alias is close to the
mathematical [predicate operator](https://en.wikipedia.org/wiki/Set-builder_notation#Sets_defined_by_a_predicate) `|`
used in set builders.
Expand All @@ -35,4 +35,7 @@ desugars to

```scala
val x: Int = ???
```
```

For further details about type refinement, see [Refinement Methods](refinement.md).
To create custom constraints, see [Constraint reference](constraint.md).

0 comments on commit 5420705

Please sign in to comment.