Ruby
Haskell

HaskellからRubyを使う - メソッド呼び出し

More than 1 year has passed since last update.

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を使う

startRubyInterpretercloseRubyInterpreterを見ると、精神的によくないので、ローンパターンな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

Screen Shot 2017-12-13 at 0.47.36.png

なので、今のところRubyInterpretermainで生成して、他の関数には引数に渡して対処しています。

メソッドを呼ぶ

以下の例は、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の使い方を理解するために使ったリポジトリがある場所です。

https://github.com/nwtgck/hruby-prac-haskell