Skip to content

Commit 437af75

Browse files
authored
Implement FunctorFilter and add some more laws (#108)
* Add mapOrFail, and some more laws * Use FunctorFilter and names for Parsers
1 parent bd8cef4 commit 437af75

2 files changed

Lines changed: 191 additions & 14 deletions

File tree

core/shared/src/main/scala/cats/parse/Parser.scala

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
package cats.parse
2323

24-
import cats.{Eval, Monad, Defer, Alternative, FlatMap, Now, MonoidK, Order}
24+
import cats.{Eval, FunctorFilter, Monad, Defer, Alternative, FlatMap, Now, MonoidK, Order}
2525
import cats.data.{AndThen, Chain, NonEmptyList}
2626

2727
import cats.implicits._
@@ -189,6 +189,33 @@ sealed abstract class Parser[+A] {
189189
def map[B](fn: A => B): Parser[B] =
190190
Parser.map(this)(fn)
191191

192+
/** Transform parsed values using the given function, or fail on None
193+
*
194+
* When the function return None, this parser fails
195+
* This is implemented with select, which makes it more efficient
196+
* than using flatMap
197+
*/
198+
def mapFilter[B](fn: A => Option[B]): Parser[B] = {
199+
val leftUnit = Left(())
200+
201+
val first = map { a =>
202+
fn(a) match {
203+
case Some(b) => Right(b)
204+
case None => leftUnit
205+
}
206+
}
207+
Parser.select(first)(Parser.Fail)
208+
}
209+
210+
/** Transform parsed values using the given function, or fail when not defined
211+
*
212+
* When the function is not defined, this parser fails
213+
* This is implemented with select, which makes it more efficient
214+
* than using flatMap
215+
*/
216+
def collect[B](fn: PartialFunction[A, B]): Parser[B] =
217+
mapFilter(fn.lift)
218+
192219
/** If the predicate is not true, fail
193220
* you may want .filter(fn).backtrack so if the filter fn
194221
* fails you can fall through in an oneOf or orElse
@@ -357,11 +384,30 @@ sealed abstract class Parser1[+A] extends Parser[A] {
357384
def <*[B](that: Parser[B]): Parser1[A] =
358385
(this ~ that.void).map(_._1)
359386

387+
/** This method overrides `Parser#collect` to refine the return type.
388+
*/
389+
override def collect[B](fn: PartialFunction[A, B]): Parser1[B] =
390+
mapFilter(fn.lift)
391+
360392
/** This method overrides `Parser#map` to refine the return type.
361393
*/
362394
override def map[B](fn: A => B): Parser1[B] =
363395
Parser.map1(this)(fn)
364396

397+
/** This method overrides `Parser#mapFilter` to refine the return type.
398+
*/
399+
override def mapFilter[B](fn: A => Option[B]): Parser1[B] = {
400+
val leftUnit = Left(())
401+
402+
val first = map { a =>
403+
fn(a) match {
404+
case Some(b) => Right(b)
405+
case None => leftUnit
406+
}
407+
}
408+
Parser.select1(first)(Parser.Fail)
409+
}
410+
365411
/** This method overrides `Parser#flatMap` to refine the return type.
366412
*/
367413
override def flatMap[B](fn: A => Parser[B]): Parser1[B] =
@@ -1279,16 +1325,28 @@ object Parser extends ParserInstances {
12791325
case (notSingleChar, _) => notSingleChar.map(Impl.ConstFn(b))
12801326
}
12811327

1282-
implicit val catsInstancesParser1: FlatMap[Parser1] with Defer[Parser1] with MonoidK[Parser1] =
1283-
new FlatMap[Parser1] with Defer[Parser1] with MonoidK[Parser1] {
1328+
implicit val catsInstancesParser1
1329+
: FlatMap[Parser1] with Defer[Parser1] with MonoidK[Parser1] with FunctorFilter[Parser1] =
1330+
new FlatMap[Parser1] with Defer[Parser1] with MonoidK[Parser1] with FunctorFilter[Parser1] {
12841331
def empty[A] = Fail
12851332

12861333
def defer[A](pa: => Parser1[A]): Parser1[A] =
12871334
defer1(pa)
12881335

1336+
def functor = this
1337+
12891338
def map[A, B](fa: Parser1[A])(fn: A => B): Parser1[B] =
12901339
map1(fa)(fn)
12911340

1341+
def mapFilter[A, B](fa: Parser1[A])(f: A => Option[B]): Parser1[B] =
1342+
fa.mapFilter(f)
1343+
1344+
override def filter[A](fa: Parser1[A])(fn: A => Boolean): Parser1[A] =
1345+
fa.filter(fn)
1346+
1347+
override def filterNot[A](fa: Parser1[A])(fn: A => Boolean): Parser1[A] =
1348+
fa.filter { a => !fn(a) }
1349+
12921350
def flatMap[A, B](fa: Parser1[A])(fn: A => Parser1[B]): Parser1[B] =
12931351
flatMap10(fa)(fn)
12941352

@@ -2175,16 +2233,28 @@ object Parser extends ParserInstances {
21752233
}
21762234

21772235
abstract class ParserInstances {
2178-
implicit val catInstancesParser: Monad[Parser] with Alternative[Parser] with Defer[Parser] =
2179-
new Monad[Parser] with Alternative[Parser] with Defer[Parser] {
2236+
implicit val catInstancesParser
2237+
: Monad[Parser] with Alternative[Parser] with Defer[Parser] with FunctorFilter[Parser] =
2238+
new Monad[Parser] with Alternative[Parser] with Defer[Parser] with FunctorFilter[Parser] {
21802239
def pure[A](a: A): Parser[A] = Parser.pure(a)
21812240

21822241
def defer[A](a: => Parser[A]) = Parser.defer(a)
21832242

21842243
def empty[A]: Parser[A] = Parser.Fail
21852244

2245+
def functor = this
2246+
21862247
override def map[A, B](fa: Parser[A])(fn: A => B): Parser[B] = Parser.map(fa)(fn)
21872248

2249+
def mapFilter[A, B](fa: Parser[A])(f: A => Option[B]): Parser[B] =
2250+
fa.mapFilter(f)
2251+
2252+
override def filter[A](fa: Parser[A])(fn: A => Boolean): Parser[A] =
2253+
fa.filter(fn)
2254+
2255+
override def filterNot[A](fa: Parser[A])(fn: A => Boolean): Parser[A] =
2256+
fa.filter { a => !fn(a) }
2257+
21882258
override def product[A, B](fa: Parser[A], fb: Parser[B]): Parser[(A, B)] =
21892259
Parser.product(fa, fb)
21902260

core/shared/src/test/scala/cats/parse/ParserTest.scala

Lines changed: 116 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -496,10 +496,29 @@ object ParserGen {
496496
)
497497
}
498498

499+
def genParser[A](genA: Gen[A]): Gen[Parser[A]] =
500+
for {
501+
genT <- gen
502+
fn <- Gen.function1(genA)(genT.cogen)
503+
} yield genT.fa.map(fn)
504+
505+
def genParser1[A](genA: Gen[A]): Gen[Parser1[A]] =
506+
for {
507+
genT <- gen1
508+
fn <- Gen.function1(genA)(genT.cogen)
509+
} yield genT.fa.map(fn)
510+
511+
implicit def arbParser[A: Arbitrary]: Arbitrary[Parser[A]] =
512+
Arbitrary(genParser(Arbitrary.arbitrary[A]))
513+
514+
implicit def arbParser1[A: Arbitrary]: Arbitrary[Parser1[A]] =
515+
Arbitrary(genParser1(Arbitrary.arbitrary[A]))
499516
}
500517

501518
class ParserTest extends munit.ScalaCheckSuite {
502519

520+
import ParserGen.{arbParser, arbParser1}
521+
503522
val tests: Int = if (BitSetUtil.isScalaJs) 50 else 2000
504523

505524
override def scalaCheckTestParameters =
@@ -1517,6 +1536,20 @@ class ParserTest extends munit.ScalaCheckSuite {
15171536
}
15181537
}
15191538

1539+
property("a.backtrack.peek.orElse(b.peek) == (a.backtrack.orElse(b)).peek") {
1540+
forAll(ParserGen.gen, ParserGen.gen, Arbitrary.arbitrary[String]) { (a, b, str) =>
1541+
val pa = a.fa.backtrack
1542+
val pb = b.fa
1543+
1544+
val left = pa.peek.orElse(pb.peek)
1545+
val right = pa.orElse(pb).peek
1546+
1547+
val leftRes = left.parse(str).toOption
1548+
val rightRes = right.parse(str).toOption
1549+
assertEquals(leftRes, rightRes)
1550+
}
1551+
}
1552+
15201553
property("a.peek == a.peek *> a.peek") {
15211554
forAll(ParserGen.gen, Arbitrary.arbitrary[String]) { (a, str) =>
15221555
val pa = a.fa.peek
@@ -1580,6 +1613,28 @@ class ParserTest extends munit.ScalaCheckSuite {
15801613
}
15811614
}
15821615

1616+
property("!fail == unit") {
1617+
forAll { (str: String) =>
1618+
val left = !Parser.fail
1619+
val right = Parser.unit
1620+
1621+
val leftRes = left.parse(str)
1622+
val rightRes = right.parse(str)
1623+
assertEquals(leftRes, rightRes)
1624+
}
1625+
}
1626+
1627+
property("!pure(_) == fail") {
1628+
forAll { (str: String, i: Int) =>
1629+
val left = !Parser.pure(i)
1630+
val right = Parser.fail
1631+
1632+
val leftRes = left.parse(str).toOption
1633+
val rightRes = right.parse(str).toOption
1634+
assertEquals(leftRes, rightRes)
1635+
}
1636+
}
1637+
15831638
property("anyChar.repAs[String] parses the whole string") {
15841639
forAll { (str: String) =>
15851640
assertEquals(Parser.anyChar.repAs[String].parse(str), Right(("", str)))
@@ -1615,24 +1670,36 @@ class ParserTest extends munit.ScalaCheckSuite {
16151670
}
16161671
}
16171672

1618-
/*
16191673
property("select(pa.map(Left(_)))(pf) == (pa, pf).mapN((a, fn) => fn(a))") {
1620-
forAll(ParserGen.gen, ParserGen.gen, Arbitrary.arbitrary[String]) { (genP, genRes, str) =>
1621-
val pa = genP.fa
1622-
val pf = null: Parser[genP.A => genRes.A]
1623-
assertEquals(Parser.select(pa.map(Left(_)))(pf).parse(str), pf.ap(pa).parse(str))
1674+
forAll { (pa: Parser[Int], pf: Parser[Int => String], str: String) =>
1675+
assertEquals(
1676+
Parser.select(pa.map(Left(_)))(pf).parse(str),
1677+
(pa, pf).mapN((a, f) => f(a)).parse(str)
1678+
)
1679+
}
1680+
}
1681+
1682+
property("select1(pa.map(Left(_)))(pf) == (pa, pf).mapN((a, fn) => fn(a))") {
1683+
forAll { (pa: Parser1[Int], pf: Parser[Int => String], str: String) =>
1684+
assertEquals(
1685+
Parser.select(pa.map(Left(_)))(pf).parse(str),
1686+
(pa, pf).mapN((a, f) => f(a)).parse(str)
1687+
)
16241688
}
16251689
}
1626-
*/
16271690

16281691
property("select(pa.map(Right(_)))(pf) == pa") {
1629-
forAll(ParserGen.gen, ParserGen.gen, Arbitrary.arbitrary[String]) { (genP, genRes, str) =>
1630-
val pa = genRes.fa
1631-
val pf: Parser[genP.A => genRes.A] = Parser.fail
1692+
forAll { (pa: Parser[String], pf: Parser[Int => String], str: String) =>
16321693
assertEquals(Parser.select(pa.map(Right(_)))(pf).parse(str), pa.parse(str))
16331694
}
16341695
}
16351696

1697+
property("select1(pa.map(Right(_)))(pf) == pa") {
1698+
forAll { (pa: Parser1[String], pf: Parser[Int => String], str: String) =>
1699+
assertEquals(Parser.select1(pa.map(Right(_)))(pf).parse(str), pa.parse(str))
1700+
}
1701+
}
1702+
16361703
property("p.filter(_ => true) == p") {
16371704
forAll(ParserGen.gen, Arbitrary.arbitrary[String]) { (genP, str) =>
16381705
val res0 = genP.fa.filter(_ => true).parse(str)
@@ -1671,4 +1738,44 @@ class ParserTest extends munit.ScalaCheckSuite {
16711738
)
16721739
}
16731740
}
1741+
1742+
property("mapFilter is the same as filter + map") {
1743+
forAll { (pa: Parser[Int], fn: Int => Option[String], str: String) =>
1744+
val left = pa.mapFilter(fn)
1745+
val right = pa.map(fn).filter(_.isDefined).map(_.get)
1746+
1747+
assertEquals(left.parse(str), right.parse(str))
1748+
}
1749+
}
1750+
1751+
property("mapFilter is the same as filter + map Parser1") {
1752+
forAll { (pa: Parser1[Int], fn: Int => Option[String], str: String) =>
1753+
val left = pa.mapFilter(fn)
1754+
val right = pa.map(fn).filter(_.isDefined).map(_.get)
1755+
1756+
assertEquals(left.parse(str), right.parse(str))
1757+
}
1758+
}
1759+
1760+
property("collect is the same as filter + map") {
1761+
forAll { (pa: Parser[Int], fn: Int => Option[String], str: String) =>
1762+
val left = pa.collect {
1763+
case i if fn(i).isDefined => fn(i).get
1764+
}
1765+
val right = pa.map(fn).filter(_.isDefined).map(_.get)
1766+
1767+
assertEquals(left.parse(str), right.parse(str))
1768+
}
1769+
}
1770+
1771+
property("collect is the same as filter + map Parser1") {
1772+
forAll { (pa: Parser1[Int], fn: Int => Option[String], str: String) =>
1773+
val left = pa.collect {
1774+
case i if fn(i).isDefined => fn(i).get
1775+
}
1776+
val right = pa.map(fn).filter(_.isDefined).map(_.get)
1777+
1778+
assertEquals(left.parse(str), right.parse(str))
1779+
}
1780+
}
16741781
}

0 commit comments

Comments
 (0)