Work in progress
Experimentation of scala macro paradise for utility macros.
Any feedback or comment is welcome.
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
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)
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
When mixing the SelfType trait, a type Self <:
the inheriting trait is automatically declared.
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 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
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