JVM用の純粋関数型言語 Frege で Applicative Functor を使用

Frege は Haskell によく似た JVM 用の純粋関数型プログラム言語です。

なかなか面白そうな言語だったので、関数を Applicative として使うサンプル (書籍「すごいHaskellたのしく学ぼう! 」より) を試してみました。

ソースは http://github.com/fits/try_samples/tree/master/blog/20130810/

Frege の使い方

Frege を使うには、まず http://code.google.com/p/frege/downloads/list から JAR ファイル (例 frege3.21.107-g4bd09eb.jar) をダウンロードしてきます。

カレントディレクトリに fregec.jar というファイル名で保存した場合、下記のようにすればコンパイル処理を実行できます。 (-d で出力先を指定しなければ、カレントディレクトリに Java ソース・クラスファイルが出力されます)

コンパイル
> java -jar fregec.jar [オプション] <ソースファイル>

実行は、下記のように fregec.jar をクラスパスに追加してコンパイル処理で出力された Java クラスを実行するだけです。

実行 (Windows の場合)
> java -cp .;fregec.jar <クラス名>

単純な処理の実行

それでは 3 * 5 の結果を出力するだけの単純なプログラム(下記)をコンパイル・実行してみます。

func_sample.fr
package sample.FuncSample where

main args = do
    let f = (*3)
    putStrLn $ show $ f 5

次のような点が Haskell と異なります。

  • package <クラス名> where が必要
  • main に引数(上記の args)が必要

ちなみに、package で指定したクラス名が Java ソース化される際のクラス名・パッケージ名に使われます。

下記のようにコンパイルすると sample ディレクトリへ FuncSample.java や FuncSample.class 等が出力されます。

コンパイル
> java -jar func_sample.fr
calling: javac -cp fregec.jar;. -d . -encoding UTF-8 ./sample/FuncSample.java
runtime 4.58 wallclock seconds.

実行結果はこのようになります。

実行結果
> java -cp .;fregec.jar sample.FuncSample
15
runtime 0.068 wallclock seconds.

関数を Functor として使う

次は、関数を Functor として使ってみます。

Haskell による実装

まず Haskell で実装してみました。 +100 した後 *3 する処理に 1 を与えた結果を出力します。

functor_sample.hs (Haskell 版)
main = do
    let f = fmap (*3) (+100)
    putStrLn $ show $ f 1
    print $ f 1
実行結果 (Haskell 版)
> runghc functor_sample.hs
303
303

Frege による実装

それでは本題の Frege による実装です。

Frege には今のところ、関数 "(->) r" に対する Functor のインスタンス宣言が無いようなので自前で行う必要がありました。(見落としているだけかもしれませんが)

インスタンス宣言の仕方は Haskell と同じです。

なお、Haskell の print 関数は改行しますが、Frege の print 関数は改行しないので代わりに println 関数を使います。

functor_sample.fr (Frege 版)
package sample.FunctorSample where

instance Functor ((->) r) where
    fmap = (.)

main args = do
    let f = fmap (*3) (+100)
    putStrLn $ show $ f 1
    println $ f 1
コンパイルと実行結果 (Frege 版)
> java -jar fregec.jar functor_sample.fr
・・・
> java -cp .;fregec.jar sample.FunctorSample
303
303
runtime 0.072 wallclock seconds.

ちなみに、"instance Functor ((->) r)・・・" を定義しなかった場合、コンパイル時に下記のようなエラーが出る事になります。

E functor_sample.fr:6: -> Int is not an instance of Functor

関数を Applicative として使う

最後に関数を Applicative として使ってみます。

Haskell による実装

Haskell 版は下記の通りです。 *3 した結果と +10 した結果を合計する処理に 4 を渡して出力します。( (3 * 4) + (10 + 4) = 26 )

applicative_sample.hs (Haskell 版)
import Control.Applicative

main = do
    let f = (+) <$> (*3) <*> (+10)
    print $ f 4
実行結果 (Haskell 版)
> runghc applicative_sample.hs
26

Frege による実装

それでは Frege 版です。 Functor と同様に関数 "(->) r" に対する Applicative のインスタンス宣言も自前で行う必要があるようです。

applicative_sample.fr (Frege 版)
package sample.ApplicativeSample where

instance Functor ((->) r) where
    fmap = (.)

instance Applicative ((->) r) where
    return x = (\_ -> x)
    f <*> g = \x -> f x (g x)

main args = do
    let f = (+) <$> (*3) <*> (+10)
    println $ f 4
コンパイルと実行結果 (Frege 版)
> java -jar fregec.jar applicative_sample.fr
・・・
> java -cp .;fregec.jar sample.ApplicativeSample
26
runtime 0.083 wallclock seconds.