Last active
August 31, 2017 15:37
-
-
Save edofic/7f0673b07726cb2783b21af67f788ca6 to your computer and use it in GitHub Desktop.
Tuple apply method in scala
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
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)) | |
} | |
} |
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 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)) | |
} | |
} |
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 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)) | |
} | |
} |
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 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)) | |
} | |
} |
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
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)) | |
} | |
} |
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
// 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)) | |
} | |
} |
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 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