macrogen
macrogen copied to clipboard
Experimentation of scala macro paradise for utility macros.
macrogen
Work in progress
Experimentation of scala macro paradise for utility macros.
Any feedback or comment is welcome.
Tuple
macrogen provides collection like methods on tuples (head, tail, reverse, ::, :+, toList, map, reduceLeft, reduceRight, foldLeft, foldRight).
In addition, some utility methods are provided for numeric computation: |+|, |-|, |*|, |/|.
The operate method allows applying a given function on the tuple.
Lambda expressions, polymorphic or overloaded functions can be passed to operate, map, reduceLeft, reduceRight, foldLeft and foldRight.
Example REPL session.
scala> import macrogen.TupleImplicit._
import macrogen.TupleImplicit._
scala> val tup = (1, "two") :+ 3.0
tup: (Int, String, Double) = (1,two,3.0)
scala> val tup = (1, 2.0) :+ 3f
tup: (Int, Double, Float) = (1,2.0,3.0)
scala> tup.operate(_ * _ + _)
res1: Double = 5.0
scala> tup.reduceLeft(_ + _)
res2: Double = 6.0
scala> tup.map(_ + 1)
res3: (Int, Double, Float) = (2,3.0,4.0)
scala> tup.reverse
res4: (Float, Double, Int) = (3.0,2.0,1)
Limitation: still some issues with chaining operations when beginning with append
TraitOf
The TraitOf generic trait allows building a trait from an implementation class. It will export any public val, var or def into inheriting trait. Get rid of boilerplate.
e.g. from test specs
import macrogen.TraitOf._
class AImpl {
def f(i: Int): Int = i + 42
val x = 81
protected val xx = 32
var y = 2
type X = Int
class B
trait C
object o { val i = 77 }
}
// types, object, class traits must be declared explicitly
trait ATrait extends TraitOf[AImpl] {
type X <: Int
}
class A extends AImpl with ATrait
val a: ATrait = new A
a.f(1) must beEqualTo(43)
a.x must beEqualTo(81)
a.y must beEqualTo(2)
a.y = 55
a.y must beEqualTo(55)
Function Cached Macro
For (pure) function memoization, you can use the cached macro just before the function definition, as show after.
scala> import macrogen.cached
import macrogen.cached
scala> import macrogen.Util._
import Util._
scala> def fac(i: BigInt): BigInt = if(i<2) 1 else i*fac(i-1)
fac: (i: BigInt)BigInt
scala> def facCached(i: BigInt): BigInt = cached { if(i < 2) 1 else i * facCached(i - 1) }
facCached: (i: BigInt)BigInt
scala> time { (1 to 10000).foreach(_ => fac(200)) }
471409 microseconds
scala> time { (1 to 10000).foreach(_ => facCached(200)) }
14720 microseconds
SelfType
When mixing the SelfType trait, a type Self <: the inheriting trait is automatically declared.
TypeOf
This type macro gets the type of an expression and allows using it as a type.
e.g.
scala> import macrogen.TypeOf._
import macrogen.TypeOf._
scala> trait A { val i: Int; val j: TypeOf(i) }
defined trait A
scala> implicitly[TypeOf(Array(42)) =:= Array[Int]]
res0: =:=[Array[Int],Array[Int]] = <function1>
Type Operators
Type operators complete usual type relationship testing, such as =:=, <:<, >:>, <%< with
their negation =:!=, <:!<, >:!>, <%!<.
It also brings way to combine those thanks to !:! (type negation), &:& and |:|.
Example REPL session.
scala> import macrogen.TypeOperators._
import macrogen.TypeOperators._
scala> trait A
defined trait A
scala> trait B
defined trait B
scala> implicitly[A =:!= B]
res0: macrogen.TypeOperators.=:!=[A,B] = $anon$1@1106d26c
scala> type A_alias = A
defined type alias A_alias
scala> implicitly[A =:!= A_alias] // oops
<console>:13: error: could not find implicit value for parameter e: macrogen.TypeOperators.=:!=[A,A_alias]
implicitly[A =:!= A_alias] // oops
Constructor[T]
With Constructor type macro, you can instantiate a generic type in a natural way, without resorting to
(runtime) reflection.
Many ran into the following kind of issue.
scala> def f[T](/* ... */) = /* ... */ new T /* ... */
<console>:10: error: class type required but T found
def f[T](/* ... */) = /* ... */ new T /* ... */
^
Constructor helps solving this kind of issues. Here is an example REPL session.
scala> import macrogen.Constructor._
import macrogen.Constructor._
scala> class A
defined class A
scala> def make[T](implicit ctor: Constructor[T]): T = ctor.make
make: [T](implicit ctor: macrogen.Constructor.Constructor[T])T
scala> make[A]
res0: A = A@28dd6524
scala> trait T
defined trait T
scala> make[A with T]
res1: A with T = $anon$1$anon1$1@20ccd7c
scala> class B(x: Int)
defined class B
scala> def make[T](x: Int)(implicit ctor: Constructor[Int => T]): T = ctor.make(x)
make: [T](x: Int)(implicit ctor: macrogen.Constructor.Constructor[Int => T])T
scala> make[B](42)
res2: B = B@103dcc33
scala> make[B with T](42)
res3: B with T = $anon$1$$anonfun$make$1$anon1$1@b17a1f2