seratch's weblog in Japanese

About Scala, Java and Ruby programming in Japaense. If you need English information, go to http://blog.seratch.net/

Scalaのいろんな機能を使ったHello World

Scalaの主要な機能を使いながらHello Worldサンプルを書いてみたものです。
なお、以下の例に出てくるApplicationトレイトは2.9.0.final以降でdeprecatedになりました。新しいAppトレイトを使用して下さい。

記事

  • Singletonオブジェクトのmain関数を実行する
object HelloWorldByMainMethod {
  def main(args: Array[String]) = println("Hello World!")
}
// Hello World!
  • Applicationトレイトを使ってmain関数を省略する
object HelloWorldByApplication extends Application {
  println("Hello World!")
}
// Hello World!
  • メソッドを定義して使う
object HelloWorldWithMethod extends Application {
  def hello(str: String) = println("Hello " + str + "!")
  hello("World")
  def helloWorld(str: String = "World") = println("Hello " + str + "!")
  helloWorld()
  helloWorld("WORLD")
}
// Hello World!
// Hello World!
// Hello WORLD!
  • 関数を使う

helloFunc1は関数リテラル、helloFunc2はsayHelloToを部分適用した関数です。

object HelloWorldWithFunction extends Application {
  val helloFunc1: (String) => Unit = {
    str => println("Hello " + str + "!")
  }
  helloFunc1("World")
  helloFunc1.apply("World")
  def sayHelloTo(str: String) = println("Hello " + str + "!")
  val helloFunc2 = sayHelloTo _
  helloFunc2("World")
  helloFunc2.apply("World")
}
// Hello World!
// Hello World!
// Hello World!
// Hello World!
  • カリー化された関数を部分適用して使う
object HelloWorldWithPartiallyAppliedFunction extends Application {
  def displayCurrying(hello: String)(world: String) = {
    println(hello + " " + world + "!")
  }
  val displayHello = displayCurrying("Hello") _
  displayHello("World")
}
// Hello World!
  • call-by-name(名前渡し)パラメータの関数を使う

名前渡しすると引数として渡したときには評価(実行)されず、渡された関数の中で呼び出されたときにはじめて評価されます。
下の例で1と2は同じ挙動ですが、定義の仕方や呼び出し方は微妙に異なります。

object HelloWorldWithCallByName extends Application {
  def message(): String = {
    print("Hello ")
    "World!"
  }

  def displayHelloWorld1(message: => String) = {
    print("*** ")
    print(message)
    println(" ***")
  }
  displayHelloWorld1(message())

  def displayHelloWorld2(message: () => String) = {
    print("*** ")
    print(message())
    println(" ***")
  }
  val messageFunc = message _
  displayHelloWorld2(messageFunc)
}
// *** Hello World! ***
// *** Hello World! ***
  • lazy val(遅延評価値)を使う
object HelloWorldWithLazyVal extends Application {
  lazy val lazyWorld = "Hello World! (" + new Date + ")";
  println("Waiting...   (" + new Date + ")")
  Thread.sleep(2000L)
  println(lazyWorld)
}
// Waiting...   (Tue Feb 22 00:41:53 JST 2011)
// Hello World! (Tue Feb 22 00:41:55 JST 2011)
  • クロージャを使う
object HelloWorldWithClosure extends Application {
  val prefix = "Hello "
  var suffix = "!"
  val helloClosure: (String) => Unit = {
    str => println(prefix + str + suffix)
  }
  helloClosure("World")
  suffix = "!!!"
  helloClosure("World")
}
// Hello World!
// Hello World!!!
  • classを使う
object HelloWorldWithClass extends Application {
  class HelloWorld {
    def display() = println("Hello World!")
  }
  val hw = new HelloWorld
  hw.display()
}
// Hello World!
  • case classを使う
object HelloWorldWithCaseClass extends Application {
  val English = "English"
  val Japanese = "Japanese"
  case class HelloWorldIn(lang: String)
  def printHelloWorld(hw: HelloWorldIn) = {
    hw match {
      case HelloWorldIn(English) => println("Hello World!")
      case HelloWorldIn(Japanese) => println("こんにちは世界!")
      case _ => throw new IllegalArgumentException("unknown case class : " + hw)
    }
  }
  printHelloWorld(HelloWorldIn(English))
  printHelloWorld(HelloWorldIn(Japanese))
}
// Hello World!
// こんにちは世界!
  • traitを使ってSingletonオブジェクトにmix-inする
