假设我们要编写一个定义具有某些类型成员或方法的匿名类的宏,然后创建该类的实例,该类通过这些方法静态地键入为结构类型等.这可以通过2.10中的宏系统实现. 0,类型成员部分非常容易:
object MacroExample extends ReflectionUtils {
import scala.language.experimental.macros
import scala.reflect.macros.Context
def foo(name: String): Any = macro foo_impl
def foo_impl(c: Context)(name: c.Expr[String]) = {
import c.universe._
val Literal(Constant(lit: String)) = name.tree
val anon = newTypeName(c.fresh)
c.Expr(Block(
ClassDef(
Modifiers(Flag.FINAL), anon, Nil, Template(
Nil, emptyValDef, List(
constructor(c.universe),
TypeDef(Modifiers(), newTypeName(lit), Nil, TypeTree(typeOf[Int]))
)
)
),
Apply(Select(New(Ident(anon)), nme.CONSTRUCTOR), Nil)
))
}
}
Run Code Online (Sandbox Code Playgroud)
(提供我方法ReflectionUtils的便利特性在哪里constructor.)
这个宏允许我们将匿名类的类型成员的名称指定为字符串文字:
scala> MacroExample.foo("T")
res0: AnyRef{type T = Int} = $1$$1@7da533f6
Run Code Online (Sandbox Code Playgroud)
请注意,它是适当的键入.我们可以确认一切都按预期工作:
scala> implicitly[res0.T =:= Int] …Run Code Online (Sandbox Code Playgroud) 我将从一个例子开始.这里的List.fill元组与Scala 2.10中的宏相当:
import scala.language.experimental.macros
import scala.reflect.macros.Context
object TupleExample {
def fill[A](arity: Int)(a: A): Product = macro fill_impl[A]
def fill_impl[A](c: Context)(arity: c.Expr[Int])(a: c.Expr[A]) = {
import c.universe._
arity.tree match {
case Literal(Constant(n: Int)) if n < 23 => c.Expr(
Apply(
Select(Ident("Tuple" + n.toString), "apply"),
List.fill(n)(a.tree)
)
)
case _ => c.abort(
c.enclosingPosition,
"Desired arity must be a compile-time constant less than 23!"
)
}
}
}
Run Code Online (Sandbox Code Playgroud)
我们可以使用以下方法:
scala> TupleExample.fill(3)("hello")
res0: (String, String, String) = (hello,hello,hello)
Run Code Online (Sandbox Code Playgroud)
这个家伙在几个方面都是一只奇怪的鸟.首先,arity参数必须是一个文字整数,因为我们需要在编译时使用它.在Scala的早期版本中,(根据我所知),方法甚至无法判断其中一个参数是否是编译时文字.
其次, …
假设我有一个类型类,证明Shapeless副产品中的所有类型都是单例类型:
import shapeless._
trait AllSingletons[A, C <: Coproduct] {
def values: List[A]
}
object AllSingletons {
implicit def cnilSingletons[A]: AllSingletons[A, CNil] =
new AllSingletons[A, CNil] {
def values = Nil
}
implicit def coproductSingletons[A, H <: A, T <: Coproduct](implicit
tsc: AllSingletons[A, T],
witness: Witness.Aux[H]
): AllSingletons[A, H :+: T] =
new AllSingletons[A, H :+: T] {
def values = witness.value :: tsc.values
}
}
Run Code Online (Sandbox Code Playgroud)
我们可以证明它适用于简单的ADT:
sealed trait Foo
case object Bar extends Foo
case object Baz extends Foo
Run Code Online (Sandbox Code Playgroud)
然后: …
我正在将David Blei的潜在Dirichlet分配的原始C实现移植到Haskell,我正在尝试决定是否在C中留下一些低级别的东西.以下函数是一个例子 - 它是一个近似值的二阶导数lgamma:
double trigamma(double x)
{
double p;
int i;
x=x+6;
p=1/(x*x);
p=(((((0.075757575757576*p-0.033333333333333)*p+0.0238095238095238)
*p-0.033333333333333)*p+0.166666666666667)*p+1)/x+0.5*p;
for (i=0; i<6 ;i++)
{
x=x-1;
p=1/(x*x)+p;
}
return(p);
}
Run Code Online (Sandbox Code Playgroud)
我把它翻译成或多或少惯用的Haskell如下:
trigamma :: Double -> Double
trigamma x = snd $ last $ take 7 $ iterate next (x' - 1, p')
where
x' = x + 6
p = 1 / x' ^ 2
p' = p / 2 + c / x'
c = foldr1 (\a b -> …Run Code Online (Sandbox Code Playgroud) 我在许多项目中使用Scalaz 7的迭代,主要用于处理大型文件.我想开始切换到Scalaz 流,这些流旨在取代iteratee包(坦率地说它缺少很多部分并且使用起来很麻烦).
Streams基于机器(iteratee理念的另一种变体),它也已在Haskell中实现.我已经使用了Haskell机器库,但机器和流之间的关系并不是完全明显的(至少对我来说),并且流库的文档仍然有点稀疏.
这个问题是关于一个简单的解析任务,我希望看到使用流而不是迭代来实现.如果没有其他人能打败我,我会自己回答这个问题,但我确信我不是唯一一个正在(甚至考虑)这种转变的人,因为无论如何我需要完成这项工作,想我不妨在公共场合做这件事.
假设我有一个包含已被标记化并用词性标记的句子的文件:
no UH
, ,
it PRP
was VBD
n't RB
monday NNP
. .
the DT
equity NN
market NN
was VBD
illiquid JJ
. .
Run Code Online (Sandbox Code Playgroud)
每行有一个标记,单词和词性由单个空格分隔,空白行表示句子边界.我想解析这个文件并返回一个句子列表,我们也可以将它们表示为字符串元组的列表:
List((no,UH), (,,,), (it,PRP), (was,VBD), (n't,RB), (monday,NNP), (.,.))
List((the,DT), (equity,NN), (market,NN), (was,VBD), (illiquid,JJ), (.,.)
Run Code Online (Sandbox Code Playgroud)
像往常一样,如果我们遇到无效输入或文件读取异常,我们希望优雅地失败,我们不想担心手动关闭资源等.
首先是一些常规文件读取的东西(它应该是iteratee包的一部分,它目前不提供远程高级别的任何东西):
import java.io.{ BufferedReader, File, FileReader }
import scalaz._, Scalaz._, effect.IO
import iteratee.{ Iteratee => I, _ …Run Code Online (Sandbox Code Playgroud) 当我使用支持类型级编程的库时,我经常会发现自己编写如下的注释(来自Paul Snively在Strange Loop 2012中提供的示例):
// But these invalid sequences don't compile:
// isValid(_3 :: _1 :: _5 :: _8 :: _8 :: _2 :: _8 :: _6 :: _5 :: HNil)
// isValid(_3 :: _4 :: _5 :: _8 :: _8 :: _2 :: _8 :: _6 :: HNil)
Run Code Online (Sandbox Code Playgroud)
/**
* If we wanted to confirm that the list uniquely contains `Foo` or any
* subtype of `Foo`, we could …Run Code Online (Sandbox Code Playgroud) 所以我有这个宏:
import language.experimental.macros
import scala.reflect.macros.Context
class Foo
class Bar extends Foo { def launchMissiles = "launching" }
object FooExample {
def foo: Foo = macro foo_impl
def foo_impl(c: Context): c.Expr[Foo] =
c.Expr[Foo](c.universe.reify(new Bar).tree)
}
Run Code Online (Sandbox Code Playgroud)
我已经说了三次我想要foo返回一个Foo,但我可以做以下(在2.10.0-RC3中):
scala> FooExample.foo
res0: Bar = Bar@4118f8dd
scala> res0.launchMissiles
res1: String = launching
Run Code Online (Sandbox Code Playgroud)
如果我删除任何一个上的类型参数,也会发生同样的事情c.Expr.如果我真的想确保无论谁打电话foo都看不到他们正在获得Bar,我必须在树本身添加类型归属.
这实际上非常棒 - 例如,我可以将宏指向某种类型的模式,并Vocabulary使用表示词汇表中的术语的成员方法创建某个类的匿名子类,这些将在返回的对象上可用.
我想知道我到底在做什么,所以我有几个问题.首先,该foo方法实际上的返回类型是什么?它是否仅适用于(可选)文档?它明确地约束了返回类型(例如,Int在这种情况下我无法将其更改为),如果我将其完全删除,则会出现如下错误:
scala> FooExample.foo
<console>:8: error: type mismatch;
found : Bar
required: Nothing
FooExample.foo …Run Code Online (Sandbox Code Playgroud) Scala Future(在2.10和现在的2.9.3中是新的)是一个应用程序的函子,这意味着如果我们有一个可遍历的类型 F,我们可以使用F[A]一个函数A => Future[B]并将它们变成一个函数Future[F[B]].
此操作在标准库中可用Future.traverse.如果我们从库中导入applicative functor实例,Scalaz 7还提供了更通用的功能.traverseFuturescalaz-contrib
这两种traverse方法在流的情况下表现不同.标准库遍历在返回之前使用流,而Scalaz会立即返回未来:
import scala.concurrent._
import ExecutionContext.Implicits.global
// Hangs.
val standardRes = Future.traverse(Stream.from(1))(future(_))
// Returns immediately.
val scalazRes = Stream.from(1).traverse(future(_))
Run Code Online (Sandbox Code Playgroud)
还有另一个不同之处,正如Leif Warner 在此观察到的那样.标准库traverse立即启动所有异步操作,而Scalaz启动第一个,等待它完成,启动第二个,等待它,依此类推.
通过编写一个将为流中的第一个值休眠几秒钟的函数来显示第二个差异非常容易:
def howLong(i: Int) = if (i == 1) 10000 else 0
import scalaz._, Scalaz._ …Run Code Online (Sandbox Code Playgroud) 我正在用Scala宏替换Java程序中的一些代码生成组件,并且运行Java虚拟机对单个方法生成的字节代码大小的限制(64千字节).
例如,假设我们有一个大型的XML文件,它表示我们想要在程序中使用的从整数到整数的映射.我们希望避免在运行时解析此文件,因此我们将编写一个宏,它将在编译时进行解析并使用该文件的内容来创建方法的主体:
import scala.language.experimental.macros
import scala.reflect.macros.Context
object BigMethod {
// For this simplified example we'll just make some data up.
val mapping = List.tabulate(7000)(i => (i, i + 1))
def lookup(i: Int): Int = macro lookup_impl
def lookup_impl(c: Context)(i: c.Expr[Int]): c.Expr[Int] = {
import c.universe._
val switch = reify(new scala.annotation.switch).tree
val cases = mapping map {
case (k, v) => CaseDef(c.literal(k).tree, EmptyTree, c.literal(v).tree)
}
c.Expr(Match(Annotated(switch, i.tree), cases))
}
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,编译的方法将超过大小限制,但不是一个很好的错误说,我们给了一个巨大的堆栈跟踪与大量的调用,TreePrinter.printSeq并被告知我们已经杀死了编译器.
我有一个解决方案,涉及将案例拆分为固定大小的组,为每个组创建一个单独的方法,并添加一个顶级匹配,将输入值调度到适当的组的方法.它可以工作,但它很不愉快,而且我不想每次编写宏时都不必使用这种方法,其中生成的代码的大小取决于某些外部资源.
有没有更清洁的方法来解决这个问题?更重要的是,有没有办法更优雅地处理这种编译器错误?我不喜欢库用户得到一个难以理解的"该条目似乎已经杀死编译器"错误消息的想法只是因为宏正在处理的某些XML文件已超过一些(相当低的)大小阈值.
这个问题与我之前的问题的动机相似(虽然它是关于我在不同背景下遇到的问题).
在没有quasiquotes的情况下,我可以非常轻松地在函数文字上进行模式匹配:
import scala.reflect.macros.Context
import scala.language.experimental.macros
object QQExample {
def funcDemo(f: Int => String) = macro funcDemo_impl
def funcDemo_impl(c: Context)(f: c.Expr[Int => String]) = {
import c.universe._
f.tree match {
case Function(ps, body) => List(ps, body) foreach println
case _ => c.abort(
c.enclosingPosition,
"Must provide a function literal."
)
}
c.literalUnit
}
}
Run Code Online (Sandbox Code Playgroud)
其工作方式如下:
scala> QQExample.funcDemo((a: Int) => a.toString)
List(val a: Int = _)
a.toString()
Run Code Online (Sandbox Code Playgroud)
现在假设我想使用quasiquotes更灵活地进行相同类型的匹配.以下内容也将匹配该功能,并打印出我们期望的内容.
case q"($x: $t) => $body" => List(x, t, body) foreach …Run Code Online (Sandbox Code Playgroud) scala ×9
macros ×5
scala-macros ×5
scala-2.10 ×3
scalaz ×2
shapeless ×2
types ×2
applicative ×1
c ×1
concurrency ×1
future ×1
haskell ×1
implicits ×1
iterate ×1
java ×1
jvm ×1
math ×1
performance ×1
testing ×1