Created
May 29, 2020 05:17
-
-
Save matfournier/e667b5014fb194c2bc11512d63b2945e to your computer and use it in GitHub Desktop.
kleisli.combineK
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.hootsuite.service.channelauthaudit.services.forensics.models | |
import org.scalatest.{FlatSpec, Matchers} | |
import cats._ | |
import cats.data._ | |
import cats.implicits._ | |
class BlahTest extends FlatSpec with Matchers { | |
import KleisliOps._ | |
it should "match parsers" in { | |
// type Parser = ReaderT[Option, String, Match] | |
val containsCharacterA: ParserA[Option] = Kleisli(s => | |
if(s.contains("a")) IsMatch("contains a").some else None | |
) | |
val containsCharacterB: ParserA[Option] = Kleisli(s => { | |
println("why does this run?") | |
if (s.contains("b")) IsMatch("contains b").some else None | |
} | |
) | |
// val parserAs = containsCharacterA <+> containsCharacterB // eager doesn't work | |
//. since kleisli.combine doesn't know what F is | |
// so it runs both branches but returns the first | |
// need to use Defer in order to get it to work | |
// OR define some OrElse behavior? | |
val parserAs2 = containsCharacterA <|> containsCharacterB // orElse behavior? | |
val testA = "this a" | |
val testB = "this b" | |
val testBoth = testA + testB | |
val result = parserAs2.run(testBoth) // returns Some(IsMatch("contains b")), side-effect of B runs | |
result shouldBe Some(IsMatch("contains a")) | |
} | |
it should "fold in " in { | |
// type Parser = ReaderT[Option, String, Match] | |
val containsCharacterA: ParserA[Option] = Kleisli(s => | |
if (s.contains("a")) IsMatch("contains a").some else None | |
) | |
val containsCharacterB: ParserA[Option] = Kleisli(s => { | |
println("why does this run?") | |
if (s.contains("b")) IsMatch("contains b").some else None | |
} | |
) | |
val parserAs2 = List(containsCharacterA,containsCharacterB).reduceRight(_ <|> _) | |
val testA = "this a" | |
val testB = "this b" | |
val testBoth = testA + testB | |
val result = parserAs2.run(testBoth) // returns Some(IsMatch("contains b")), side-effect of B runs | |
result shouldBe Some(IsMatch("contains a")) | |
} | |
it should "dunno explicit" in { | |
object Parser { | |
type Parser[B] = B => Option[Match] | |
val containsCharacterA: Parser[String] = s => | |
if(s.contains("a")) IsMatch("contains a").some else None | |
val containsCharacterB: Parser[String] = s => { | |
println("why does this run?") | |
if (s.contains("b")) IsMatch("contains b").some else None | |
} | |
implicit val ck: SemigroupK[Parser] = new SemigroupK[Parser] { | |
override def combineK[A](x: Parser.Parser[A], y: Parser.Parser[A]): Parser.Parser[A] = s => | |
x(s) orElse y(s) | |
} | |
} | |
import Parser._ | |
val testBoth = "this a this b" | |
val parsers = containsCharacterA <+> containsCharacterB | |
val result = parsers(testBoth) | |
result shouldBe Some(IsMatch("contains a")) | |
} | |
} | |
object KleisliOps { | |
type ParserA[F[_]] = Kleisli[F, String, Match] | |
implicit class KleisliThing(private val p: ParserA[Option]) extends AnyVal { | |
def <|> (p2: ParserA[Option]): ParserA[Option] = Kleisli { | |
s => p(s) orElse p2(s) | |
} | |
} | |
} | |
sealed trait Match | |
case class IsMatch(s: String) extends Match | |
case object NoMatch extends Match |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment