Skip to content

Instantly share code, notes, and snippets.

@matfournier
Created May 29, 2020 05:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save matfournier/e667b5014fb194c2bc11512d63b2945e to your computer and use it in GitHub Desktop.
Save matfournier/e667b5014fb194c2bc11512d63b2945e to your computer and use it in GitHub Desktop.
kleisli.combineK
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