|
| 1 | +object Structures: |
| 2 | + |
| 3 | + trait Functor[F[_]]: |
| 4 | + extension [A](fa: F[A]) |
| 5 | + def map[B](f: A => B): F[B] |
| 6 | + def as[B](b: B): F[B] = map(_ => b) |
| 7 | + def void: F[Unit] = as(()) |
| 8 | + |
| 9 | + trait Applicative[F[_]] extends Functor[F]: |
| 10 | + def pure[A](a: A): F[A] |
| 11 | + def unit: F[Unit] = pure(()) |
| 12 | + extension[A](fa: F[A]) |
| 13 | + def map2[B, C](fb: F[B], f: (A, B) => C): F[C] |
| 14 | + def map[B](f: A => B): F[B] = |
| 15 | + fa.map2(unit, (a, _) => f(a)) |
| 16 | + |
| 17 | + trait Monad[F[_]] extends Applicative[F]: |
| 18 | + extension[A](fa: F[A]) |
| 19 | + def flatMap[B](f: A => F[B]): F[B] |
| 20 | + override def map[B](f: A => B): F[B] = |
| 21 | + flatMap(a => pure(f(a))) |
| 22 | + def map2[B, C](fb: F[B], f: (A, B) => C): F[C] = |
| 23 | + flatMap(a => fb.map(b => f(a, b))) |
| 24 | + |
| 25 | + given Monad[List] with |
| 26 | + def pure[A](a: A) = List(a) |
| 27 | + extension[A](fa: List[A]) |
| 28 | + def flatMap[B](f: A => List[B]) = fa.flatMap(f) |
| 29 | + |
| 30 | + given Monad[Option] with |
| 31 | + def pure[A](a: A) = Some(a) |
| 32 | + extension[A](fa: Option[A]) |
| 33 | + def flatMap[B](f: A => Option[B]) = fa.flatMap(f) |
| 34 | + |
| 35 | + |
| 36 | + opaque type Kleisli[F[_], A, B] = A => F[B] |
| 37 | + |
| 38 | + extension [F[_], A, B](k: Kleisli[F, A, B]) |
| 39 | + def apply(a: A): F[B] = k(a) |
| 40 | + |
| 41 | + object Kleisli: |
| 42 | + def apply[F[_], A, B](f: A => F[B]): Kleisli[F, A, B] = f |
| 43 | + |
| 44 | + given [F[_], A](using F: Monad[F]): Monad[[B] =>> Kleisli[F, A, B]] with |
| 45 | + def pure[B](b: B) = Kleisli(_ => F.pure(b)) |
| 46 | + extension[B](k: Kleisli[F, A, B]) |
| 47 | + def flatMap[C](f: B => Kleisli[F, A, C]) = |
| 48 | + a => k(a).flatMap(b => f(b)(a)) |
| 49 | + |
| 50 | +end Structures |
| 51 | + |
| 52 | +@main def run = |
| 53 | + import Structures.{*, given} |
| 54 | + println(List(1, 2, 3).map2(List(4, 5, 6), (_, _))) |
| 55 | + |
| 56 | + val p: Kleisli[Option, Int, Int] = Kleisli((x: Int) => if x % 2 == 0 then Some(x) else None) |
| 57 | + val q: Kleisli[Option, Int, Int] = summon[Applicative[[B] =>> Kleisli[Option, Int, B]]].pure(20) |
| 58 | + println(p.map2(q, _ + _)(42)) |
| 59 | + |
0 commit comments