Frege 上で Java クラスを使用する
前回、Frege で Functor や Applicative を試しましたが、今回は Frege のソース内で Java クラスを使用してみました。
サンプルソースは http://github.com/fits/try_samples/tree/master/blog/20130901_3/
はじめに
一部の Java クラスは初めから Frege のソース内で使用できるようになっていますが、それ以外の Java クラスを使用するには、下記のような data 宣言を行う必要があります。(厳密な定義は Frege の仕様書をご覧ください)
data <データタイプ名> <型変数・・・> = [mutable | pure] native <Javaタイプ名> where [pure] native <関数名> [<Javaメソッド名など>] :: <型シグネチャ・・・> ・・・
補足事項は下記。
- Immutable(不変)クラスや副作用の無いメソッドに対しては pure native を指定
- pure native では無いメソッド(オブジェクトの状態を変化させたりするメソッド等)に対しては IO や ST モナドを返すようにする
- Java インスタンスメソッドを使う場合は第一引数を自身のデータタイプとする
- "関数名" と "Javaメソッド名" が等しい場合は "Javaメソッド名" を省略可能
Immutable(不変)クラスの場合
まずは Java の Immutable クラスを使用する場合です。
java.math.BigDecimal クラスを題材に、下記のような処理を Frege で実装してみました。(ちなみに java.math.BigInteger の方は Integer として Frege 内で使えます)
- (1) 文字列を引数に取るコンストラクタ
- (2) toString メソッド
- (3) add メソッド
- (4) add メソッド(
+
関数として使用) - (5) multiply メソッド(
*
関数として使用)
bigdecimal_sample.fr
package sample.BigDecimalSample where data BigDecimal = pure native java.math.BigDecimal where -- (1) pure native new :: String -> BigDecimal -- (2) pure native toString :: BigDecimal -> String -- (3) pure native add :: BigDecimal -> BigDecimal -> BigDecimal -- (4) pure native (+) add :: BigDecimal -> BigDecimal -> BigDecimal -- (5) pure native (*) multiply :: BigDecimal -> BigDecimal -> BigDecimal main args = do let num1 = BigDecimal.new "100" let num2 = BigDecimal.new "50" putStrLn $ (num1.add num2).toString putStrLn $ (num1.+ num2).toString putStrLn $ (num1.* num2).toString
実行結果
> java -cp .;fregec.jar sample.BigDecimalSample 150 150 5000 runtime ・・・
Num の型インスタンス
上記処理で num1.+ num2
では無く num1 + num2
とするには BigDecimal を Num の型インスタンス宣言してやる必要があります。
今回、Num の型インスタンス宣言を行うには下記のような関数定義が必要でした。
- (+)
- (-)
- (*)
- one
- zero
- fromInt
- hashCode
- <=>
bigdecimal_sample2.fr (Num 型インスタンス宣言版)
package sample.BigDecimalSample2 where data BigDecimal = pure native java.math.BigDecimal where pure native new :: String -> BigDecimal pure native toString :: BigDecimal -> String pure native zero java.math.BigDecimal.ZERO :: BigDecimal pure native one java.math.BigDecimal.ONE :: BigDecimal pure native (+) add :: BigDecimal -> BigDecimal -> BigDecimal pure native (-) subtract :: BigDecimal -> BigDecimal -> BigDecimal pure native (*) multiply :: BigDecimal -> BigDecimal -> BigDecimal pure native hashCode :: BigDecimal -> Int pure native compareTo :: BigDecimal -> BigDecimal -> Int instance Ord BigDecimal where a <=> b = (a.compareTo b).<=> 0 instance Num BigDecimal where pure native fromInt java.math.BigDecimal.valueOf :: Int -> BigDecimal main args = do let num1 = BigDecimal.new "100" let num2 = BigDecimal.new "50" putStrLn $ (num1 + num2).toString putStrLn $ (num1 * num2).toString putStrLn $ (num1 - num2).toString
実行結果
> java -cp .;fregec.jar sample.BigDecimalSample2 150 5000 50 runtime ・・・
Mutable(可変)クラスの場合
次は Java の Mutable クラスを使用する場合です。
題材として良さそうなクラスが思い浮かばなかったので、とりあえず java.awt.Point を使ってみました。
Mutable クラスの場合は基本的に pure を付けない native な関数を定義して IO や ST モナドを返すようにします。
IO モナド版
まずは IO モナド版です。
new や toString 等で IO <データタイプ> を返すようにします。(今回は IO Point)
Java のインスタンスメソッドに対する関数(toString や move 等)の第一引数は IO モナドでは無く普通の値(今回の場合は Point)となります。
とりあえず下記のような処理を実装してみました。
- (1) new で x=10, y=20 へ設定
- (2) move で x=20, y=30 へ移動
- (3) translate で x を +5、y を +3
point_sample.fr (IOモナド版)
package sample.PointSample where data Point = mutable native java.awt.Point where native new :: Int -> Int -> IO Point native toString :: Point -> IO String native move :: Point -> Int -> Int -> IO () native translate :: Point -> Int -> Int -> IO () main args = do -- (1) p <- Point.new 10 20 -- (2) p.move 20 30 -- (3) p.translate 5 3 p.toString >>= putStrLn
実行結果
> java -cp .;fregec.jar sample.PointSample java.awt.Point[x=25,y=33] ・・・
ST モナド版
次に ST モナド版です。
new が ST s (Mutable s Point) を返し、toString 等の第一引数が Mutable s Point となっている点が IO モナド版との違いです。
point_sample2.fr (STモナド版)
package sample.PointSample2 where data Point = mutable native java.awt.Point where native new :: Int -> Int -> ST s (Mutable s Point) native toString :: Mutable s Point -> ST s String native move :: Mutable s Point -> Int -> Int -> ST s () native translate :: Mutable s Point -> Int -> Int -> ST s () sample :: Mutable s Point -> ST s String sample p = do p.move 20 30 p.translate 5 3 p.toString main args = do Point.new 10 20 >>= sample >>= putStrLn
実行結果
> java -cp .;fregec.jar sample.PointSample2 java.awt.Point[x=25,y=33] ・・・
補足
move や translate 等のオブジェクトの状態を変化させるようなメソッドを一切使わないのであれば java.awt.Point を pure native とする事も可能です。
point_sample3.fr
package sample.PointSample3 where data Point = pure native java.awt.Point where pure native new :: Int -> Int -> Point pure native toString :: Point -> String main args = do let p = Point.new 10 20 putStrLn $ p.toString