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

Haskellの値をRubyの値に変換したり、HaskellからRubyのメソッドを呼んだりする方法の紹介です。

# ライブラリ - hruby

[hruby](https://github.com/bartavelle/hruby)というライブラリを使います。

ライブラリ内部では`ruby.h`を使ってFFIでRubyを呼び出している感じです。（`ruby`コマンドを叩いたりしている感じではなさそうです）

# Haskellの`Int`をRubyの値にする

Haskellの`1`をRubyの値にする例です

```hs
import Foreign.Ruby
    
main :: IO ()
main = do
    ri <- startRubyInterpreter
    Right rubyOne <- toRuby ri (1 :: Int)
    print rubyOne
    closeRubyInterpreter ri
```

`ri`というのは`RubyInterpreter`型の変数です

## 出力

出力はこんな感じになりました

```
0x0000000000000003
```

# Rubyの値をHaskellの値にする

```hs
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`を使う方法の紹介です。

使い方は以下のような感じです。

```hs
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
```

<img width="652" alt="Screen Shot 2017-12-13 at 0.47.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/84451/768c8693-319f-8c03-6c22-e55410855bea.png">

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


# メソッドを呼ぶ

以下の例は、Rubyの`[5, -3, 93, -73, 2] * 3`をHaskell上でやる例です。

`mySafeMethodCall`というのは、自作関数でメソッドを呼ぶライブラリのメソッドを呼ぶ関数でうまく呼び出せなかったので、`Foreign.Ruby.Bindings`にある関数たちを組み合わせて作ったものです。


```hs
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