twitter-archive/jaqen

Name: jaqen

Owner: Twitter, Inc.

Owner: Twitter Archive

Description: A type-safe heterogenous Map or a Named field Tuple

Created: 2014-07-16 21:33:34.0

Updated: 2018-03-31 00:56:08.0

Pushed: 2014-11-08 02:27:37.0

Homepage:

Size: 500

Language: Scala

GitHub Committers

UserMost Recent Commit# Commits

Other Committers

UserEmailMost Recent Commit# Commits

README

Jaqen

A type-safe heterogenous Map or a Named field Tuple depending how you look at it.

“Speak the names and a man will do the rest”

CI

The build runs in Travis CI: Build Status

API:

https://github.com/twitter/jaqen/blob/6661c711b20cf16c242e21e9c7f38ed198841016/jaqen-ntuple/src/main/scala/com/twitter/jaqen/ntuple/NTuple.scala#L11

t NTuple[T <: NTuple[T]] {

*
 returns the field named 'key' with the proper type
 if key does not exist, a compilation error will occur
 @param key a literal or a Symbol
 example:
 <code>
 val tuple = t('a -> 1, 'b -> "2")
 tuple('a)
 => 1 (of type Int)
 tuple('b)
 => "2" (of type String)
 </code>
/
f get(key: Any) = macro applyImp[T]
* @see get */
f apply(key: Any) = macro applyImp[T]

*
 adds a pair key -> value to the tuple
 if key already exists, a compilation error will occur
 key must be a literal or a symbol.
 example: <code>
 val tuple = t('a -> 1, 'b -> 2)
 tuple + ('c -> 3)
 => t('a -> 1, 'b -> 2, 'c -> 3)
 </code>
/
f add(pair: (Any, Any)) = macro plusImpl[T]
* @see add */
f +(pair: (Any, Any)) = macro plusImpl[T]

*
 concats another NTuple to this one
 if a key is defined in both tuples a compilation error will occur
 <code>
 val tuple1 = t('a -> 1, 'b -> 2)
 val tuple2 = t('c -> 3, 'd -> 4)
 tuple1 ++ tuple2
 => t('a -> 1, 'b -> 2, 'c -> 3, 'd -> 4)
 </code>
/
f concat[T2 <: NTuple[T2]](t: T2) = macro plusplusImpl[T,T2]
* @see concat */
f ++[T2 <: NTuple[T2]](t: T2) = macro plusplusImpl[T,T2]

*
 removes a key from the tuple
 if key does not exist, a compilation error will occur
 <code>
 val tuple = t('a -> 1, 'b -> 2)
 tuple - 'a
 => t('b -> 2)
 </code>
/
f remove(key: Any) = macro minusImpl[T]
* @see remove */
f -(key: Any) = macro minusImpl[T]

*
 takes a key -> value pair and replaces the existing key with the given value
 if key does not exist, a compilation error will occur
 example:
 <code>
 val tuple = t('a -> 1, 'b -> 2)
 tuple -+ ('a -> 3)
 => t('a -> 3, 'b -> 2)
 </code>
/
f replace(pair: (Any, Any)) = macro replaceImpl[T]
* @see replace */
f -+(pair: (Any, Any)) = macro replaceImpl[T]

*
 prefixes all the key names with the given prefix.
 useful to concatenate 2 tuples
 example:
 <code>
 t('a -> 1, 'b -> 2).prefix("t")
 => t('ta -> 1, 'tb -> 2)
 </code>
/
f prefix(prefix: String) = macro prefixImpl[T]

*
 takes a pair (inputs -> output) and a function
 inputs: a tuple of the keys of the values to pass to the function
 output: the key to set with the result
 @returns the resulting tuple with the output key set with the result of the function
 example:
 <code>
 val tuple = t('a -> 1, 'b -> 2)
 tuple.map(('a, 'b) -> 'c) { (a: Int, b: Int) => a + b }
 => t('a -> 1, 'b -> 2, 'c -> 3)
 </code>
/
f map(pair: Any)(f: Any) = macro mapImpl[T]

*
 @returns a string representation of this tuple
 example:
 <code>
 t('a -> 1, 'b -> 2).mkString
 (a -> 1, b -> 2)
 </code>
/
f mkString = macro mkStringImpl[T]

*
 converts this tuple to a Map.
 @returns an immutable Map
/
f toMap = macro toMapImpl[T]


ct NTuple {

*
 creates a new NTuple from a list of key -> value pairs
 the types of the values are preserved and will be returned accordingly when apply is called
 if a key is defined twice a compilation error will occur
 <code>
 val tuple1 = t('a -> 1, 'b -> "2")
 </code>
/
f t(pairs: Any*) = macro newTupleImpl

plicit def nTupleToString[T <: NTuple[T]](ntuple: T): String = macro nTupleToStringImpl[T]

plicit def listOfNTupleToRichList[T <: NTuple[T]](list: List[T]) = RichList[T](list)

try it:
clean compile
a -classpath jaqen-ntuple/target/classes

Examples:

rt com.twitter.jaqen.ntuple.NTuple._

foo = "FOO"
bar = 3

aps
map = Map("a" -> foo, "b" -> bar)
+ ("d" -> 3f)
ma: String = map("a")
mb: Int = map("b")
"c")
- "a"

uples
tuple1 = (foo, bar)

tuple1a: String = tuple1._1
tuple1b: Int = tuple1._2
r
(tuple1a, tuple1b) = tuple1

uple with named fields!
t1 = t('a -> foo, 'b -> bar)
kString
t1a: String = t1('a)
t1b: Int = t1('b)
c) // error: t1 does not contain key: "c"
- 'b).mkString
t2 = t1 + ('c -> 2)
 ('c -> 3) // error: t2 already contains key c
-+ ('c -> 3)).mkString
lso a tuple
1
2
(t1a, t1b) = t1.toTuple
oMap

empty = t()
notempty = empty + ("A" -> foo)
mpty("A")

 class Person(val name: String, val age: Int)
input = List(Person("John", 10), Person("Jack", 5))

 class PersonBirthYear(val name: String, val birthYear: Int)
t.map((in) => PersonBirthYear(in.name, 2014 - in.age)).filter(_.birthYear > 2005).map(_.name)

t.map((in) => (in.name, 2014 - in.age)).filter{ case (_, birthYear) => birthYear > 2005 }.map { case (name, _) => name }

t.map((in) => t('name -> in.name, 'birthYear -> (2014 - in.age))).filter(_('birthYear) > 2005).map(_('name))

Result:

a> com.twitter.jaqen.ntuple.NTuple._
rt com.twitter.jaqen.ntuple.NTuple._

a> 

a> val foo = "FOO"
 String = FOO

a> val bar = 3
 Int = 3

a> 

a> // maps

a> val map = Map("a" -> foo, "b" -> bar)
 scala.collection.immutable.Map[String,Any] = Map(a -> FOO, b -> 3)

a> map + ("d" -> 3f)
: scala.collection.immutable.Map[String,Any] = Map(a -> FOO, b -> 3, d -> 3.0)

a> val ma: String = map("a")
sole>:13: error: type mismatch;
nd   : Any
uired: String
   val ma: String = map("a")
                       ^

a> val mb: Int = map("b")
sole>:13: error: type mismatch;
nd   : Any
uired: Int
   val mb: Int = map("b")
                    ^

a> map("c")
.util.NoSuchElementException: key not found: c
at scala.collection.MapLike$class.default(MapLike.scala:228)
at scala.collection.AbstractMap.default(Map.scala:58)
at scala.collection.MapLike$class.apply(MapLike.scala:141)
at scala.collection.AbstractMap.apply(Map.scala:58)
at .<init>(<console>:14)
at .<clinit>(<console>)
at .<init>(<console>:7)
at .<clinit>(<console>)
at $print(<console>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:734)
at scala.tools.nsc.interpreter.IMain$Request.loadAndRun(IMain.scala:983)
at scala.tools.nsc.interpreter.IMain.loadAndRunReq$1(IMain.scala:573)
at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:604)
at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:568)
at scala.tools.nsc.interpreter.ILoop.reallyInterpret$1(ILoop.scala:756)
at scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:801)
at scala.tools.nsc.interpreter.ILoop.command(ILoop.scala:713)
at scala.tools.nsc.interpreter.ILoop.processLine$1(ILoop.scala:577)
at scala.tools.nsc.interpreter.ILoop.innerLoop$1(ILoop.scala:584)
at scala.tools.nsc.interpreter.ILoop.loop(ILoop.scala:587)
at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply$mcZ$sp(ILoop.scala:878)
at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply(ILoop.scala:833)
at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply(ILoop.scala:833)
at scala.tools.nsc.util.ScalaClassLoader$.savingContextLoader(ScalaClassLoader.scala:135)
at scala.tools.nsc.interpreter.ILoop.process(ILoop.scala:833)
at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:83)
at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:96)
at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:105)
at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)


a> map - "a"
: scala.collection.immutable.Map[String,Any] = Map(b -> 3)

a> 

a> // tuples

a> val tuple1 = (foo, bar)
e1: (String, Int) = (FOO,3)

a> 

a> val tuple1a: String = tuple1._1
e1a: String = FOO

a> val tuple1b: Int = tuple1._2
e1b: Int = 3

a> // or

a> val (tuple1a, tuple1b) = tuple1
e1a: String = FOO
e1b: Int = 3

a>  
 | // tuple with named fields!

a> val t1 = t('a -> foo, 'b -> bar)
com.twitter.jaqen.ntuple.NTuple2[String("a"),String,String("b"),Int] = (FOO,3)

a> t1.mkString
: String = (a -> FOO, b -> 3)

a> val t1a: String = t1('a)
 String = FOO

a> val t1b: Int = t1('b)
 Int = 3

a> t1('c) // error: t1 does not contain key: "c"
sole>:14: error: t1 does not contain key c
          t1('c) // error: t1 does not contain key: "c"
            ^

a> (t1 - 'b).mkString
: String = (a -> FOO)

a> val t2 = t1 + ('c -> 2)
com.twitter.jaqen.ntuple.NTuple3[String("a"),String,String("b"),Int,String("c"),Int(2)] = (FOO,3,2)

a> t2 + ('c -> 3) // error: t2 already contains key c
sole>:15: error: t2 already contains key c
          t2 + ('c -> 3) // error: t2 already contains key c
             ^

a> (t2 -+ ('c -> 3)).mkString
: String = (a -> FOO, b -> 3, c -> 3)

a> // also a tuple

a> t1._1
: String = FOO

a> t1._2
0: Int = 3

a> val (t1a, t1b) = t1.toTuple
 String = FOO
 Int = 3

a> t1.toMap
1: scala.collection.immutable.Map[Any,Any] = Map(a -> FOO, b -> 3)

a> 

a> val empty = t()
y: ntuple.NTuple0 = ()

a> val notempty = empty + ("A" -> foo)
mpty: com.twitter.jaqen.ntuple.NTuple1[String("A"),String] = FOO

a> notempty("A")
2: String = FOO

a> 

a> case class Person(val name: String, val age: Int)
ned class Person

a> val input = List(Person("John", 10), Person("Jack", 5))
t: List[Person] = List(Person(John,10), Person(Jack,5))

a> 

a> case class PersonBirthYear(val name: String, val birthYear: Int)
ned class PersonBirthYear

a> input.map((in) => PersonBirthYear(in.name, 2014 - in.age)).filter(_.birthYear > 2005).map(_.name)
3: List[String] = List(Jack)

a> 

a> input.map((in) => (in.name, 2014 - in.age)).filter{ case (_, birthYear) => birthYear > 2005 }.map { case (name, _) => name }
4: List[String] = List(Jack)

a> 

a> input.map((in) => t('name -> in.name, 'birthYear -> (2014 - in.age))).filter(_('birthYear) > 2005).map(_('name))
5: List[String] = List(Jack)

This work is supported by the National Institutes of Health's National Center for Advancing Translational Sciences, Grant Number U24TR002306. This work is solely the responsibility of the creators and does not necessarily represent the official views of the National Institutes of Health.