MacOSX+.NET Core SDK+VS CodeでF#開発

Microsoftが.NET Coreをオープンソース化するというニュースは衝撃的でしたが、その後の進捗がどうなっているかについてはイマイチ知られていない気がします。

.NETのランタイムであるところの.NET Coreは先日1.1がリリースされました。しかし多くの人にとっては.NET Core単体ではあまり使い道がないため、通常はコマンドラインツールやビルド環境を含む「.NET Core SDK」をインストールして使うことになるはずです。.NET Core SDK自体はまだ正式リリースされていないのですが、徐々にこなれてきたという印象です。

さらにVisual Studio Codeと組み合わせて使えばコード補完や型推論結果の表示など昨今の統合開発環境と同等のサポートが受けられますので、素のテキストエディタでコードを書くよりは断然良い環境だと言えます。ようやく非Windows環境でも現代的な.NET開発ができそうでワクワクしますね（たぶんXamarinなら元々リッチな環境で開発できてたとか、筆者の知らないこともあるんだろうと思いますが…）。

最初にお断りしておくと、筆者は.NETスーパー初心者かつWindows環境に久しく触っていないという経歴です。初心者目線の記事ということでお楽しみください。

# 本稿の元ネタ

今回、Build 2016の1セッションの動画「[Getting Started with F# on .NET Core](https://channel9.msdn.com/Events/Build/2016/T661)」をなぞってみたので、その内容を紹介します。
![fsharp-on-dotnetcore.jpg](https://qiita-image-store.s3.amazonaws.com/0/46856/d21f5efc-62ad-5513-82bf-2b51407d0e1c.jpeg)

Microsoftのエンジニアが林檎マークのマシンでGitHubにアクセスしていても昨今では驚きが無かったりして、時代だなーという気持ちになります。

ちなみに、この動画が撮られた頃に比べると各ソフトウェアのバージョンが上がっており、動画と実際とで異なっている部分もありますのでご注意ください。本稿執筆時の各ソフトウェアのバージョンは下記の通りです。

- .NET Core SDK preview2-1-003177
- VS Code 1.7.2
- Ionide-fsharp 2.10.1

# F#とは

F#はMicrosoftが開発している関数型プログラミング言語で、OCamlの兄弟のような言語です。Haskellよりは副作用を直接的に扱い、Lisp系言語よりはカッコが少ないので、パッと見で取っつきやすい言語かと思います。

# .NET Core SDKインストール

まず　https://www.microsoft.com/net/core#macos から.NET Core SDKのpkgファイルをダウンロード、インストールします。

ただし、OpenSSLの1.0系をHomebrewでインストールしておく必要があります。手動で`/usr/local/lib/`以下にシンボリックリンクを作ってね、という注意書きもあるのですが、これは.NET Coreのインストーラが勝手にやってくれるようです。

# F#でHello World

上記インストール手順後に別のシェルを起動すると`dotnet`コマンドが実行できるようになっています。この`dotnet`コマンドは開発に必要な作業を隠蔽して色々やってくれる便利なコマンドです。おそらく内部的にはyeomanやNuGetを呼んでいると思います。

下記の通り、ひな形ファイルを作り、ライブラリをダウンロードし、ビルドして実行するところまで全て`dotnet`コマンドがやってくれます。

```
$ mkdir /tmp/hello
$ cd /tmp/hello
$ dotnet new --lang F#
Welcome to .NET Core!
（略）
Decompressing 100% 3701 ms
Expanding 100% 14725 ms
Created new F# project in /private/tmp/hello.
$ ls
Program.fs  project.json
$ dotnet restore
（略）
log  : Writing lock file to disk. Path: /private/tmp/hello/project.lock.json
log  : /private/tmp/hello/project.json
log  : Restore completed in 33226ms.
```

本来であればここで`dotnet run`で動くはずなのですが、困ったことに`dotnet-compile-fsc`の設定ファイル中に.NET Coreのバージョンが1.0.0決め打ちで書いてあるせいで期待通りに動きません。以下のように手動で調整してやれば動きます（これが対処として正しいのかよくわかっていません…）。

```
$ cd $HOME/.nuget/packages/dotnet-compile-fsc/1.0.0-preview2-020000/lib/netcoreapp1.0
$ cp dotnet-compile-fsc.runtimeconfig.json dotnet-compile-fsc.runtimeconfig.json.old
$ vi dotnet-compile-fsc.runtimeconfig.json
$ diff dotnet-compile-fsc.runtimeconfig.json.old dotnet-compile-fsc.runtimeconfig.json
$ diff -c dotnet-compile-fsc.runtimeconfig.json.old dotnet-compile-fsc.runtimeconfig.json
*** dotnet-compile-fsc.runtimeconfig.json.old	2016-12-05 01:12:59.000000000 +0900
--- dotnet-compile-fsc.runtimeconfig.json	2016-11-23 20:38:00.000000000 +0900
***************
*** 2,8 ****
    "runtimeOptions": {
      "framework": {
        "name": "Microsoft.NETCore.App",
!       "version": "1.0.0"
      }
    }
  }
--- 2,8 ----
    "runtimeOptions": {
      "framework": {
        "name": "Microsoft.NETCore.App",
!       "version": "1.1.0"
      }
    }
  }
```

これでコンパイルが通るようになります。

```
$ dotnet run
Project hello (.NETCoreApp,Version=v1.1) will be compiled because expected outputs are missing
Compiling hello for .NETCoreApp,Version=v1.1

Compilation succeeded.
    0 Warning(s)
    0 Error(s)

Time elapsed 00:00:08.1475129


Hello World!
$
```

やった！F#初心者だけどHello Worldできちゃった！

# Visual Studio Codeにionide-fsharp拡張をインストール

さて、補完やツールチップ表示つきの環境での開発も経験してみましょう。

まず下記ページからVisual Studio Codeをインストールします。

- https://code.visualstudio.com/

起動後、拡張機能のタブから「Ionide-fsharp」を検索、インストールします。インストール後に再起動ダイアログが出るので、再起動するとコード補完など統合開発環境っぽい機能が使えるようになります。

これらの機能のうち、型推論の結果を勝手に表示してくれたりマウスオーバーで見せてくれたりするのは本当に素晴らしいと感じます。筆者がEmacsでHaskellを書いていて一番困るのが型に関するエラーを入れ込んだときにどこでミスしたかわかりにくいことなんですが、コードを書きながら型推論結果がドンドン出てくればミスしたタイミングで気づけそうで、ちょっとしたパラダイムシフトなのでは？と感じました。F#プログラマの人たちは普段これ以上に便利な環境で仕事しているのだとしたら少しうらやましいですね。

![tooltip2.png](https://qiita-image-store.s3.amazonaws.com/0/46856/c839f66e-3f9c-c886-fc23-7e080bbc74ff.png)

このように、letで定義した関数`f`,`g`,`h`の型推論結果`int -> int`が各行の上に表示されています（自分でプログラム中に書いたわけではありません）。また、変数にマウスオーバーすれば型情報がツールチップで表示されます。

注意点ですが、Ionide-fsharpの動作にはMonoが必要です。ちょっとずっこける感じですが仕方ないというものでしょう。

```
$ brew install mono
```

でMonoをインストールしてから遊びましょう。

# コードを書いてみる (1) 別モジュールの呼び出し

ここからは動画を見ながら実際にコードを打ち込んだ方がいいと思います。以下では動画中盤（9:45）のLib.fsを動かす部分を紹介します。

先ほどの雛形ディレクトリで、下記のファイルを新規作成します。

```fsharp:Lib.fs
namespace Hello

module Lib=
    let f x = x * x
    let g x = x * 42
    let h x = 1 - x
```

```fsharp:Program.fs
// Learn more about F# at http://fsharp.org
namespace Hello
open System

module Program =
    [<EntryPoint>]
    let main argv =
        printfn "Hello World!"

        argv
        |> Array.map int
        |> Array.map Lib.f
        |> Array.iter (printfn "%i")

        0 // return an integer exit code
```

上のコードのままだとionide-fsharpが「もっとカッコいい書き方があるよ！」などと言ってきますが無視しましょう。

さらに`project.json`の`compileFiles`に`Lib.fs`を追加します。

```json:project.json
{
(snip)
    "compileFiles": [
        "Lib.fs",
        "Program.fs"
    ],
(snip)
}
```

では実行してみましょう。

```
$ dotnet run 1 2 3 4 5
Project hello (.NETCoreApp,Version=v1.1) will be compiled because expected outputs are missing
Compiling hello for .NETCoreApp,Version=v1.1

Compilation succeeded.
    0 Warning(s)
    0 Error(s)

Time elapsed 00:00:09.7112710


Hello World!
1
4
9
16
25
```

無事にビルド＆実行できました。

# コードを書いてみる (2) *.fsxスクリプトの実行

次に同じディレクトリに下記ファイルを新規作成します。

```fsharp:Script.fsx
#load "Lib.fs"
open Hello

let x = Lib.f 2
let y = Lib.g 4
let z = Lib.h 42
```

ここで上記内容を全選択した上でOption+EnterをタイプするとVS Code内からプログラムが実行され、実行結果が表示されます。

![fsi.png](https://qiita-image-store.s3.amazonaws.com/0/46856/c3bf1654-a6d6-a0eb-c348-f93a12b2b3a4.png)


これを実行しているのはMonoということで再びずっこける感じですね。現時点の.NET Coreで*.fsxが動かせるのかどうかはわかりませんでした。

# 感想など

本稿は単なる「やってみた」記事なんですけど、仲間が少ないと情報も少なくて厳しいなぁというのが所感です。本当はBuild 2016直後に公開しようと思っていたんですが、stable版の.NET Core SDKでF#が動かない状況が続いていたため公開できずにいました。本稿で紹介した通り、いまだにだうまく動かない状況が続いており、現時点でのベストプラクティスがわからなかったりします。はやくSDKの正式リリース版が来てほしいですね。