Last active
September 21, 2016 14:25
-
-
Save ramirez7/526e2dd952c50955571ec07d84f0cd4a to your computer and use it in GitHub Desktop.
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
| import scalaz._, Scalaz._, \&/.{Both, This, That} | |
| object TheseUtils { | |
| // For reference: | |
| // sealed trait \&/[+A, +B] | |
| // case class This[A](aa: A) extends (A \&/ Nothing) | |
| // case class That[B](bb: B) extends (Nothing \&/ B) | |
| // case class Both[A, B](aa: A, bb: B) extends (A \&/ B) | |
| // smart constructors | |
| def both[A,B](a: A, b: B): A \&/ B = Both(a, b) | |
| def this_[A, B](a: A): A \&/ B = This(a) // `this_` because `this` is a keyword | |
| def that[A, B](b: B): A \&/ B = That(b) | |
| implicit class RichThese[A,B](val these: A \&/ B) extends AnyVal { | |
| def withThis(a: A): A \&/ B = these.b.map(b => Both(a,b)).getOrElse(This(a)) | |
| def withThat(b: B): A \&/ B = these.a.map(a => Both(a,b)).getOrElse(That(b)) | |
| } | |
| // `At` is a typeclass supporting the updating of arbitrarily nested These | |
| // `update` updates A (which is a These) with B | |
| // `insert` creates a new A (which is a These) that only contains B | |
| @scala.annotation.implicitNotFound("Could not find ${B} within ${A}. ${A} should be a nested These") | |
| sealed trait At[A, B] { | |
| def update(these: A, fresh: B): A | |
| def insert(fresh: B): A | |
| } | |
| implicit def thisAt[A, B]: At[A \&/ B, A] = new At[A \&/ B, A] { | |
| override def update(these: A \&/ B, fresh: A): A \&/ B = these.withThis(fresh) | |
| override def insert(fresh: A): A \&/ B = this_[A,B](fresh) | |
| } | |
| implicit def thatAt[A, B]: At[A \&/ B, B] = new At[A \&/ B, B] { | |
| override def update(these: A \&/ B, fresh: B): A \&/ B = these.withThat(fresh) | |
| override def insert(fresh: B): A \&/ B = that[A,B](fresh) | |
| } | |
| implicit def theseAt[A, B, C](implicit ev: At[A, C]): At[A \&/ B, C] = new At[A \&/ B, C] { | |
| override def update(these: A \&/ B, fresh: C): A \&/ B = | |
| these.withThis(these.a.map(a => ev.update(a, fresh)).getOrElse(ev.insert(fresh))) | |
| override def insert(fresh: C): A \&/ B = this_[A,B](ev.insert(fresh)) | |
| } | |
| implicit class AtOps[A, B](val these: A \&/ B) extends AnyVal { | |
| def at[C](c: C)(implicit at: At[A \&/ B, C]): A \&/ B = at.update(these, c) | |
| } | |
| sealed trait Get[A, B] { | |
| def get(these: A): Option[B] | |
| } | |
| implicit def thisGet[A, B]: Get[A \&/ B, A] = new Get[A \&/ B, A] { | |
| override def get(these: A \&/ B): Option[A] = these.a | |
| } | |
| implicit def thatGet[A, B]: Get[A \&/ B, B] = new Get[A \&/ B, B] { | |
| override def get(these: A \&/ B): Option[B] = these.b | |
| } | |
| implicit def theseGet[A, B, C](implicit ev: Get[A, C]): Get[A \&/ B, C] = new Get[A \&/ B, C] { | |
| override def get(these: A \&/ B): Option[C] = these.a.flatMap(ev.get) | |
| } | |
| implicit class GetOps[A, B](val these: A \&/ B) extends AnyVal { | |
| def get[C](implicit get: Get[A \&/ B, C]): Option[C] = get.get(these) | |
| } | |
| val x: Boolean \&/ Int \&/ String \&/ Long = that(2) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment