Skip to content

Instantly share code, notes, and snippets.

@ramirez7
Last active September 21, 2016 14:25
Show Gist options
  • Select an option

  • Save ramirez7/526e2dd952c50955571ec07d84f0cd4a to your computer and use it in GitHub Desktop.

Select an option

Save ramirez7/526e2dd952c50955571ec07d84f0cd4a to your computer and use it in GitHub Desktop.
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