Skip to content

Commit

Permalink
docs: Move "new types" section to its own page
Browse files Browse the repository at this point in the history
  • Loading branch information
Iltotore committed Jun 14, 2023
1 parent 5558783 commit c64d7c1
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 38 deletions.
38 changes: 0 additions & 38 deletions docs/_docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ ivy"io.github.iltotore::iron:version"

## Common imports

### Base

The following import is often used:

```scala
Expand Down Expand Up @@ -53,42 +51,6 @@ import io.github.iltotore.iron.*
import io.github.iltotore.iron.constraint.all.*
```

### Companion object
`RefinedTypeOps` create convenient companion object.

```scala
opaque type Temperature = Double :| Positive
object Temperature extends RefinedTypeOps[Temperature]
```

#### apply function for compile-time validation
```scala
val temperature = Temperature(100) //temperature is of Temperature type
```
`Temperature(-100)` won't compile.

#### fromIronType to wrap already refined values

`fromIronType` helps to manage values which already were checked against constrains.

Implication works here; hence, both `Double :| Greater[10]` and `Double :| Positive` could be `Temperature`
```scala
val x: Double :| Positive = 5.0
val y: Double :| Greater[10] = 15.0
val t1 = Temperature.fromIronType(x)
val t2 = Temperature.fromIronType(y)
```

#### Option/Either
`Temperature.either(-5.0)` and `Temperature.option(-5.0)` return `Either` and `Option`.

#### applyUnsafe
`applyUnsafe` throws `IllegalArgumentException` exception if predicate fails.
`Temperature.applyUnsafe(-1)` is example of usage.

#### assume
`Temperature.assume(x)` doesn't fail (and returns `Temperature`). No validations being performed. Consider it as unsafe cast.

## Next steps

You can find the list of all standard constraints in the [[constraint package summary|io.github.iltotore.iron.constraint]].
Expand Down
96 changes: 96 additions & 0 deletions docs/_docs/reference/newtypes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
---
title: "Creating New Types"
---

# Creating New Types

You can create no-overhead new types like [scala-newtype](https://github.com/estatico/scala-newtype) in Scala 2 with Iron.

## Opaque types

The aliased type of an [opaque type](https://docs.scala-lang.org/scala3/book/types-opaque-types.html) is only known in its definition file. It is not considered like a type alias outside of it:

```scala
opaque type Temperature = Double :| Positive
```

```scala
val x: Double :| Positive = 5
val temperature: Temperature = x //Error: Temperature expected, got Double :| Positive
```

Such encapsulation is especially useful to avoid mixing different domain types with the same refinement:

```scala
opaque type Temperature = Double :| Positive
opaque type Moisture = Double :| Positive
```

```scala
case class Info(temperature: Temperature, moisture: Moisture)

val temperature: Temperature = ???
val moisture: Moisture = ???

Info(moisture, temperature) //Compile-time error
```

But as is, you cannot create an "instance" of your opaque type outside of its definition file. You need to add methods yourself like:

```scala
opaque type Temperature = Double :| Positive

object Temperature:

def apply(x: Double :| Positive): Temperature = x
```

## RefinedTypeOps

Iron provides a convenient trait called `RefinedTypeOps` to easily add smart constructors to your type:

```scala
opaque type Temperature = Double :| Positive
object Temperature extends RefinedTypeOps[Temperature]
```

```scala
val temperature = Temperature(15) //Compiles
println(temperature) //15

val positive: Int :| Positive = 15
val tempFromIron = Temperature.fromIronType(positive)
```

### Runtime refinement

`RefinedTypeOps` supports [all refinement methods](refinement.md) provided by Iron:

```scala
val unsafeRuntime: Temperature = Temperature.applyUnsafe(15)
val option: Option[Temperature] = Temperature.option(15)
val either: Either[String, Temperature] = Temperature.either(15)
```

Constructors for other modules exist:

```scala
val zioValidation: Temperature = Temperature.validation(15)
```

Note: all these constructors are inline. They don't bring any overhead:

```scala
val temperature: Temperature = Temperature(15)
val unsafeRuntime: Temperature = Temperature.applyUnsafe(runtimeValue)
```

compiles to

```scala
val temperature: Double = 15
val unsafeRuntime: Double =
if runtimeValue > 0 then runtimeValue
else throw new IllegalArgumentException("...")
```

0 comments on commit c64d7c1

Please sign in to comment.