Mac OS X でScala入門ログ(+SWT入門)

はてな界隈で賑わっていたので、気になっている方も多いのでは?

Ruby からScalaに乗り換えた15くらいの理由 - ヽ( ・∀・)ノくまくまー

Mac OS XScalaに入門してみたので、その軌跡を簡単に書いてログを残しておきます。


※ 僕はJava関数型言語も特に使える訳ではないので、一部情報が間違っているかもしれません
※ あくまでも入門ログなので、プログラムの解説は得にありません
※ 環境は以下の通りです
OS:Mac OS X 10.6.3
シェル:zsh 4.3.9 (i386-apple-darwin10.0)
MacPorts1.8.1
Java
1.6.0_17
Java(TM) SE Runtime Environment (build 1.6.0_17-*b04-248-10M3025)
Java HotSpot(TM) 64-Bit Server VM (build 14.3-b01-101, mixed mode)

1. インストール

Scalaのウェブサイトからダウンロードすることも出来ますが、僕はMacPortsからインストールしました。お手軽です。
まだMacPortsを導入していない方はこちらをどうぞ

GUI世代のためのCUI入門以前 ー MacPorts導入方法


インストールはコマンド一発です。あとはrehashするだけ。

> sudo port install scala
> rehash

※ エラーが出た方もいるみたいです。こちらが参考になるかと思います。
Scalaのインストール - でぶろぐ


2. コマンドラインから遊ぶ

Scalaでも他の言語と同じように、コマンドラインで対話できます。

> scala
Welcome to Scala version 2.7.5.final (Java HotSpot(TM) Client VM, Java 1.6.0_17).
Type in expressions to have them evaluated.
Type :help for more information.

scala> 1 + 1
res0: Int = 2

3. ファイルにプログラムを書いて遊ぶ

もちろん、普段はファイルにプログラムを書くことになると思います。ファイルの拡張子は ".scala" が一般的なようです。
手順は、

1) プログラムを書く ⇒ scalaコマンドで直接実行する
2) プログラムを書く ⇒ scalacコマンドでコンパイルする(classファイルを生成する) ⇒ scalaコマンドで実行する

の2手順があります(他にもあるかも)。
それぞれの手順に合せてプログラムの内容もちょこっと変えなきゃいけないので、基本的には2の方法を取るといいと思います。

ちなみに "scala" コマンドの実態は実はシェルスクリプトで、"java" コマンドを呼んだり前処理をしていたりしていました(場所は "/opt/local/bin/scala" のはず)。

> touch HelloWorld.scala
> vim HelloWorld.scala
(なにか書く)
> scalac HelloWorld.scala
> scala HelloWorld
> Hello World
// HelloWorld.scala
object HelloWorld extends Application
{
    println("Hello! World")
}

生成されるclassファイルの名前はobjectの名前です。
ちなみにscalacコマンドを通さない場合、単に

println("Hello! World")

と書くこともできます。

4. swingを使った例

そもそもScalaを始めたのは、「CUIGUIもウェブもこなせる!マルチプラットフォーム!しかもScalaって超クール!」という言語入門オタク的発想だったので、とりあえずswingを使って簡単なGUIアプリケーションを作ってみました。プログラム自体は下のブログを完全に参考にさせていただいて、解析などをしていました。
Swingのサンプル - etc9
※ 一応プログラムコードを載せさせていただきますが、内容は参考ブログ記事と同様です

import scala.swing._
import scala.swing.event._
import javax.swing.UIManager

object SimpleGUIApplicationHello extends SimpleGUIApplication
{
    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName())

    def top = new MainFrame 
    {
        title = "Swing with scala";
        contents = new BorderPanel {
            import BorderPanel.Position._
            add(label, Center)
            add(button, South)
        }
    }

    val button = new Button("Click")
    object label extends Label("Count:-") 
    {
        var count = 0;
        listenTo(button)
        reactions += {
            case ButtonClicked(button) => 
                count += 1
                text = "Count:" + count
        }
    }
}

5. SWTを使った例

SWT(The Standard Widget Toolkit)はJava標準の一環として提供するJavaGUIツールキット(Standard Widget Toolkit - Wikipedia)だそうです。
簡単に言えばSwingよりきれいにウィジェット(ボタン、チェックボックス、ラベル のようなもの)を使うことができて、抽象度が高くて使い易いんだとか。
また、SWTを通してOSのAPIを叩くこともできる、なんてこともあるので、僕はなるべくならSWTを使おうと思っているところです。


長くなりましたが、SWTを使うためにはSWTを入手する必要があります。


SWT: The Standard Widget Toolkit


ここのページ中程にある "Releases > Development" の "Mac OS X" のリンクからダウンロードしました。
ダウンロードしたzipファイルを解凍して、中にある "swt-debug.jar" と "swt.jar" を、"/opt/local/share/scala/bin" に設置すると、プログラムコード内でimportしたときに自動的に読み込んでくれます。
上記のパスはMacPortsでインストールした場合なので、他の場合は異るはずです。


あと一点だけ。
64bit版のJava VMが動いている場合、標準の32bit版SWTJava VMがロード出来ないようでエラーが発生します。
これを解決するために、JAVA_OPTSを以下のように設定します。

> export JAVA_OPTS="-XstartOnFirstThread -d32"


重要なのは "-d32" というオプションで、32bit版のJava VMを指定するのだそうです。
ちなみに"-XstartOnFirstThread" はMac固有の不具合を避けるためのおまじないだそうです。直接関係はありません。たぶん。



あとは素人臭いソースコードを晒して終わりです。お付き合いありがとうございました。お役にたつことがあれば嬉しいです。では。

//
// SWTCounter.scala
// 
// 64bit版Macでは
// | export JAVA_OPTS="-XstartOnFirstThread -d32"
// 

import org.eclipse.swt.widgets._
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData
import org.eclipse.swt.layout.GridLayout

object SWTCounter extends Application
{
    var display = new Display
    var shell = new Shell(display)
    shell.setSize(200, 120)

    var glayout = new GridLayout(1, false)
    glayout.horizontalSpacing = 0
    glayout.verticalSpacing = 0
    shell.setLayout(glayout)

    var count = 0

    var label = new Label(shell, SWT.PUSH | SWT.CENTER)
    label.setText("Count:-")
    label.setLayoutData(new GridData(GridData.FILL_BOTH))

    var button = new Button(shell, SWT.PUSH)
    button.setText("click")
    button.setLayoutData(new GridData(GridData.FILL_BOTH))
    button.addListener(SWT.Selection, new Listener() {
        def handleEvent(e: Event)
        {
            count += 1
            label.setText("Count:" + count)
        }
    })


    // main loop
    shell.open

    while (!shell.isDisposed) {
        if (!display.readAndDispatch) {
            display.sleep
        }
    }

    display.dispose
    // end main loop
}