Skip to content

Instantly share code, notes, and snippets.

@bvenners
Last active January 2, 2016 03:18
Show Gist options
  • Save bvenners/8242691 to your computer and use it in GitHub Desktop.
Save bvenners/8242691 to your computer and use it in GitHub Desktop.
An example of giving one type multiple Applicative personalities
// Here is code that can be pasted into the REPL
import scalaz._
import Scalaz._
import org.scalautils._
def all[F[_]: Applicative, A](s: String, parsers: List[String => F[A]]): F[List[A]] = parsers.traverse(p => p(s))
implicit class BadDog[G, B](or: Or[G, B]) {
def badMap[C](bToC: B => C): G Or C = or.swap.map(bToC).swap
}
trait LowPriorityImplicit {
implicit def orMonad[B]: Monad[({type l[g]=g Or B})#l] = new Monad[({type l[g]=g Or B})#l] {
def point[G](g: => G) = Good(g)
def bind[G, H](fa: G Or B)(f: G => H Or B) = fa flatMap f
}
}
object Implicits extends LowPriorityImplicit {
implicit def accumulatingOrApplicative[G, B, E[b] <: Every[b]]: Applicative[({type l[g] = g Or Every[B]})#l] =
new Applicative[({type l[g] = g Or Every[B]})#l] {
override def map[G, H](fa: G Or Every[B])(f: G => H) = fa map f
def point[G](g: => G) = Good(g)
def ap[G, H](fa: => G Or Every[B])(f: => ((G => H) Or Every[B])): H Or Every[B] =
(fa, f) match {
case (Good(g), Good(f)) => Good(f(g))
case (Bad(b), Good(_)) => Bad(b)
case (Good(f), Bad(b)) => Bad(b)
case (Bad(b1), Bad(b2)) => Bad(b1 ++ b2)
}
}
}
import Implicits._
def toIntE(s: String): Int Or ErrorMessage = attempt(s.toInt).badMap(_.getMessage)
def one: String => (Int Or List[ErrorMessage]) = s => toIntE(s).badMap(List(_))
def two: String => (Int Or List[ErrorMessage]) = s => toIntE(s.take(2)).badMap(List(_))
def three: String => (Int Or List[ErrorMessage]) = s => toIntE(s.drop(2)).badMap(List(_))
all[({ type l[a] = a Or List[ErrorMessage] })#l, Int]("1234", List(one, two, three))
all[({ type l[a] = a Or List[ErrorMessage] })#l, Int]("asfasf", List(one, two, three))
def oneA: String => (Int Or Every[ErrorMessage]) = s => toIntE(s).badMap(Every(_))
def twoA: String => (Int Or Every[ErrorMessage]) = s => toIntE(s.take(2)).badMap(Every(_))
def threeA: String => (Int Or Every[ErrorMessage]) = s => toIntE(s.drop(2)).badMap(Every(_))
all[({ type l[a] = a Or Every[ErrorMessage] })#l, Int]("1234", List(oneA, twoA, threeA))
all[({ type l[a] = a Or Every[ErrorMessage] })#l, Int]("asfasf", List(oneA, twoA, threeA))
// And here is how it looks when you paste it into the REPL:
scala> import scalaz._
import scalaz._
scala> import Scalaz._
import Scalaz._
scala> import org.scalautils._
import org.scalautils._
scala> def all[F[_]: Applicative, A](s: String, parsers: List[String => F[A]]): F[List[A]] = parsers.traverse(p => p(s))
warning: there were 1 feature warning(s); re-run with -feature for details
all: [F[_], A](s: String, parsers: List[String => F[A]])(implicit evidence$1: scalaz.Applicative[F])F[List[A]]
scala> implicit class BadDog[G, B](or: Or[G, B]) {
| def badMap[C](bToC: B => C): G Or C = or.swap.map(bToC).swap
| }
defined class BadDog
scala> trait LowPriorityImplicit {
| implicit def orMonad[B]: Monad[({type l[g]=g Or B})#l] = new Monad[({type l[g]=g Or B})#l] {
| def point[G](g: => G) = Good(g)
| def bind[G, H](fa: G Or B)(f: G => H Or B) = fa flatMap f
| }
| }
defined trait LowPriorityImplicit
scala> object Implicits extends LowPriorityImplicit {
| implicit def accumulatingOrApplicative[G, B, E[b] <: Every[b]]: Applicative[({type l[g] = g Or Every[B]})#l] =
| new Applicative[({type l[g] = g Or Every[B]})#l] {
| override def map[G, H](fa: G Or Every[B])(f: G => H) = fa map f
|
| def point[G](g: => G) = Good(g)
|
| def ap[G, H](fa: => G Or Every[B])(f: => ((G => H) Or Every[B])): H Or Every[B] =
| (fa, f) match {
| case (Good(g), Good(f)) => Good(f(g))
| case (Bad(b), Good(_)) => Bad(b)
| case (Good(f), Bad(b)) => Bad(b)
| case (Bad(b1), Bad(b2)) => Bad(b1 ++ b2)
| }
| }
| }
warning: there were 1 feature warning(s); re-run with -feature for details
defined module Implicits
scala> import Implicits._
import Implicits._
scala> def toIntE(s: String): Int Or ErrorMessage = attempt(s.toInt).badMap(_.getMessage)
toIntE: (s: String)org.scalautils.Or[Int,org.scalautils.ErrorMessage]
scala> def one: String => (Int Or List[ErrorMessage]) = s => toIntE(s).badMap(List(_))
one: String => org.scalautils.Or[Int,List[org.scalautils.ErrorMessage]]
scala> def two: String => (Int Or List[ErrorMessage]) = s => toIntE(s.take(2)).badMap(List(_))
two: String => org.scalautils.Or[Int,List[org.scalautils.ErrorMessage]]
scala> def three: String => (Int Or List[ErrorMessage]) = s => toIntE(s.drop(2)).badMap(List(_))
three: String => org.scalautils.Or[Int,List[org.scalautils.ErrorMessage]]
scala> all[({ type l[a] = a Or List[ErrorMessage] })#l, Int]("1234", List(one, two, three))
res0: org.scalautils.Or[List[Int],List[String]] = Good(List(1234, 12, 34))
scala> all[({ type l[a] = a Or List[ErrorMessage] })#l, Int]("asfasf", List(one, two, three))
res1: org.scalautils.Or[List[Int],List[String]] = Bad(List(For input string: "asfasf"))
scala> def oneA: String => (Int Or Every[ErrorMessage]) = s => toIntE(s).badMap(Every(_))
oneA: String => org.scalautils.Or[Int,org.scalautils.Every[org.scalautils.ErrorMessage]]
scala> def twoA: String => (Int Or Every[ErrorMessage]) = s => toIntE(s.take(2)).badMap(Every(_))
twoA: String => org.scalautils.Or[Int,org.scalautils.Every[org.scalautils.ErrorMessage]]
scala> def threeA: String => (Int Or Every[ErrorMessage]) = s => toIntE(s.drop(2)).badMap(Every(_))
threeA: String => org.scalautils.Or[Int,org.scalautils.Every[org.scalautils.ErrorMessage]]
scala> all[({ type l[a] = a Or Every[ErrorMessage] })#l, Int]("1234", List(oneA, twoA, threeA))
res2: org.scalautils.Or[List[Int],org.scalautils.Every[String]] = Good(List(1234, 12, 34))
scala> all[({ type l[a] = a Or Every[ErrorMessage] })#l, Int]("asfasf", List(oneA, twoA, threeA))
res3: org.scalautils.Or[List[Int],org.scalautils.Every[String]] = Bad(Many(For input string: "fasf", For input string: "as", For input string: "asfasf"))
@bvenners
Copy link
Author

bvenners commented Jan 3, 2014

Note: there was a typo in the original code I pasted in. I had an extraneous s on the end of LowPriorityImplicit, which only worked because of history in my REPL session. With s removed this now works out of the box in a project that includes only scalaz and scalautils.

@S11001001
Copy link

An inferency wrapper around all:

def allU[FA](s: String, parsers: List[String => FA])(implicit U: Unapply[Applicative, FA]): U.M[List[U.A]] =
  all(s, U.leibniz.subst[({type l[a] = List[String => a]})#l](parsers))(U.TC)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment