Skip to content

Instantly share code, notes, and snippets.

@vkostyukov
Last active August 29, 2015 14:16
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 vkostyukov/4d6cbc64a1411d2aa4a2 to your computer and use it in GitHub Desktop.
Save vkostyukov/4d6cbc64a1411d2aa4a2 to your computer and use it in GitHub Desktop.
A sane alternative to F1[A], ..., F22[A, B, ..., V]
// A type for a composite value
case class ~[+A, +B](a: A, b: B)
// An applicative functor
trait F[+A] { self =>
def value: A
def ~[B](that: F[B]): F[A ~ B] = new F[A ~ B] {
def value: A ~ B = new ~(self.value, that.value)
}
}
// A helper factory method
object F {
def apply[A](v: A): F[A] = new F[A] {
def value: A = v
}
}
// We define a base map implimentation as a low-piority one
trait LowPriorityFOps {
implicit class FMap1[A](fa: F[A]) {
def map[B](f: A => B): F[B] = new F[B] {
def value: B = f(fa.value)
}
}
}
// Other implementations have a higher priority
object FOps extends LowPriorityFOps {
// Allows to map F[A ~ B] to a function (A, B) => C
implicit class FMap2[A, B](fab: F[A ~ B]) {
def map[C](f: (A, B) => C): F[C] = new F[C] {
def value: C = fab.value match {
case a ~ b => f(a, b)
}
}
}
// Allows to map F[A ~ B ~ C] to a function (A, B, C) => D
implicit class FMap3[A, B, C](fabc: F[A ~ B ~ C]) {
def map[C](f: (A, B, C) => D): F[D] = new F[D] {
def value: D = fabc.value match {
case a ~ b ~ c => f(a, b, c)
}
}
}
}
import FOps._
// Uses low-priority FMap1 ops
val r0: F[Int] = F("a").map(_.length)
// Uses high-priority FMap2 ops
val r1: F[Int] = (F(10) ~ F(20)).map(_ + _)
// Uses high-priority FMap3 ops
case class Foo(s: String, i: Int, d: Double)
val r2: F[Foo] = (F("foo") ~ F(10) ~ F(10.0)).map(Foo)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment