Skip to content

Commit 6da3684

Browse files
authored
Merge pull request #239 from typelevel/foption
Add syntax for `F[Option[A]]`
2 parents b104cb5 + 4d452b3 commit 6da3684

File tree

4 files changed

+197
-0
lines changed

4 files changed

+197
-0
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Mouse includes enrichments for:
3030
- [Long](./shared/src/main/scala/mouse/long.scala)
3131
- [Double](./shared/src/main/scala/mouse/double.scala)
3232
- [Map](./shared/src/main/scala/mouse/map.scala)
33+
- [F\[Option\[A\]\]](./shared/src/main/scala/mouse/foption.scala)
3334

3435
#### Example:
3536

@@ -97,6 +98,9 @@ res0: Either[String,Int] = Right(6)
9798

9899
scala> val mapped = Map(1 -> 2, 3 -> 4).mapKeys(_ * 2)
99100
mapped: Map[Int,Int] = Map(2 -> 2, 6 -> 4)
101+
102+
scala> val foption = List(Option(1), Option(2), Option(4)).mapIn(_ * 2)
103+
foption: List[Option[Int]] = List(Some(2), Some(4), Some(8))
100104
```
101105

102106
#### Release Notes

shared/src/main/scala/mouse/all.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ trait AllSharedSyntax
1212
with DoubleSyntax
1313
with PartialFunctionLift
1414
with MapSyntax
15+
with FOptionSyntax
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package mouse
2+
3+
import cats.{Applicative, FlatMap, Functor, Monad, Traverse}
4+
5+
trait FOptionSyntax {
6+
implicit final def FOptionSyntaxMouse[F[_], A](foa: F[Option[A]]): FOptionOps[F, A] = new FOptionOps(foa)
7+
}
8+
9+
final class FOptionOps[F[_], A](private val foa: F[Option[A]]) extends AnyVal {
10+
11+
def cata[B](default: => B, f: A => B)(implicit F: Functor[F]): F[B] =
12+
F.map(foa)(_.fold(default)(f))
13+
14+
def cataF[B](default: => F[B], f: A => F[B])(implicit F: FlatMap[F]): F[B] =
15+
F.flatMap(foa)(_.fold(default)(f))
16+
17+
def existsIn(f: A => Boolean)(implicit F: Functor[F]): F[Boolean] =
18+
F.map(foa)(_.exists(f))
19+
20+
def existsF(f: A => F[Boolean])(implicit F: Monad[F]): F[Boolean] =
21+
F.flatMap(foa) {
22+
case None => F.pure(false)
23+
case Some(a) => f(a)
24+
}
25+
26+
def filterIn(f: A => Boolean)(implicit F: Functor[F]): F[Option[A]] =
27+
F.map(foa)(_.filter(f))
28+
29+
def filterF(f: A => F[Boolean])(implicit F: Monad[F]): F[Option[A]] =
30+
F.flatMap(foa) {
31+
case None => F.pure(None)
32+
case s @ Some(v) =>
33+
F.map(f(v)) {
34+
case true => s
35+
case _ => None
36+
}
37+
}
38+
39+
def flatMapIn[B](f: A => Option[B])(implicit F: Functor[F]): F[Option[B]] =
40+
F.map(foa)(_.flatMap(f))
41+
42+
def flatMapF[B](f: A => F[Option[B]])(implicit F: Monad[F]): F[Option[B]] =
43+
F.flatMap(foa)(_.fold(F.pure(Option.empty[B]))(f))
44+
45+
def foldIn[B](default: => B)(f: A => B)(implicit F: Functor[F]): F[B] =
46+
cata(default, f)
47+
48+
def foldF[B](default: => F[B])(f: A => F[B])(implicit F: FlatMap[F]): F[B] =
49+
cataF(default, f)
50+
51+
def forallIn(f: A => Boolean)(implicit F: Functor[F]): F[Boolean] =
52+
F.map(foa)(_.forall(f))
53+
54+
def forallF(f: A => F[Boolean])(implicit F: Monad[F]): F[Boolean] =
55+
F.flatMap(foa) {
56+
case None => F.pure(true)
57+
case Some(a) => f(a)
58+
}
59+
60+
def getOrElse[B >: A](a: => B)(implicit F: Functor[F]): F[B] =
61+
F.map(foa)(_.fold(a)(identity))
62+
63+
def getOrElseF[B >: A](fa: => F[B])(implicit F: Monad[F]): F[B] =
64+
F.flatMap(foa)(_.fold(fa)(F.pure))
65+
66+
def mapIn[B](f: A => B)(implicit F: Functor[F]): F[Option[B]] =
67+
F.map(foa)(_.map(f))
68+
69+
def orElseIn(default: Option[A])(implicit F: Functor[F]): F[Option[A]] =
70+
F.map(foa) {
71+
case None => default
72+
case x => x
73+
}
74+
75+
def orElseF(defaultF: => F[Option[A]])(implicit F: Monad[F]): F[Option[A]] =
76+
F.flatMap(foa) {
77+
case None => defaultF
78+
case x => F.pure(x)
79+
}
80+
81+
def traverseIn[G[_]: Applicative, B](f: A => G[B])(implicit F: Functor[F]): F[G[Option[B]]] =
82+
F.map(foa)(a => Traverse[Option].traverse(a)(f))
83+
84+
def traverseF[G[_]: Applicative, B](f: A => G[B])(implicit F: Traverse[F]): G[F[Option[B]]] =
85+
F.traverse(foa)(a => Traverse[Option].traverse(a)(f))
86+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package mouse
2+
3+
class FOptionSyntaxTest extends MouseSuite {
4+
test("FOptionSyntax.cata") {
5+
assertEquals(List(Option(1)).cata(0, _ => 2), List(2))
6+
assertEquals(List(Option.empty[Int]).cata(0, _ => 2), List(0))
7+
}
8+
9+
test("FOptionSyntax.cataF") {
10+
assertEquals(List(Option(1)).cataF(List.empty[Int], _ => List(2)), List(2))
11+
assertEquals(List(Option.empty[Int]).cataF(List.empty[Int], _ => List(2)), List.empty[Int])
12+
}
13+
14+
test("FOptionSyntax.existsIn") {
15+
assertEquals(Option(Option(1)).existsIn(_ > 2), Option(false))
16+
assertEquals(Option(Option(1)).existsIn(_ == 1), Option(true))
17+
assertEquals(Option(Option.empty[Int]).existsIn(_ > 2), Option(false))
18+
}
19+
20+
test("FOptionSyntax.existsF") {
21+
assertEquals(Option(Option(1)).existsF(a => Option(a > 2)), Option(false))
22+
assertEquals(Option(Option(1)).existsF(a => Option(a == 1)), Option(true))
23+
assertEquals(Option(Option.empty[Int]).existsF(a => Option(a > 2)), Option(false))
24+
}
25+
26+
test("FOptionSyntax.filterIn") {
27+
assertEquals(Option(Option(1)).filterIn(_ > 2), Option(Option.empty[Int]))
28+
assertEquals(Option(Option(1)).filterIn(_ == 1), Option(Option(1)))
29+
assertEquals(Option(Option.empty[Int]).filterIn(_ > 2), Option(Option.empty))
30+
}
31+
32+
test("FOptionSyntax.filterF") {
33+
assertEquals(Option(Option(1)).filterF(a => Option(a > 2)), Option(Option.empty[Int]))
34+
assertEquals(Option(Option(1)).filterF(a => Option(a == 1)), Option(Option(1)))
35+
assertEquals(Option(Option.empty[Int]).filterF(a => Option(a > 2)), Option(Option.empty[Int]))
36+
}
37+
38+
test("FOptionSyntax.flatMapIn") {
39+
assertEquals(List(Option(1)).flatMapIn(a => Option(a * 2)), List(Option(2)))
40+
assertEquals(List(Option.empty[Int]).flatMapIn(a => Option(a * 2)), List(Option.empty[Int]))
41+
}
42+
43+
test("FOptionSyntax.flatMapF") {
44+
assertEquals(List(Option(1)).flatMapF(a => List(Option(a * 2))), List(Option(2)))
45+
assertEquals(List(Option.empty[Int]).flatMapF(a => List(Option(a * 2))), List(Option.empty[Int]))
46+
}
47+
48+
test("FOptionSyntax.foldIn") {
49+
assertEquals(List(Option(1)).foldIn(false)(_ => true), List(true))
50+
assertEquals(List(Option.empty[Int]).foldIn(false)(_ => true), List(false))
51+
}
52+
53+
test("FOptionSyntax.foldF") {
54+
assertEquals(List(Option(1)).foldF(List(false))(_ => List(true)), List(true))
55+
assertEquals(List(Option.empty[Int]).foldF(List(false))(_ => List(true)), List(false))
56+
}
57+
58+
test("FOptionSyntax.forallIn") {
59+
assertEquals(Option(Option(1)).forallIn(_ > 2), Option(false))
60+
assertEquals(Option(Option(1)).forallIn(_ == 1), Option(true))
61+
assertEquals(Option(Option.empty[Int]).forallIn(_ > 2), Option(true))
62+
}
63+
64+
test("FOptionSyntax.forallF") {
65+
assertEquals(Option(Option(1)).forallF(a => Option(a > 2)), Option(false))
66+
assertEquals(Option(Option(1)).forallF(a => Option(a == 1)), Option(true))
67+
assertEquals(Option(Option.empty[Int]).forallF(a => Option(a > 2)), Option(true))
68+
}
69+
70+
test("FOptionSyntax.getOrElse") {
71+
assertEquals(List(Option(1)).getOrElse(0), List(1))
72+
assertEquals(List(Option.empty[Int]).getOrElse(0), List(0))
73+
}
74+
75+
test("FOptionSyntax.getOrElseF") {
76+
assertEquals(List(Option(1)).getOrElseF(List(0)), List(1))
77+
assertEquals(List(Option.empty[Int]).getOrElseF(List(0)), List(0))
78+
}
79+
80+
test("FOptionSyntax.mapIn") {
81+
assertEquals(List(Option(1)).mapIn(_ + 1), List(Option(2)))
82+
assertEquals(List(Option.empty[Int]).mapIn(_ + 1), List(Option.empty[Int]))
83+
}
84+
85+
test("FOptionSyntax.orElseIn") {
86+
assertEquals(List(Option(1)).orElseIn(Option(0)), List(Option(1)))
87+
assertEquals(List(Option.empty[Int]).orElseIn(Option(0)), List(Option(0)))
88+
}
89+
90+
test("FOptionSyntax.orElseF") {
91+
assertEquals(List(Option(1)).orElseF(List(Option(0))), List(Option(1)))
92+
assertEquals(List(Option.empty[Int]).orElseF(List(Option(0))), List(Option(0)))
93+
}
94+
95+
test("FOptionSyntax.traverseIn") {
96+
assertEquals(List(Option(1)).traverseIn(a => List(a * 2)), List(List(Option(2))))
97+
assertEquals(List.empty[Option[Int]].traverseIn(a => List(a * 2)), List.empty[List[Option[Int]]])
98+
assertEquals(List(Option.empty[Int]).traverseIn(a => List(a * 2)), List(List(Option.empty[Int])))
99+
}
100+
101+
test("FOptionSyntax.traverseF") {
102+
assertEquals(List(Option(1)).traverseF(a => Option(a * 2)), Option(List(Option(2))))
103+
assertEquals(List.empty[Option[Int]].traverseF(a => Option(a * 2)), Option(List.empty[Option[Int]]))
104+
assertEquals(List(Option.empty[Int]).traverseF(a => Option(a * 2)), Option(List(Option.empty[Int])))
105+
}
106+
}

0 commit comments

Comments
 (0)