Skip to content

Commit 6b7e497

Browse files
author
Arnaud ESTEVE
committed
Step3: rewrite the Monad part (apart from the Reader monad)
1 parent 171a09a commit 6b7e497

File tree

1 file changed

+53
-8
lines changed

1 file changed

+53
-8
lines changed

docs/docs/reference/contextual/typeclasses-new.md

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -144,21 +144,66 @@ Since we can now use the `map` method directly accessible on `original` which is
144144

145145
### Monads
146146

147-
```scala
148-
trait Monad[F[_]] extends Functor[F] {
149-
def [A, B](x: F[A]).flatMap(f: A => F[B]): F[B]
150-
def [A, B](x: F[A]).map(f: A => B) = x.flatMap(f `andThen` pure)
147+
Now we have a `Functor` for `List`.
148+
149+
Applying the `List.map` ability with the following mapping function as parameter: `mapping: A => B` would result in a `List[B]`.
150+
151+
Now, applying the `List.map` ability with the following mapping function as parameter: `mapping: A => List[B]` would result in a `List[List[B]]`.
152+
153+
To avoid avoid managing lists of lists, we may want to "flatten" the values in a single list.
154+
155+
That's where `Monad` enter the party. A `Monad` for type `F[_]` is a `Functor[F]` with 2 more abilities:
156+
* the flatten ability we just described: turning `F[A]` to `F[B]` when given a `mapping: A => F[B]` function
157+
* the ability to create `F[A]` from a single value `A`
158+
159+
Here is the translation of this definition in Scala 3:
151160

152-
def pure[A](x: A): F[A]
161+
```scala
162+
trait Monad[F[_]] extends Functor[F] { // "A `Monad` for type `F[_]` is a `Functor[F]`" => thus has the `map` ability
163+
def pure[A](x: A): F[A] // `pure` can construct F[A] from a single value A
164+
def [A, B](x: F[A]).flatMap(f: A => F[B]): F[B] // the flattening ability is named `flatMap`, using extension methods as previous examples
165+
def [A, B](x: F[A]).map(f: A => B) = x.flatMap(f `andThen` pure) // the `map(f)` ability is simply a combination of applying `f` then turning the result into an `F[A]` then applying `flatMap` to it
153166
}
167+
```
168+
169+
#### List
154170

171+
Let us declare the `Monad` ability for type `List`
172+
```scala
155173
given listMonad as Monad[List] {
156-
def [A, B](xs: List[A]).flatMap(f: A => List[B]): List[B] =
157-
xs.flatMap(f)
158174
def pure[A](x: A): List[A] =
159175
List(x)
176+
def [A, B](xs: List[A]).flatMap(f: A => List[B]): List[B] =
177+
xs.flatMap(f) // let's rely on the existing `flatMap` method of `List`
160178
}
179+
```
180+
181+
`map` implementation is no longer needed.
182+
183+
#### Option
184+
185+
`Option` is an other type having the same kind of behaviour:
186+
* the `map` ability turning `Option[A]` into `Option[B]` if passed a function `f: A => B`
187+
* the `flatMap` ability turning `Option[A]` into `Option[B]` if passed a function `f: A => Option[B]`
188+
* the `pure` ability turning `A` into `Option[A]`
189+
190+
```scala
191+
given optionMonad as Monad[Option] {
192+
def pure[A](x: A): Option[A] =
193+
Option(x)
194+
def [A, B](xs: Option[A]).flatMap(f: A => Option[B]): Option[B] =
195+
xs.flatMap(f) // let's rely on the existing `flatMap` method of `Option`
196+
}
197+
```
198+
199+
#### The Reader Monad
161200

201+
Another example of a `Monad` is the Reader Monad. It no longer acts on a type like `List` or `Option`, but on a function.
202+
It can be used for example for combining functions that all have need the same type of parameter, for instance, if multiple functions need to access some configuration, context, environment variables, etc.
203+
204+
The Reader monad allows to abstract over such a `Config` dependency (or context, environment, ...), named `Ctx` in the following examples. It is therefore _parameterized_ by `Ctx`:
205+
206+
```scala
162207
given readerMonad[Ctx] as Monad[[X] =>> Ctx => X] {
163208
def [A, B](r: Ctx => A).flatMap(f: A => Ctx => B): Ctx => B =
164209
ctx => f(r(ctx))(ctx)
@@ -173,4 +218,4 @@ The definition of a _typeclass_ is expressed in Scala 3 via a `trait`.
173218
The main difference with other traits resides in how these traits are implemented.
174219
In the case of a _typeclass_ the trait's implementations are expressed through `given ... as` type definitions, and not through classes that `extends` the trait linearly.
175220

176-
In addition to these given instances, extension methods and context bounds allow a concise and natural expression of _typeclasses_.
221+
In addition to these given instances, other constructs like extension methods, context bounds and type lambdas allow a concise and natural expression of _typeclasses_.

0 commit comments

Comments
 (0)