Skip to content

Instantly share code, notes, and snippets.

@edofic
Last active August 31, 2017 15:37
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 edofic/7f0673b07726cb2783b21af67f788ca6 to your computer and use it in GitHub Desktop.
Save edofic/7f0673b07726cb2783b21af67f788ca6 to your computer and use it in GitHub Desktop.
Tuple apply method in scala
object App {
// this integer references can be avoided when/if SIP-23 - Literal-based
// singleton types is implemented
val _1 = 1: Integer
val _2 = 2: Integer
val _3 = 3: Integer
// ... up to 22
trait TupleGet[T, A, N <: Integer] {
def get(t: T): A
}
object TupleGet {
implicit def tuple2get1[A, B] = new TupleGet[(A, B), A, _1.type] {
def get(t: (A, B)): A = t._1
}
implicit def tuple2get2[A, B] = new TupleGet[(A, B), B, _2.type] {
def get(t: (A, B)): B = t._2
}
implicit def tuple3get1[A, B, C] = new TupleGet[(A, B, C), A, _1.type] {
def get(t: (A, B, C)): A = t._1
}
implicit def tuple3get2[A, B, C] = new TupleGet[(A, B, C), B, _2.type] {
def get(t: (A, B, C)): B = t._2
}
implicit def tuple3get3[A, B, C] = new TupleGet[(A, B, C), C, _3.type] {
def get(t: (A, B, C)): C = t._3
}
// ...
}
implicit class TupleAccessor[T](tuple: T) {
def apply[A](i: Integer)(implicit getter: TupleGet[T, A, i.type]): A =
getter.get(tuple)
}
def main(args: Array[String]) {
val t1 = ("1", 123)
val t2 = (false, 1234, "foobar")
println(t1(_2))
println(t2(_1))
println(t2(_3))
}
}
import shapeless.nat.{_1, _2, _3}
object App {
trait TupleGet[T, A, N] {
def get(t: T): A
}
object TupleGet {
implicit def tuple2get1[A, B] = new TupleGet[(A, B), A, _1.type] {
def get(t: (A, B)): A = t._1
}
implicit def tuple2get2[A, B] = new TupleGet[(A, B), B, _2.type] {
def get(t: (A, B)): B = t._2
}
implicit def tuple3get1[A, B, C] = new TupleGet[(A, B, C), A, _1.type] {
def get(t: (A, B, C)): A = t._1
}
implicit def tuple3get2[A, B, C] = new TupleGet[(A, B, C), B, _2.type] {
def get(t: (A, B, C)): B = t._2
}
implicit def tuple3get3[A, B, C] = new TupleGet[(A, B, C), C, _3.type] {
def get(t: (A, B, C)): C = t._3
}
// ...
}
implicit class TupleAccessor[T](tuple: T) {
def apply[A](i: AnyRef)(implicit getter: TupleGet[T, A, i.type]): A =
getter.get(tuple)
}
def main(args: Array[String]) {
val t1 = ("1", 123)
val t2 = (false, 1234, "foobar")
println(t1(_2))
println(t2(_1))
println(t2(_3))
}
}
import shapeless._
import shapeless.nat._
object App {
trait TupleGet[T, A, N <: Nat] {
def get(t: T): A
}
object TupleGet {
implicit def tuple2get1[A, B] = new TupleGet[(A, B), A, _1.N] {
def get(t: (A, B)): A = t._1
}
implicit def tuple2get2[A, B] = new TupleGet[(A, B), B, _2.N] {
def get(t: (A, B)): B = t._2
}
implicit def tuple3get1[A, B, C] = new TupleGet[(A, B, C), A, _1.N] {
def get(t: (A, B, C)): A = t._1
}
implicit def tuple3get2[A, B, C] = new TupleGet[(A, B, C), B, _2.N] {
def get(t: (A, B, C)): B = t._2
}
implicit def tuple3get3[A, B, C] = new TupleGet[(A, B, C), C, _3.N] {
def get(t: (A, B, C)): C = t._3
}
// ...
}
implicit class TupleAccessor[T](tuple: T) {
def apply[A](i: Nat)(implicit getter: TupleGet[T, A, i.N]): A =
getter.get(tuple)
}
def main(args: Array[String]) {
val t1 = ("1", 123)
val t2 = (false, 1234, "foobar")
println(t1(2))
println(t2(1))
println(t2(3))
}
}
import shapeless.syntax.std.tuple._
object App {
def main(args: Array[String]) {
val t1 = ("1", 123)
val t2 = (false, 1234, "foobar")
println(t1.apply(0))
println(t2.apply(1))
println(t2.apply(2))
}
}
sealed trait TNat
case class TZero() extends TNat
case class TSucc[A <: TNat](prev: A) extends TNat
sealed trait Nat {
type N <: TNat
}
case object Zero extends Nat {
type N = TZero
}
case class Succ(prev: Nat) extends Nat {
type N = TSucc[prev.N]
}
object Nat {
implicit def fromInt(n: Int): Nat = {
assert(n >= 0)
if (n == 0) {
Zero
} else {
Succ(fromInt(n - 1))
}
}
}
trait At[T, A, N <: TNat] {
def apply(t: T): A
}
object At {
implicit def tuple2_at0[A, B] = new At[(A, B), A, TZero] {
def apply(t: (A, B)): A = t._1
}
implicit def tuple2_at1[A, B] = new At[(A, B), B, TSucc[TZero]] {
def apply(t: (A, B)): B = t._2
}
implicit def tuple3_at0[A, B, C] = new At[(A, B, C), A, TZero] {
def apply(t: (A, B, C)): A = t._1
}
implicit def tuple3_at1[A, B, C] = new At[(A, B, C), B, TSucc[TZero]] {
def apply(t: (A, B, C)): B = t._2
}
implicit def tuple3_at2[A, B, C] = new At[(A, B, C), C, TSucc[TSucc[TZero]]] {
def apply(t: (A, B, C)): C = t._3
}
}
object App {
implicit class TupleGet[T](t: T) {
def apply[A](n: Nat)(implicit at: At[T, A, n.N]): A = at(t)
}
def main(args: Array[String]) {
val t1 = ("1", 123)
val t2 = (false, 1234, "foobar")
// these now don't compile since n.N is not known at compile time
println(t1.apply(0))
println(t2.apply(1))
println(t2.apply(2))
}
}
// build.sbt
scalaVersion in ThisBuild := "2.12.3"
lazy val nats = (project in file("nats")).settings(
libraryDependencies += "org.scala-lang" % "scala-reflect" % "2.12.3"
)
lazy val core = (project in file("core")).dependsOn(nats)
// nats/nats.scala
import scala.language.experimental.macros
import scala.language.implicitConversions
import scala.reflect.macros.whitebox.Context
sealed trait TNat
trait TZero extends TNat
trait TSucc[N <: TNat] extends TNat
trait Nat {
type N <: TNat
def value: Int
}
object Nat {
def fromIntImpl(c: Context)(n: c.Expr[Int]): c.Expr[Nat] = {
import c.universe._
val value = c.eval(n)
assert(value >= 0)
val type_ = (1 to value).foldLeft("TZero")( (t, _) => s"TSucc[$t]")
c.Expr[Nat](c.parse(s"new Nat { type N = $type_; val value = ${value} }"))
}
implicit def fromInt(n: Int): Nat = macro fromIntImpl
}
// core/tuples.scala
trait At[T, A, N <: TNat] {
def apply(t: T): A
}
object At {
implicit def tuple2_at0[A, B] = new At[(A, B), A, TZero] {
def apply(t: (A, B)): A = t._1
}
implicit def tuple2_at1[A, B] = new At[(A, B), B, TSucc[TZero]] {
def apply(t: (A, B)): B = t._2
}
implicit def tuple3_at0[A, B, C] = new At[(A, B, C), A, TZero] {
def apply(t: (A, B, C)): A = t._1
}
implicit def tuple3_at1[A, B, C] = new At[(A, B, C), B, TSucc[TZero]] {
def apply(t: (A, B, C)): B = t._2
}
implicit def tuple3_at2[A, B, C] = new At[(A, B, C), C, TSucc[TSucc[TZero]]] {
def apply(t: (A, B, C)): C = t._3
}
}
object App {
implicit class TupleGet[T](t: T) {
def apply[A](n: Nat)(implicit at: At[T, A, n.N]): A = at(t)
}
def main(args: Array[String]) {
val t1 = ("1", 123)
val t2 = (false, 1234, "foobar")
println(t1.apply(0))
println(t2.apply(1))
println(t2.apply(2))
}
}
import scala.language.experimental.macros
import scala.language.implicitConversions
import scala.reflect.macros.whitebox.Context
object Macros {
def tupleApplyImpl(c: Context)(n: c.Tree): c.Tree = {
import c.universe._
val value = c.eval(c.Expr[Int](n))
assert(value >= 0)
val q"$_($tuple)" = c.prefix.tree
c.parse(s"${tuple.toString}._${value + 1}")
}
implicit class TupleOps[T](tuple: T) {
def apply(n: Int): Any = macro tupleApplyImpl
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment