Haskellの値をRubyの値に変換したり、HaskellからRubyのメソッドを呼んだりする方法の紹介です。
ライブラリ - hruby
hrubyというライブラリを使います。
ライブラリ内部ではruby.h
を使ってFFIでRubyを呼び出している感じです。(ruby
コマンドを叩いたりしている感じではなさそうです)
HaskellのInt
をRubyの値にする
Haskellの1
をRubyの値にする例です
import Foreign.Ruby
main :: IO ()
main = do
ri <- startRubyInterpreter
Right rubyOne <- toRuby ri (1 :: Int)
print rubyOne
closeRubyInterpreter ri
ri
というのはRubyInterpreter
型の変数です
出力
出力はこんな感じになりました
0x0000000000000003
Rubyの値をHaskellの値にする
import Foreign.Ruby
main :: IO ()
main = do
ri <- startRubyInterpreter
-- Haskell => Ruby
Right rubyOne <- toRuby ri (1 :: Int)
print rubyOne
-- Ruby => Haskell
Right haskellOne <- fromRuby ri rubyOne :: IO (Either RubyError Int)
putStr "Ruby => Haskell:"
print haskellOne
closeRubyInterpreter ri
注意
:: IO (Either RubyError Int)
がないとHaskellの何型に変換したらいいのわからないため必須です。
出力
出力はこんな感じになります
0x0000000000000003
Ruby => Haskell:1
withRubyInterpreter
を使う
startRubyInterpreter
とcloseRubyInterpreter
を見ると、精神的によくないので、ローンパターンなwithRubyInterpreter
を使う方法の紹介です。
使い方は以下のような感じです。
import Foreign.Ruby
main :: IO ()
main = do
withRubyInterpreter $ \ri -> do
Right rubyOne <- toRuby ri (1 :: Int)
print rubyOne
object allocation during garbage collection phase (未解決)
以下のようにRubyInterpreter
を複数回作成すると、「ruby: [BUG] object allocation during garbage collection phase」というエラーが出て落ちます。なので関数ごとに内部にwithRubyInterpreter
を使って、それらを複数回呼ぶと落ちてしまうので、一つのRubyInterpreter
しか作れない感じです。
import Foreign.Ruby
main :: IO ()
main = do
withRubyInterpreter $ \ri -> do
Right rubyOne <- toRuby ri (1 :: Int)
print rubyOne
withRubyInterpreter $ \ri -> do
Right rubyOne <- toRuby ri (1 :: Int)
print rubyOne
なので、今のところRubyInterpreter
をmain
で生成して、他の関数には引数に渡して対処しています。
メソッドを呼ぶ
以下の例は、Rubyの[5, -3, 93, -73, 2] * 3
をHaskell上でやる例です。
mySafeMethodCall
というのは、自作関数でメソッドを呼ぶライブラリのメソッドを呼ぶ関数でうまく呼び出せなかったので、Foreign.Ruby.Bindings
にある関数たちを組み合わせて作ったものです。
import Foreign.Ruby
import Foreign.Ruby.Bindings
mySafeMethodCall :: RubyInterpreter
-> RValue
-> String
-> [RValue]
-> IO (Either RubyError RValue)
mySafeMethodCall ri reciever methodName args = makeSafe ri $ do
rid <- rb_intern methodName
rb_funcall reciever rid args
main :: IO ()
main = do
withRubyInterpreter $ \ri -> do
-- Haskell => Ruby
Right rubyArr <- toRuby ri ([5, -3, 93, -73, 2] :: [Int])
Right rubyThree <- toRuby ri (3 :: Int)
-- call Array#*
Right rubyTimesed <- mySafeMethodCall ri rubyArr "*" [rubyThree]
putStr "rubyArr * 3: "
Right haskellTimesedArr <- fromRuby ri rubyTimesed :: IO (Either RubyError [Int])
print haskellTimesedArr
"*"
というのはメソッド名です。Rubyでは演算子もメソッドなので、これでOKです。演算子に限らず、メソッド名をかけば動きます
出力
正しく出力できていることがわかります
rubyArr * 3: [5,-3,93,-73,2,5,-3,93,-73,2,5,-3,93,-73,2]
GitHubリポジトリ
hrubyの使い方を理解するために使ったリポジトリがある場所です。