trait HelloWorldTrait {
  def display() = println("Hello World!")
}
object HelloWorldWithSingletonObjectMixiIn extends HelloWorldTrait {
  def main(args: Array[String]) = display
}
// Hello World!
  • traitを使ってclassににmix-inする
trait Printer {
  def p(str: String) = println(str)
}
trait Message {
  val helloWorld = "Hello World!"
}
class MixInPrinter extends Printer with Message {
  def display() = p(helloWorld)
}
object HelloWorldWithMixIn extends Application {
  val printer = new MixInPrinter
  printer.display()
}
// Hello World!

self typeによってSelfTypeAnnotationPrinterの内部はPrinterやMessageがmix-inされた状態となります。self typeの変数名には慣習的にthisやselfが用いられますが、実際には任意の変数名で動作します。

trait Printer {
  def p(str: String) = println(str)
}
trait Message {
  val helloWorld = "Hello World!"
}
class SelfTypeAnnotationPrinter { this: Printer with Message =>
  def display() = p(helloWorld)
}

SelfTypeAnnotationPrinterは、インスタンス化する際にコンパイラによってPrinterやMessageのmix-inが強制されます。

object HelloWorldWithSelfTypeAnnotation extends Application {
  val printer = new SelfTypeAnnotationPrinter with Printer with Message
  printer.display()
}
// Hello World!
  • Listを扱う
object HelloWorldWithList extends Application {
  val charList = List("H", "e", "l", "l", "o", " ", "W", "o", "r", "l", "d", "!", "\n")
  // foreach
  charList foreach (print)
  // generator
  for (c <- charList) print(c)
  // recursive call
  def recursiveDisplay(list: List[String]): Unit = {
    list match {
      case h :: Nil => print(h)
      case h :: t => {
        print(h)
        recursiveDisplay(t)
      }
      case _ =>
    }
  }
  recursiveDisplay(charList)
}
// Hello World!
// Hello World!
// Hello World!
  • implicit parameters(暗黙の関数パラメータ)を使う

同一スコープ内でその型(以下の例ではWorld)の値がimplicit var/valで定義されている場合、そのimplicit引数は暗黙に設定させる事ができます。2つ目のように省略せず指定すれば指定したものが優先されます。

object HelloWorldWithImplicitParameters extends Application {
  object HelloWorld {
    def display(hello: String)(implicit world: World) = println(hello + " " + world.name + "!")
  }
  case class World(val name:String)
  implicit val WORLD_VAL = World("Scala World")

  HelloWorld.display("Hello")
  HelloWorld.display("Hello")(World("Groovy World"))
}
// Hello Scala World!
// Hello Groovy World!
  • implicit conversions(暗黙の型変換)を使う

implicit defで定義した関数の引数型で戻り値の型で定義されているメソッドが呼べるようになります。

object HelloWorldWithImplicitConversions extends Application {
  case class Printer(val value: String) {
    def display() = println(value)
  }
  implicit def toPrinter(str: String): Printer = Printer(str)
  "Hello World!".display()
}
// Hello World!
  • XMLを読み込む

この記事内では円マークとして表示されてしまっていますが、実際にはバックスラッシュです。

object HelloWorldWithXML extends Application {
  val xml: Elem =
    <items>
      <item>Hello World!</item>
    </items>
  val node: NodeSeq = xml \ "item"
  println(node.text)
}
// Hello World!
  • Actorを使う

2つのActorを起動しているサンプルです。無限ループにした場合はJVMを停止しないと起動しっぱなしになるので注意です。

object HelloWorldWithActor extends Application {
  import actors.Actor
  class HelloActor extends Actor {
    override def act() = {
      loop {
        react {
          case (str: String, wait: Long) => {
            Thread.sleep(wait)
            println("Hello " + str + "!")
          }
          case _ => println("unknown message")
        }
      }
    }
  }
  val hello1 = new HelloActor
  val hello2 = new HelloActor
  hello1.start()
  hello2.start()
  hello1 ! ("World", 200L)
  hello2 ! ("WORLD1", 100L)
  hello2 ! ("WORLD2", 300L)
  Thread.sleep(2000L)
  System.exit(0)
}
// Hello WORLD1!
// Hello World!
// Hello WORLD2!