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 の 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.