Haskell - GHC for iOS : 既存のSwiftプロジェクトにHaskellで作った関数をさくっと組み込む

# この記事は
http://blog.euphonictech.com/entry/2015/03/30/235956からの転載です。

# この記事の目標は

ものすごく簡単な関数をHaskellで書いて、それをSwiftから呼び出せるようにしてみます。

今回呼び出す関数は

```haskell
plus10 :: Int -> IO Int
plus10 i = do
    putStrLn "Called!"
    return $ i + 10
```

です。なんもしない関数ですね。成功すればstdoutに"Called!"が出るはずです。

# まずはお決まりのghc-ios用xcodeプロジェクトの設定

### HaskelliOS.xcconfigをプロジェクトにコピーします。

https://github.com/ghc-ios/ghc-ios-scriptsに説明とともにあります。

### Info -> Configurations を両方HaskelliOSに変更

![20150126205627.png](https://qiita-image-store.s3.amazonaws.com/0/52015/1721e3ac-b56d-3649-2ea7-c57129855cd3.png)

### PROJECT -> Build Settings -> Architectures -> Build Active Architecture Onlyをすべて```NO```に

![20150126205617.png](https://qiita-image-store.s3.amazonaws.com/0/52015/7bb3211f-2cfa-bccf-26ce-c3b12a2a84ca.png)

## そして追加で libconv*.dylibを追加

libconvがないとリンカが通らないので適当にlibconv*.dylibをすべて追加しておきます。

![20150330232339.png](https://qiita-image-store.s3.amazonaws.com/0/52015/5b159b5c-c7ec-2521-4233-8b98baa6a00d.png)

![20150330232406.png](https://qiita-image-store.s3.amazonaws.com/0/52015/4b94be2a-a7ba-78f7-ad76-d687b45782d3.png)

## まずはghc-iosでバイナリとスタブヘッダを作ります。

ソースコードはFFIを使ってこんな感じに:

```haskell
{-# LANGUAGE ForeignFunctionInterface #-}
module Counter where
import Control.Monad

foreign export ccall plus10 :: Int -> IO Int

plus10 :: Int -> IO Int
plus10 i = do
    putStrLn "Called!"
    return $ i + 10
```

という感じで```plus10```を```foreign export ccall```しておきます。そうするとこの関数の呼び出しがスタブヘッダに出てきます。モジュール名は適当です。ghc-iosの例をそのまま改変したのでモジュール名が残っています。

HaskellプロジェクトはXcodeのプロジェクトルート直下にディレクトリを掘ってその中にIntelliJプロジェクトまるごと入れておくと全部簡単にGithubにプッシュできておすすめです。

これをビルドして```hoge.a```ファイルと```hoge_stub.h```を作ります。

```
$ ghc-ios src/*.hs
[1 of 1] Compiling Counter          ( src/Main.hs, build/arm/Counter.o )
Linking build/arm/Main.a ...
[1 of 1] Compiling Counter          ( src/Main.hs, build/i386/Counter.o )
Linking build/i386/Main.a ...
```

これで出来上がりです。

## ビルドしたものをXcodeにコピーします。

![20150330233151.png](https://qiita-image-store.s3.amazonaws.com/0/52015/2207d14c-ab2b-8d36-1cfa-4ee3460acac5.png)

## 使えるように```include```します。

普通にSwiftからは```import```できないのでブリッジヘッダに```import```をします。

ブリッジヘッダの名前は```<プロジェクト名>-Bridging-Header.h```とします。

![20150330233250.png](https://qiita-image-store.s3.amazonaws.com/0/52015/3fa955f7-70f4-c33d-3a07-9e5dbf77c8e1.png)

```swing
#ifndef FlatReversi_FlatReversi_Bridging_Header_h
#define FlatReversi_FlatReversi_Bridging_Header_h

#import "intrinsics.h"
#import "HsFFI.h"
#import "Counter_stub.h"

#endif
```

こんな感じで```HsFFI.h```と```Counter_stub.h```(ファイル名は環境に合わせてください)をインポートします。

HsFFI.hはHaskellシステムを初期化するコードの呼び出しに必要です。

# hs_initを呼び出してHaskellシステムを初期化する。

hs_initを呼び出すまではHaskellの関数を呼び出すことはできません。万が一呼び出すとそのままエラーメッセージとともに停止します。

呼び出す場所はなるべくアプリケーションの初期化場所に近いところがいいのですが、それほど初期化地点に拘る必要は今のところありません。最初のHaskell関数の呼び出し前であればどこでも構いません。なのでAppDelegateの中で呼び出すのがよいでしょう。

```swift
import UIKit

@asmname("hs_init")
    func hs_init(UnsafePointer<String>, UnsafePointer<Int>)

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?


    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // Override point for customization after application launch.
        hs_init(nil, nil);
        NSLog("hs_init initialized!");
        return true
    }
...
```

という感じで (1) hs_initをSwift内で使えるように宣言し、(2) 実際に呼び出しをします。

```@asmname```でC関数を使えるようにします。asmname()のカッコの中にはCヘッダ内の関数の名前を、その次の行にはその関数をどのようにSwift内にエクスポートするかを指定します。

hs_initはHsFFI.hでみてみると

```c
extern void hs_init     (int *argc, char **argv[]);
```

なので、第一引数は```UnsafeMutablePointer<int>```とします。第二引数は```UnsafeMutablePointer<String>```とします。constついていないのでMutableにします。

hs_init(nil, nil)で呼び出します。なのでMutableは（結局）どっちでも大丈夫です。なんやねん。

# 作った関数```plus10```を呼び出す。

同じ要領で上で書いた関数を呼び出してみます。再掲すると

```haskell
plus10 :: Int -> IO Int
plus10 i = do
    putStrLn "Called!"
    return $ i + 10
```

です。Swift風に言うと型は```Int -> Int```です。なので

![20150330235051.png](https://qiita-image-store.s3.amazonaws.com/0/52015/461374b7-5177-479d-a4df-e76ea0c28065.png)

```swift
import Foundation

@asmname("plus10")
    func plus10(Int) -> Int

class SearchEvalPlayer: ComputerPlayer {
```

とグローバルで関数を宣言します。

使う場所では普通の関数とかわりなく使えます(この例では返り値使ってないですね)。

![20150330235149.png](https://qiita-image-store.s3.amazonaws.com/0/52015/8a323d03-e0b4-9a0f-a285-994dad398af9.png)

```swift
    override func think() {
        NSLog("Start thinking")

        plus10(20)

        var retx = 0
        var rety = 0
...
```

と呼び出します。

![20150330235255.png](https://qiita-image-store.s3.amazonaws.com/0/52015/8fccfdcc-4450-de57-a7ca-5a1b70faf94e.png)

できた！