Scala

Implicit Value ClassとUniversal Trait

More than 5 years have passed since last update.

書こうと思っていたネタが事前の調査不足でボツってしまったのでちょっと使いまわしでいきます。

Scalaz Advent Calendarの2日目で、Scala 2.10の新機能であるimplicit class/value classを使いましたが本筋と関係なかったのでそれには触れませんでした。

上記リンク中のHatenaOpsTwitterOpsというのがそれですが、この2つのクラスはPrintableというトレイトをmixinしています。そのPrintableを見ると、trait Printable extends Anyという見慣れないことをしていると思います。

これは"Universal Trait"というもので、value class(AnyValを継承するクラス)にトレイトをmixinしたい場合はUniversal Traitである必要があります。詳しくはこちらを参照してください。

リンクにあるようにPrintableprintlnメソッドを呼ぶと、value classであろうと新しいインスタンスが生成されてしまいます。

それはしょうがないんですが、ではPrintableで抽象メンバを定義してHatenaOpsTwitterOpsで実装したshowsの方はどうなるのか気になったので、バイトコードを覗いてみました。

まず、見やすくなるようにコードを興味のある部分だけに絞ります。

比較用にただのvalue classのコードも入れました。

object s3 {

trait Printable extends Any {
def shows: String
def println: Unit = Console.println(shows)
}

implicit class TwitterOps(val id: String) extends AnyVal with Printable {
def shows: String = "@" + id
}

class StringOps(val str: String) extends AnyVal {
def hello: String = s"Hello, $str!"
}

def main(args: Array[String]) {
"kxbmap".println
"kxbmap".shows
new StringOps("kxbmap").hello
}
}

そしてmainの中身だけバイトコードを持ってきたのが以下。

   L0

LINENUMBER 19 L0
NEW advent/scalaz2012/day2/s3$TwitterOps
DUP
ALOAD 0
LDC "kxbmap"
INVOKEVIRTUAL advent/scalaz2012/day2/s3$.TwitterOps (Ljava/lang/String;)Ljava/lang/String;
INVOKESPECIAL advent/scalaz2012/day2/s3$TwitterOps.<init> (Ljava/lang/String;)V
INVOKEINTERFACE advent/scalaz2012/day2/s3$Printable.println ()V

GETSTATIC advent/scalaz2012/day2/s3$TwitterOps$.MODULE$ : Ladvent/scalaz2012/day2/s3$TwitterOps$;
L1
LINENUMBER 20 L1
ALOAD 0
LDC "kxbmap"
INVOKEVIRTUAL advent/scalaz2012/day2/s3$.TwitterOps (Ljava/lang/String;)Ljava/lang/String;
INVOKEVIRTUAL advent/scalaz2012/day2/s3$TwitterOps$.shows$extension (Ljava/lang/String;)Ljava/lang/String;
POP

GETSTATIC advent/scalaz2012/day2/s3$StringOps$.MODULE$ : Ladvent/scalaz2012/day2/s3$StringOps$;
L2
LINENUMBER 21 L2
LDC "kxbmap"
INVOKEVIRTUAL advent/scalaz2012/day2/s3$StringOps$.hello$extension (Ljava/lang/String;)Ljava/lang/String;
POP

普段あんまりバイトコードを覗いたりしていないので詳しく解説はできないですが、printlnTwitterOpsのインスタンスを生成しているのに対して、showsはちゃんとvalue classっぽい動きをしてくれるようです。


まとめ

ネタは事前にチェックしましょう。