を書いた。
#サンプル
以下のように関数を定義すると
import x7c1.salad.parameter.Agglomerator.define
def show = define { (id: Int, name: String) =>
s"id:$id, name:$name"
}
二通りの方法で引数を渡すことができるようになる。
一つはいつもどおりの形。
show(123, "foo")//id:123, name:foo
もう一つは構造的部分型を利用した形。
case class User(id: Int, name: String, nick: String)
val user = User(
id = 123,
name = "foo",
nick = "bar"
)
show(user)//id:123, name:foo
これで委譲するためだけの同じような関数を書く手間は不要になった。
##補足
引数の名前と型さえ一致していれば任意のオブジェクトを渡すことができる。
case class Address(id: Int, name: String, postal: String)
val address = Address(
id = 123,
name = "foo",
postal = "bar"
)
println(show(address))// id:123, name:foo
もちろん引数を満たしていなければコンパイル時に検出される。
case class Anonym(id: Int)
val anon = Anonym(id = 123)
println(show(anon))
//type mismatch;
//[error] required: Object{def id: Int; def name: String}
//[error] println(show(anon))
//[error] ^
//[error] one error found
#利用方法
lazy val `your-app` = project.
dependsOn(ProjectRef(uri("git://github.com/x7c1/Salad.git#0.4.0"), "salad-lib"))
- リポジトリの場所 : Salad
- ビルド定義のサンプル : sample-taupe/project
#中身
object Agglomerator {
def define[A](f: A): Any = macro AgglomeratorImpl.define[A]
}
private object AgglomeratorImpl {
def define[A: c.WeakTypeTag](c: whitebox.Context)(f: c.Expr[A]) = {
import c.universe._
val args = f.tree.children.filter(_.isDef)
val names = args.map(_.symbol.asTerm.name)
val fields = args.map{ x =>
q"def ${x.symbol.asTerm.name}: ${x.symbol.typeSignature}"
}
val A = TypeName(c.freshName())
val arg = TermName(c.freshName())
val access: TermName => Tree = key => q"$arg.$key"
q"""
new {
type $A = { ..$fields }
def apply($arg: $A) = $f(..${names map access})
def apply(..$args) = $f(..$names)
}
"""
}
}
#参考 : パラメータオブジェクトについて
#参考 : Scala のマクロについて