F# を知ってほしい

## なんでこんなのを書いてるのか

F# を布教[^evangelism]~~するたびに誤解を解いたりどこらへんが良いのか列挙したりするの疲れたし, URL だけ投げつければ済むように~~したいからです.

[^evangelism]: ~~ヴァーチャル F# エヴァンジェリストになりたいので雇ってください. 藍沢家という前例があるので~~

## F# とは, なぜ F# なのか

F# は書いていて非常に楽しく, かつ何でもできてしまうので, 趣味のプログラミングのお供には非常に最適な言語です. またバグの少ないプログラムを書くための機能が充実していると同時に, コード量が少なく済みメンテナンスしやすい設計になっているので, 実行速度の速さと相まって, [金融系企業や研究機関を中心に, 機械学習研究者やモバイルアプリ開発者などにも高く評価されています](https://fsharp.org/testimonials/).

### 1. 色々な言語のいいとこ取り

F# は OCaml という言語をベースに開発された[^ocaml]プログラミング言語で, 

[^ocaml]: OCaml にあって F# にないものはファンクタとGADTが挙げられます. ファンクタはインターフェースや型クラス的なものがあるためにほとんど必要ないですが, GADT は代替になるものがないので少し困ることもあります.

* [C# の速度・クロスプラットフォーム性・ライブラリの多さ・開発環境](#1-クロスプラットフォーム)
* [Rust のツールチェイン](#3-ツール類が-dotnet-コマンドに集約されていて-ほとんどの操作がこれだけで完結する)
* [Go のデプロイしやすさ](#2-高速な動作-簡単な配布)
* [Python のオフサイドルール](#オフサイドルール)
* [Haskell のモナド文化](#computation-expressions)
* [静的ダックタイピング(事実上のトレイト/型クラス)](#inline-関数-statically-resolved-type-parameters)
* [コンパイル時型生成(type providers)](#type-providers)

といった要素を兼ね備えています. 構文が非常に軽量で, 型推論がよく効くため型アノテーションをほとんど書かなくていいのが特徴です.

例えば Hello, world! はこんな感じです:

```fsharp
printf "Hello, world!"
```

### 2. だいたい全部 F# でできる

[fsharp.org](https://fsharp.org/) のガイドを見ていただければわかるとおり, F# では非常に多くのことをすることができます.

* [Web 開発](https://fsharp.org/guides/web/#suave): [Suave](http://suave.io/) や [Giraffe](https://github.com/giraffe-fsharp/Giraffe), [Saturn](https://saturnframework.org/) などのライブラリを使ってバックエンドを書いて, F# to JS コンパイラである [Fable](http://fable.io/) でフロントエンドを書けます[^safe]. また [Blazor](https://docs.microsoft.com/ja-jp/aspnet/core/blazor/?view=aspnetcore-3.0) の F# 用サポートライブラリである [Bolero](https://fsbolero.io/) を使えば, F# を WebAssembly にコンパイルして Web アプリを作ることもできてしまいます[^bolero].
* [データサイエンス](https://fsharp.org/guides/data-science/): Microsoft がホストしている [Azure Notebook](https://notebooks.azure.com/) ではデフォルトで F# を補完付きで使うことができます[^ifsharp]. 数値計算用ライブラリ [Math.NET Numerics](https://numerics.mathdotnet.com/) や自動微分ライブラリ [DiffSharp](http://diffsharp.github.io/DiffSharp/) などが用意されており, [XPlot](https://fslab.org/XPlot/) を使って結果を可視化できます.
* [モバイルアプリ開発](https://fsharp.org/guides/apps-and-games): [Xamarin](https://docs.microsoft.com/en-us/xamarin/cross-platform/platform/fsharp/samples) は当然使うことができますし, Xamarin.Forms を使ったいい感じの UI ライブラリ [Fabulous](https://fsprojects.github.io/Fabulous/) もあります.
* [ゲーム開発](https://fsharp.org/guides/apps-and-games/): XNA の後継である [MonoGame](http://www.monogame.net/) や Xamarin 製の Urho3D ラッパー [UrhoSharp](https://github.com/xamarin/urho) は普通に使えます. Unity は[ちょっと厳しめ](https://www.davideaversa.it/2018/04/quick-look-at-f-in-unity/).
* [機械学習](https://fsharp.org/guides/machine-learning/): Microsoft は機械学習用の言語として F# を推しているようで, [公式紹介ページ](https://dotnet.microsoft.com/apps/machinelearning-ai) にもちゃっかり登場しています. ライブラリ面では MS による [ML.NET](https://www.microsoft.com/net/apps/machinelearning-ai/ml-dotnet)[^ml-net] や, サードパーティの全部入り系ライブラリ [Accord.NET](http://accord-framework.net/) など様々なライブラリが揃っています. さらに，[SciSharp STACK](https://github.com/SciSharp) により TensorFlow や Keras, Numpy などへのバインディング/完全な移植が提供されています．
* [クラウドコンピューティング](https://fsharp.org/guides/cloud/): F# は [Microsoft Azure](https://docs.microsoft.com/en-us/dotnet/articles/fsharp/using-fsharp-on-azure/) で公式サポートされており(MS製なのでまあ当然ですが), AWS は .NET 向け [SDK](https://aws.amazon.com/jp/sdk-for-net/)/[Toolkit](https://marketplace.visualstudio.com/items?itemName=AmazonWebServices.AWSToolkitforVisualStudio2017) を提供しているのでそこでも使うことができます. また AWS Developer Blog に [F# の使い方の記事](https://aws.amazon.com/jp/blogs/developer/f-tooling-support-for-aws-lambda/) があります[^aws-lambda]. また [Docker image](https://hub.docker.com/_/fsharp/) もあります.
* 処理系開発: もちろん. [Lex/Yacc](http://fsprojects.github.io/FsLexYacc/) や [Parsec](http://www.quanttec.com/fparsec/) など必要なものは揃っています[^menhir]. スタンドアロンなバイナリで配布できるとかの嬉しさもあります. わたしも[自作言語](https://github.com/cannorin/nml)を F# で作ってます.

[^safe]: これを統合したフルスタックなフレームワーク [SAFE](https://safe-stack.github.io/) を使うと便利です. 
[^bolero]: ASP.NET Core の上で実装されており, 仕組み上 .NET Framework のほぼ全機能(=既存のライブラリのほぼ全て)を使うことができるので, まだベータであることを差し引いてもぶっちゃけ Fable よりオススメです.
[^ifsharp]: ローカルで使うには[IFSharp](https://github.com/fsprojects/IfSharp)をインストールします.
[^ml-net]: ML(言語)と紛らわしい……
[^menhir]: さすがに Menhir はないけど…… だれかポートしてくれないかな
[^aws-lambda]: AWS Lambda は .NET Core を使っていますが, cold start が結構遅いため[使いどころには気をつける必要があります](https://marcroussy.com/2018/01/10/when-to-use-aws-lambda-with-dot-net-core/). これは [tiered compilation](https://blogs.msdn.microsoft.com/dotnet/2018/08/02/tiered-compilation-preview-in-net-core-2-1/) で多少改善されると思われていましたがそうでもないようです. この問題は [ここ](https://github.com/dotnet/core/issues/1060) で議論されていて, 現行バージョンで改善があまりなかったので [ここ](https://github.com/dotnet/core/issues/1968) で reopen されました.

このようにできることが尋常でなく多いので, なにか違うことをやりたくなっても他の言語を覚える必要があまりなくなります.

### 3. だいたいどこでも動く

F# は .NET VM 上で動く言語なので, .NET VM が動く環境ならどこでも動かすことができます.

例えば, 処理系の1つ .NET Core の動作環境は [ここ](https://github.com/dotnet/core/blob/master/release-notes/2.2/2.2-supported-os.md), Mono の動作環境は [ここ](https://www.mono-project.com/docs/about-mono/supported-platforms/) に書いてあります. また, 有名な [Xamarin](https://visualstudio.microsoft.com/xamarin/) を用いればスマートフォンアプリ開発までできてしまいます.

後述の通り, どの処理系・プラットフォームでも使える API の水準が定められており, この範囲で書いている限りはどの環境でも動作することが保証されています.

## よくある誤解・質問

### Q. Windows でしか使えないんでしょう?

**A. わたしは GNU/Linux でしか F# 書いたことないです**

.NET の API にはいくつか種類・水準があります.

1. Windows に乗っているのがいわゆる "フル" の .NET Framework で, ここには WPF (UIツールキットの一種) などの Windows 専用の API も含まれています.

2. *nix 向けの老舗[^mono-date] .NET 実装である [Mono](https://www.mono-project.com/) はフルの .NET のうち[移植できないものを除いた大部分](https://www.mono-project.com/docs/about-mono/compatibility/)の API をサポートしています.

[^mono-date]: 2004年にv1.0が出て現在はv5.14が最新です. わたしは 2010 年ごろから使っています.

3. 最近[^netstandard-date]定義された .NET Standard は, .NET Framework の API のうちプラットフォームによらないポータブルな部分です.

[^netstandard-date]: v1.0は2016年で現在はv2.1です. え, PCL? 悲しい事件だったね……

4. .NET Core は Microsoft による最新のクロスプラットフォーム .NET 実装+開発環境で, .NET Standard をサポートしています. **(超オススメ!!)**

.NET Core は [Windows / Mac OS X / Linux 用にそれぞれ SDK とランタイムが用意されています](https://www.microsoft.com/net/download/dotnet-core/).

主要な Linux distro には repo も用意されていて, パッケージマネージャに管理させることもできます. OS X では Homebrew Cask にもパッケージがあるようです. またすべてのプラットフォームで root 権限が要らないバイナリ版を使うこともできます.

またライブラリ類も .NET Framework 標準の API が非常に充実しているだけでなく, C# で書かれた大量のライブラリを利用することができます[^cli]. 

[^cli]: そもそも C# と F# は同じ .NET の型システムを使っているので, どちらからどちらで書かれたライブラリでも使うことができます. 需要が大きいライブラリはまず .NET Standard に対応しているため, 本当に Windows でしか動かないようなもの以外はほぼ全て利用可能です.

Windows じゃないと困ることは Visual Studio を使えないことくらいです.

### Q. Visual Studio がないと書けないのでは?

**A. CUI だけでも書けますし, VSCode も快適です**

[Vim プラグイン](https://github.com/ionide/Ionide-vim) [^ionide-vim]や [Emacs mode](https://github.com/fsharp/emacs-fsharp-mode) があり, IntelliSense 補完やオンザフライでのシンタックス/コンパイルエラーチェック, 定義されているソースへのジャンプなどを使うことができます.

[^ionide-vim]: ちなみにメンテナは私です. 何かあったら気軽に Issue など投げてください.

また, [Ionide](http://ionide.io/) という VSCode 用の F# 拡張機能があり, こちらでは上に加えて CodeLens での型シグネチャ表示やマウスオーバーでの型表示, GUI でのデバッグなどもすることができます.

なお搭載されている補完エンジン自体は[すべて共通](https://github.com/fsharp/FsAutoComplete)のもので, Visual Studio のものよりは賢くないですが十分便利です.

.NET Core はパッケージの追加やアプリケーションの実行などで `dotnet` コマンドを多用するので, そこらへんはむしろ *nix のほうが楽まであります.

### Q. 型システムが弱いって聞いた

**A. [そんなことはない](https://fsprojects.github.io/FSharpPlus/abstractions.html)**

F# にはインライン関数というものがあって, コンパイル時に消えるのをいいことに, その内部では本来の .NET の型システムでは許されないような様々な暴挙を働くことができます.

どんなことができるのかは [わたしの](https://7colou.red/blog/2018/02-14-fsharp-typeclasses/index.html) [ブログ](https://7colou.red/blog/2018/03-08-fsharp-type-level-computation/index.html) とかみてください. 誤解を恐れずに言えば, Haskell の型クラス[^typeclass]と同等の機能があります.

[^typeclass]: "GHCの" ではない: Orphan instances が完全に許されない. でももしかしたら[そのうちできるようになるかも](https://github.com/Microsoft/visualfsharp/pull/3582)……

ブログの記事でやっているようなことを使って Haskell における様々な概念を使えるようにしたライブラリが, 上で挙げた [FSharpPlus](https://fsprojects.github.io/FSharpPlus/) です. なお F# Foundation 公式プロジェクトの1つです.

### Q. VM 言語だし(例えば OCaml や Haskell より)遅いのでは?

**A. [そんなことはない](https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/fsharp.html)**[^aot]

[^aot]: [CoreRT](https://github.com/dotnet/corert) という .NET Core の AOT コンパイラがありますが, 未だ開発途上なため [F# コードの実行にはまだ少なくない制限があります](https://github.com/dotnet/corert/issues/6055)

これはただのベンチマークなので[^benchmarksgame]そこまで参考になるわけではないですが, 一般に言って .NET/F# にはパフォーマンス上他の処理系より利点となりうる要素がそれなりにあります.

[^benchmarksgame]: かなり有名なサイトではある

**1. boxing を極力排除できる/される**

.NET では値型と参照型が区別されており, 前者に対する操作は unboxed なまま行われます. また JVM と異なりユーザが値型を定義して使うことができます(`struct`). F# においても, [再帰的でない代数的データ型やレコード型を任意に値型にすることができます](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/structures#struct-records-and-discriminated-unions).

また .NET はバイトコードレベルで1階の型システムを積んでいて[^typesystem], 型変数が値型で具体化される際には専用のコードを JIT で生成して余計な boxing/unboxing が発生しないようにします[^genericsref]. これは boxing を使ってパラメータ多相を実現する多くの処理系とは大きく異なる点です. このおかげでハッシュテーブルなどの多相な(ジェネリックな)データ構造はそのような処理系(OCaml, Haskell を含む)と比べてかなり高速です
[^comp-eq].

[^typesystem]: まぁこれが F# の型システムを容易には拡張できない足枷にもなっているのですが

[^genericsref]: 詳しくは [Andrew Kennedy and Don Syme. The design and implementation of generics for the .NET Common Language Runtime](https://www.microsoft.com/en-us/research/publication/design-and-implementation-of-generics-for-the-net-common-language-runtime/) を参照.

**2. 並列・非同期処理が非常に楽**

.NET は async/await や Reactive Extensions といった非同期プログラミング機構の流行の火付け役でもあります. F# ではそれらの機能を簡単に使うことができます.

F# では async/await は [`async` コンピューテーション式](https://fsharpforfunandprofit.com/posts/concurrency-async-and-parallel/)を用いて書かれます. 標準ライブラリに搭載されているので気軽に使うことができます.

```fsharp
let asyncOperation =
  async {
    let! cmp1 =
      heavyComputation1() |> Async.StartChild // 非同期で計算スタート
    let! cmp2 =
      heavyComputation2() |> Async.StartChild // 同上
    do! networkSend "working!"
    let! result1 = cmp1 // 結果が出るまで待つ
    let! result2 = cmp2 // 同上
    return result1 + result2
  }
```

`let!` が別の非同期処理の結果を変数に束縛, `do!` が結果を返さない(`unit` 型を返す)非同期処理の実行をそれぞれ行う文で, return はこの非同期処理の結果を返す文です.[^monad]

[Reactive Extensions](https://fsharpforfunandprofit.com/posts/concurrency-reactive/) も [Control.Observable](https://msdn.microsoft.com/en-us/visualfsharpdocs/conceptual/control.observable-module-%5Bfsharp%5D) モジュールが標準ライブラリに用意されていますが, [FSharp.Control.Reactive](http://fsprojects.github.io/FSharp.Control.Reactive/tutorial.html) パッケージを導入することで `observe` コンピューテーション式も使えるようになり, さらに簡単に扱えます.

また他にも C# で使われる `Task` や並列計算を行う `Parallel.For`, 生の `Thread` なども扱うこともできます. 実際冒頭で示したベンチマークでも, 多くのケースで F# はこれらの機構を使って OCaml よりも CPU load を最適化しています.

[^monad]: モナドで通じる人向け: bind と return です.

**3. インライン関数がある(F#)**

上でも触れましたが, F# では再帰的でない関数に `let inline` というキーワードを用いることで, その呼び出しのインライン展開を強制することができます. これはもちろん[パフォーマンス上の利点](https://stackoverflow.com/questions/14086580/why-does-f-inline-cause-11x-performance-improvement)にもなります.

F# のインライン関数はバイナリにコンパイルされてもメタデータとして残っているので, バイナリで配布されているライブラリからでもインライン展開できるのが特徴的です.

### Q. C#/F# で書かれたライブラリって F#/C# でそのまま使えるの？

**A. F# から C# を呼ぶのは基本的には簡単. 逆は少し大変だができる.**

先述の通り .NET はバイトコードレベルで型システムを積んでいるので, C# も F# も VB.NET も型システムは共通になっています. そのため, C#/F# で書かれた型を F#/C# の型システムでそのまま解釈することができます.

つまり, 実際に違いとして出てくるのはそれぞれの言語の標準ライブラリや, 言語機能の実装に使われている型の違いになります.

例えば, C# の次のコード

```csharp
public class A
{
    public static int Add(int x, int y)
    {
        return x + y;
    }
}
```

は F# では

```fsharp
type A =
  class
    static member Add : x:int * y:int -> int
  end
```

というシグネチャを持つ型として解釈されます. つまり C# の多引数メソッドは, F# ではタプルを引数に取る関数として扱われるわけです.

この種の便利な変換をしてくれないのは, そもそも使われている型自体が違う場合です. 例えば C# ではメソッドはファーストクラスではなく, ラムダ式は `Func<...>` 型か `Action<...>` 型を持つオブジェクトとして扱われます.

一方 F# の関数値は基本的に `FSharpFunc<_, _>` 型にコンパイルされ, ラムダ式は特別に `Action` や `Func` を期待される文脈ではそれらの型になり, そうでない場合は `FSharpFunc` 型になります. そのため, C# の高階メソッドを呼ぶ場合, 関数のまま渡すかラムダ式に包むかで型エラーになったりならなかったりします.

```fsharp
> [1;2;3].ToList().ForEach(fun i -> printfn "%i" i);;
1
2
3
val it : unit = ()

> [1;2;3].ToList().ForEach(printfn "%i");;           

  [1;2;3].ToList().ForEach(printfn "%i");;
  ---------------------------------^^^^

/home/user/stdin(11,34): error FS0001: This expression was expected to have type
    'System.Action<int>'    
but here has type
    ''a -> unit'
```

また F# の標準ライブラリにある型を C# から呼ぶ場合, それらは F# 独自の機能(各種リテラルや独自演算子の定義など)を前提に作られているため, 書き方が冗長になってしまう場合があります. 例えば, F# のリストは F# では

```fsharp
let xs = [1;2;3]
```

と書けますが, これは連結リストとして実装されているため, C# で同じ物体を作ろうとすると

```csharp
var xs = List<T>.Cons(1, List<T>.Cons(2, List<T>.Cons(3, List<T>.Nil)));
```

となってしまいます. 一方 C# の標準ライブラリは F# の標準ライブラリでもあるため, F# から C# の物体を扱う場合は C# と同様の記法で済みます.

ただし, C# のライブラリの高階メソッドは F# をはじめとした ML 系言語の機能を模倣するためにあるものが多い(LINQ など)ので, F# にあるものを使ってしまえば済む場合が大半です. 上の例も, 普通なら

```fsharp
[1;2;3] |> List.iter (printfn "%i")
```

と書きます. よって F# から C# 製のライブラリを使うのに困ることはあまりありません.

F# で C# (.NET) の機能を使う例は F# for fun and profit の [Anything C# can do...](https://fsharpforfunandprofit.com/posts/completeness-anything-csharp-can-do/) と [Seamless interoperation with .NET libraries](https://fsharpforfunandprofit.com/posts/completeness-seamless-dotnet-interop/) で解説されています. というか, C# ユーザの方なら [Why use F#](https://fsharpforfunandprofit.com/posts/why-use-fsharp-intro/) シリーズがそもそも C# ユーザ向けに書かれているため, この記事よりそちらを読んでいただくほうが良いかもしれません[^fffap][^for-csharper].

[^fffap]: 最近 F# for fun and profit を和訳しろという圧力を感じてます…… や, そうした方が良いんですが, わたしが全部やるのは単純に身体が1つでは足らないです.

[^for-csharper]: この記事はどちらかといえば .NET を知らない・使ったことがない人向けに書いているつもりです.

## 開発・実行環境: .NET Core & VSCode

### 特長

.NET Core は最新の .NET 実装なだけあって, 今までの実装にはない特長が数多くあります.

#### 1. クロスプラットフォーム

* Mono と同じく, .NET Core は Windows, OS X, Linux のどのプラットフォームでも全く同じ開発・実行環境を使うことができます.
* プラットフォーム依存なコードを書かない限り, プラットフォーム依存な問題は発生しません.
* VSCode などのクロスプラットフォームなエディタを使えば, たとえ短期間で異なる OS 環境を行き来するような事態になっても, ソースコードを持ってくるだけでそのまま作業を続行できます[^cp].

[^cp]: 実体験です

#### 2. 高速な動作, 簡単な配布

* .NET Core は現状で最も高速な .NET 実装の1つで, [ベンチマーク上](https://benchmarksgame-team.pages.debian.net/benchmarksgame/which-programs-are-fast.html)では例えば Go とほぼ同等〜少し速い程度のパフォーマンスを発揮します.
* .NET Core は Go と似たような[スタンドアロンバイナリの生成](https://docs.microsoft.com/en-us/dotnet/core/deploying/index#self-contained-deployments-scd)をサポートしており[^single-exe], .NET Core ランタイムがインストールされていない Windows / OS X / Linux 環境上で実行可能な状態で配布することができます.

[^single-exe]: 既定では Go のように単一の実行ファイルが出力されるのではなく, 必要なファイルが全て入ったディレクトリを出力するようになっていますが, .NET Core 3.0 から[単一の実行ファイルも作れるようになりました](https://docs.microsoft.com/ja-jp/dotnet/core/whats-new/dotnet-core-3-0#single-file-executables).

#### 3. ツール類が `dotnet` コマンドに集約されていて, ほとんどの操作がこれだけで完結する

* .NET Core には [`dotnet` という CLI ツール](https://docs.microsoft.com/en-us/dotnet/core/tools) が同梱されており, Rust における `cargo` コマンドと同様の立ち位置・同等の強力な機能[^comparison-to-cargo]を備えています.
* SDK 自体に同梱されているので何もしなくても使えますし, コンパイラを作っているのと同じところが作っているので余計な互換性問題を考えなくて済むのも利点です(`cargo` と同じように).


[^comparison-to-cargo]: テンプレートからの新規作成, ビルド, 実行, テスト実行, パッケージのインストール・作成・アップロード, [ユーザによるプラグイン機能](https://docs.microsoft.com/en-us/dotnet/core/tools/extensibility), [npm でのような CLI ツールのインストール](https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools) etc.

### インストール

.NET Core SDK は上述の通り, [ここ](https://www.microsoft.com/net/download/dotnet-core/) からインストールすることができます. *nix では何らかのパッケージマネージャに乗っかっておいたほうがアップデートが楽ですが, 直接ダウンロードするバイナリは root 権限がなくても使うことができます.

VSCode は [ここ](https://code.visualstudio.com/docs/setup/setup-overview) からインストールすることができます. 同様にパッケージマネージャに乗っかると楽です. 

F# は .NET Core SDK に標準搭載されているので, コンパイルするだけならこれ以上のインストールは必要ありません. VSCode で F# を書くには拡張機能の [Ionide](http://ionide.io/) が別途必要です[^vscode-fsharp].

なお, エディタは前述の通り補完エンジンが共通なので VSCode でなくてもそこまで変わらないです.

[^vscode-fsharp]: VSCode で書くには別途言語サポート拡張機能が必要なのは C# など他の言語でも同じですが

### 実際に触ってみる

インストールが完了したならば, 早速 F# で Hello, World! してみましょう.

```bash
$ dotnet new console -lang="F#" -o helloworld
```

でテンプレートから `helloworld` フォルダに生成されます.

このとき生成される3つの物体について説明しておくべきでしょう:

**1. `Program.fs`**  … ソースコードです.

```fsharp
open System // 名前空間やモジュールをオープンするやつです

// [<EntryPoint>] は属性と呼ばれるもので, これが付いた
// 関数がプログラムのエントリーポイントになります. 
// 属性さえついていれば名前は `main` でなくてもOKです.
// 実行したいコードは必ずここに書かなければいけないわけではなく,
// この外に `printf` など処理を書いても実行されます.
// ただし, コマンドライン引数を取りたい場合はこの関数で受け取るしかありません.
[<EntryPoint>]
let main argv (* コマンドライン引数 (strint array) *) = 
    printfn "Hello World from F#!" // 'n' は newline の意
    0 // エントリーポイントは return code (int) を返す必要があります
```

**2. `helloworld.fsproj`** … Rust における `Cargo.toml` に相当するもの.

 コンパイルするファイルや依存パッケージ, メタデータ類を記述します.

```xml
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <Compile Include="Program.fs" />
  </ItemGroup>

</Project>
```

**3. `obj/`**  … コンパイル時のキャッシュなどが入るディレクトリです.

基本的に触る必要はなく, 配布する必要もないので `.gitignore` などに入れておくべきです.

さて, コンパイルして実行してみましょう. と言いましたが, 実行するコマンドを打つだけで, 必要な場合[^compilation-needed]は勝手にコンパイルが走ります.

[^compilation-needed]: まだコンパイルしたことがない, ソースコードが変更された, etc.

```bash
$ dotnet run
Hello World from F#!
```

ではパッケージを追加してみましょう. 拙作の [FSharp.Scanf](https://www.nuget.org/packages/FSharp.Scanf/) ライブラリ[^scanf]を導入して, フォーマット付きで入力を受け付けられるようにしてみます.

[^scanf]: F# には `printf` はあるが, 入力に関しては文字や文字列単位で読み込むもの(`System.Console.ReadLine()` など)のみがあり, 組み込みでは `scanf` のような関数がない. 拙作の [scanf](https://github.com/cannorin/FSharp.CommandLine/blob/master/src/FSharp.Scanf/scanf.fs) は型安全で, `Scanf.Optimized` モジュール下の関数はマイクロ秒オーダーで入力を処理できる(ようにした, 以前と比べ約10倍の高速化. 2019/01/06).

```bash
$ dotnet add package FSharp.Scanf
  Writing /tmp/tmpmQgFq7.tmp
info : パッケージ 'FSharp.Scanf' の PackageReference をプロジェクト '/home/.../helloworld.fsproj' に追加しています。
log  : /home/.../helloworld.fsproj のパッケージを復元しています...
info :   GET https://api.nuget.org/v3-flatcontainer/fsharp.scanf/index.json
info :   OK https://api.nuget.org/v3-flatcontainer/fsharp.scanf/index.json 691 ミリ秒
info :   GET https://api.nuget.org/v3-flatcontainer/fsharp.scanf/2.2.6831.16169/fsharp.scanf.2.2.6831.16169.nupkg
info :   OK https://api.nuget.org/v3-flatcontainer/fsharp.scanf/2.2.6831.16169/fsharp.scanf.2.2.6831.16169.nupkg 773 ミリ秒
log  : FSharp.Scanf 2.2.6831.16169 をインストールしています。
info : パッケージ 'FSharp.Scanf' は、プロジェクト '/home/.../helloworld.fsproj' のすべての指定されたフレームワークとの互換性があります。
info : ファイル '/home/.../helloworld.fsproj' に追加されたパッケージ 'FSharp.Scanf' バージョン '2.2.6831.16169' の PackageReference。
```

すると, `helloworld.fsproj` が次のように変更されます.

```xml
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <Compile Include="Program.fs" />
  </ItemGroup>

  <!-- ↓New! -->
  <ItemGroup>
    <PackageReference Include="FSharp.Scanf" Version="3.0.3291.28624" />
  </ItemGroup>

</Project>
```
では, `Program.fs` を書き換えてみましょう.

```fsharp
open FSharp.Scanf

printfn "what is the ultimate answer?"

try
  let ans = scanfn "%i"
  if ans = 42 then
    printfn "correct."
  else
    printfn "%i? no." ans
with
  | _ -> printfn "you entered something other than a number."
```
わたしは 2 spaces でインデントするのが好きなのでそのように書き換えました[^tab]. また今回はコマンドライン引数が要らないのでエントリーポイントは使いません.

[^tab]: タブインデントは[言語仕様レベルで禁止されています](https://fsharpforfunandprofit.com/troubleshooting-fsharp/#watch-out-for-hidden-tab-characters). 

では, 実行してみましょう.

```bash
$ dotnet run
what is the ultimate answer?
42
correct.
$ dotnet run
what is the ultimate answer?
0
0? no.
$ dotnet run
what is the ultimate answer?
foo
you entered something other than a number.
```

このように, .NET Core における開発は基本的にはシェルで, `dotnet` コマンドを使って行われます. これは Ionide(VSCode) などを使っている場合でも同様です. 

ただし, `dotnet` コマンドではソースコードの追加・移動・削除をするコマンドがデフォルトでは用意されておらず, `.fsproj` を直接編集するか Ionide などのエディタ拡張の機能で行う必要があります.

### モジュールに関する注意

 F# ではコードが上から順番に解釈される(自分より下に記述した関数を呼び出すことができない)だけでなく, `.fsproj` において**上から書かれた順に**ソースファイルを読み込みます.

前者は [`module rec` を使う](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/modules#recursive-modules) ことで解決できますが, 後者はどうにもなりません. ただし, 既存のモジュールと同名のモジュールを別の名前空間に作っても特に問題はないので,

```fsharp
// A.fs
module A

let f x y = x + y
```

```fsharp
// B.fs
module B

let g x = A.f x 1
```

```fsharp
// AfterBDefined.fs
[<AutoOpen>] // この属性が付いていると中身が自動でグローバル名前空間に展開されます
module AfterBHasDefiend

module A =
  let h x = B.g (x*2)
```

のようにすれば, 使う側からは `A` は `f` と `h` を持つ単一のモジュールに見えます.

また複数ファイルからなる F# プログラムでは, 上の例のように, 各ソースファイルの先頭に名前空間宣言 (`namespace Foo`) もしくはモジュール宣言 (`module Foo`) を置かなければなりません. 以後に続くトップレベルの全てが先頭で宣言した名前空間/モジュールの中身に入ることになります. 詳しい解説は [MSDN](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/modules#remarks) にあります.

#### 余談(追記): 依存関係は良くないし, 相互参照は最悪である

と書いたところ "上から順にしか関数を定義できないのが言語としてブサイク" と反応を頂いた[^omae]ので, なんでこういう仕様になっているのかをちょっと書いてみたいと思います.

[^omae]: ~~お前の bio に書いてある Haskell っていう言語も [基本的には循環参照するモジュールを書けない](https://wiki.haskell.org/Mutually_recursive_modules) んだが本当に書いたことあるのか？？~~

まず前提として, 循環参照する **値(関数)** や **型** は特に問題ではありません. F# でもそれらは書くことができます.

```fsharp
let rec isEven x =
  if x = 0 then true  else isOdd  (x - 1)
and     isOdd  x =
  if x = 0 then false else isEven (x - 1)

type Tree<'a> = TEmpty | TNode of 'a * Forest<'a>
and  Forest<'a> = FNil | FCons of Tree<'a> * Forest<'a>
```

しかしご覧の通り, どの関数同士が・どの型同士が相互再帰しているのかを `and` キーワードを使って明示しなければいけません. なぜならば, そうすることで依存関係を *明確かつ最小限に* することができるからです.

F# では(というか一般的な ML 系言語は), 今書いている関数の中で使う関数は, それより前に定義されていなければなりません. これによって関数同士の依存関係が非常に明確になり(前を探せば良いのですから), メンテナンスが容易になります. 循環参照する値や型を `and` でまとめて近くに置いておけば, この "前を探せばいい" ルールが破れている箇所が最小限になり, 注意しやすくなります.

依存関係があると, ある一箇所を変更したときにそれに依存する箇所が次々と壊れてしまいます. そのため, *より一般的・抽象的・変更の可能性が少ないコード* を下のレイヤに, *より局所的・具体的・変更の可能性が大きいコード* を上のレイヤに配置し, それぞれのレイヤはより下位のレイヤにしか依存しないようにすることでメンテナンスを容易にする, というのが昔から知られた手法です.

循環参照する **モジュール** がこれをぶち壊しにすることは明確でしょう. モジュールはまさにレイヤを表現するもので, それが循環参照したならば "それぞれのレイヤはより下位のレイヤにしか依存しない" という原則自体を破ります. それゆえ F# は(一般に ML 系言語は)循環参照するモジュールを許しません. 一方循環参照する値や関数は, 同じレイヤに属する存在である限り, 制限する理由はありません.

詳しくは [F# for fun and profit - Cyclic dependencies are evil](https://fsharpforfunandprofit.com/posts/cyclic-dependencies/) を読んでください.

## F# の特徴的な言語機能

### オフサイドルール

F# は Python や Haskell などと同様にオフサイドルールを採用しており, インデントが構文解析に大きく影響する代わりに, 元となった OCaml より記述量が少なく済むようになっています[^offside].

具体的には, 次のように簡略化されます.

```fsharp
// verbose
let f x =
  let a = 1 in
  let b = 2 in
  x + a + b
in f 42

// lightweight
let f x =
  let a = 1
  let b = 2
  x + a + b
f 42
```

```fsharp
// verbose
module A = begin
  let f x = x + 1
end

// lightweight
module A =
  let f x = x + 1
```

```fsharp
// verbose
if b then begin
  printfn "a";
  printfn "b";
  printfn "c"
end

// lightweight
if b then
  printfn "a"
  printfn "b"
  printfn "c"
```

```fsharp
// verbose
match a with
| Some b ->
  begin match b with
  | Pos i -> int i
  | Neg i -> -int i
  end
| None -> 0

// lightweight
match a with
| Some b ->
  match b with
  | Pos i -> int i
  | Neg i -> -int i
| None -> 0
```

なお, OCaml と同様の構文も `#light "off"` ディレクティヴをファイルの先頭に置いてあげると使えます. この場合はインデントは構文に影響しなくなり, その代わりに様々な場所で `in` や `end` などが必要になります.

オフサイドルールについては [F# syntax: indentation and verbosity - F# for fun and profit](https://fsharpforfunandprofit.com/posts/fsharp-syntax/) に詳しいです.

[^offside]: 実はわたしはそんなに好きじゃないです. インデントが極端に深くなる書き方を避けようとして, 直し方がわからないエラーに悩まされるので.

### Computation Expressions

**コンピューテーション式(Computation Expressions)** は定型的な関数呼び出しに対していい感じな DSL を定義して使うことができる機能です[^monad2]. 

[^monad2]: ただの do-notation だと思われるかも知れませんが, 実際は独自構文を定義できたりする関係でより広い範囲の抽象 (MonadPlus, そもそもモナディックでもなんでもない物体, etc) を扱うことができます. 言い方を変えれば, モナドは computation expressions で扱える抽象の*1つに過ぎません*. 参考: [The F# Computation Expression Zoo](http://tomasp.net/blog/2013/computation-zoo-padl/index.html)

例えば, 九九を列挙する[遅延リスト](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/sequences)も:

```fsharp
seq {
  for a in 1..9 do
  for b in 1..9 do
  let text = sprintf "%i*%i=%i" a b (a*b)
  yield (a, b, text)
}
// val it : seq<int * int * string> =
//   seq
//     [(1, 1, "1*1=1"); (1, 2, "1*2=2"); (1, 3, "1*3=3"); (1, 4, "1*4=4"); ...]
```

[`async` による非同期処理](https://docs.microsoft.com/en-us/dotnet/fsharp/tutorials/asynchronous-and-concurrent-programming/async)も:

```fsharp
let heavyComputation = async {
  do! Async.Sleep 1000
  return 42
};;
// val heavyComputation : Async<int>

let exec = async {
  let startTime = DateTimeOffset.UtcNow
  let! cmp1 = heavyComputation |> Async.StartChild
  let! cmp2 = heavyComputation |> Async.StartChild
  let! result1 = cmp1
  let! result2 = cmp2
  do printfn "%fms" (DateTimeOffset.UtcNow - startTime).TotalMilliseconds
  return result1 + result2
};;
// val exec : Async<int>

Async.RunSynchronously exec;;
// 1003.727000ms
// val it : int = 84
```

一見異なる構文のように見えて, 実は同じ Computation Expression の仕組みを使って実装されています.[^seq]

[^seq]: 遅延リスト(`seq`, `IEnumerable<_>`)はコンパイラによって特別扱いされてステートマシンに展開するなどの最適化が入ることもある

Computation Expression はコンパイル時に糖衣が剥がされて, 事前に定義されたルールによって関数呼び出しに変換されます. そしてユーザがそれを定義することで, 比較的容易に Computation Expression を自作することができます.

またデフォルトで用意されているものだけでなくユーザが独自の拡張構文を導入することもできるので,

[パッケージ管理システム NuGet の設定ファイルのDSL](http://www.readcopyupdate.com/blog/2014/10/10/edsls-using-custom-operations.html)を作れたり:

```fsharp
let nugetDef = 
  nuget {
    rootDirectory "c:\\dev"
    toolsDirectory "c:\\dev\\tools"
    outputDirectory "c:\\dev\\output"
    packageProject {
      id "Foo.Bar"
      version (v"1.2.3")
      includeDependency !> ("xunit", v"1.9.1", Net40)
      includeDependency !> ("autofac", v"1.0.0")
    }
  }
```

[モナディックにコマンドラインアプリを組めるライブラリ](https://github.com/cannorin/FSharp.CommandLine)[^fsharp-commandline]を作れたりします:

[^fsharp-commandline]: 拙作

```fsharp
let colorOption = 
  commandOption {
    names ["color"; "c"]; description "Colorize the output."
    takes (format "red"   |> asConst ConsoleColor.Red)
    takes (format "green" |> asConst ConsoleColor.Green)
    takes (format "blue"  |> asConst ConsoleColor.Blue)
    suggests (fun _ -> [CommandSuggestion.Values["red"; "green"; "blue"]])
} 

let echoCommand =
  command {
    name "echo"
    displayName "main echo"
    description "Echo the input."
    opt color in colorOption |> CommandOption.zeroOrExactlyOne
    do! Command.failOnUnknownOptions()
    let! args = Command.args
    do 
      let s = args |> String.concat " " 
      match color with
        | Some c -> cprintfn c "%s" s
        | None ->   printfn "%s" s
    return 0
}
```

Computation Expression の仕組み・作り方については[ここ](https://fsharpforfunandprofit.com/posts/computation-expressions-intro/)が詳しいです.

また, FSharpPlus には [任意のモナドに使える do-notation](http://fsprojects.github.io/FSharpPlus/computation-expressions.html) もあります.

### `inline` 関数, Statically Resolved Type Parameters

**インライン関数 (inline functions)** はその名の通りコンパイル時にインライン化される関数です.

```fsharp
let inline addTwice x y = x + y + y

addTwice 2 3 // `2 + 3 + 3` になる
```

インライン関数自体はそれだけなのですが, 前述の通り "コンパイル時に消える" という性質を活かし, .NET では許されないような様々な型システム拡張を実現するのに使われます.

前提として, F# においては任意の型にメンバ変数/関数を追加することができます[^member].

[^member]: クラスベースOOPに馴染みがないならば, 型に密結合したモジュールと考えてください. `static member` は `let` 束縛のようなものでモジュールのように `型名.名前` で呼び出すことができ, `member this.hoge` は `値.名前` で呼び出すことができます.

```fsharp
type Foo = FooInt of int | FooStr of string
  with
    member this.str =
      match this with
      | FooInt i -> sprintf "%i" i
      | FooStr s -> s
    static member isInt x =
      match x with
      | FooInt _ -> true
      | FooStr _ -> false

printfn "%s" (FooInt 42).str    // "42"
printfn "%s" (FooStr "bar").str // "bar"

Foo.isInt (FooStr "bar") |> printfn "%b" // false
```

そして, インライン関数内では, 型パラメータにおいてその型が*ある特定のメンバを持っていること*を要求することができます.

```fsharp
let inline getStr (x: ^X) =
  (^X: (member str: string) x)
// val inline getStr : x: ^X -> string
//   when ^X : (member get_str :  ^X -> string)

type 'a Bar = { bar: 'a }
  with
    member this.str = sprintf "bar: %A" this.bar

FooInt 42  |> getStr |> printfn "%s" // "42"
{ bar=42 } |> getStr |> printfn "%s" // "bar: 42"
```

このように, コンパイル時に解決される型変数を Statically Resolved Type Parameters (SRTP) と呼びます. SRTP は `'` ではなく `^` が頭に付き, メンバ制約(member constraint)を加えることができます.

勘の良い方は既にお気づきの通り, SRTP のメンバ制約は型クラスと同じ働きをします.

例えば, F# のほとんど全ての組み込み演算子[^builtin-op]はインライン関数として定義されています[^inline-op] :

[^builtin-op]: `!` など特定の型にしか使えないものもあるが, [ユーザがグローバル名前空間でインライン関数として再定義すれば, 他の型にも使えるようにできる](https://github.com/fsharp/fslang-suggestions/issues/531#issuecomment-275832466).

[^inline-op]: 実際は特定の組み込み型に対してはコンパイラが直接最適な実装に置き換えるので, そのまま以下のように実装されているわけではない.

```fsharp
let inline (+) (x: ^T) (y: ^U) : ^V =
  (static member (+) : ^T * ^U -> ^V) (x,y))
```

よって型ごとに演算子をオーバーロードすることができて, しかも(実行時ではなく)コンパイル時に該当の実装に置き換えられます:

```fsharp
type Baz = { bazInt: int; bazStr: string }
 with
  static member inline (+) (x: Baz, y: Baz) =
    { bazInt = x.bazInt + y.bazInt;
      bazStr = sprintf "%s+%s" x.bazStr y.bazStr }

{ bazInt=1; bazStr="a" } + { bazInt=2; bazStr="b" } |> printfn "%A"
// { bazInt = 3; bazStr = "a+b";}
```

この例では実装もインライン化してあるので, コンパイル時には単にレコードを生成するコードになります.

そして, inline 関数の SRTP だけでなく, データ型の型変数もメンバ制約を持つことができます[^datatype-srtp].

```fsharp
type Hoge< 'a when 'a: (member Piyo: string) > = ...
```

[^datatype-srtp]: この際の型変数のプレフィックスは `'` だが, SRTP と同じ扱いになる.

なお, F# では等値判定 (`equality`) と大小比較 (`comparison`) の実装を要求する制約は特別扱いされており[^comp-eq], `^X when ^X: (static member (=): ... )` ではなく `^X when ^X: equality` のように書きます[^customcomparison].

[^comp-eq]: .NET との兼ね合いなど様々な事情により, F# の標準ライブラリの `Map`, `Set` などを含む *F#の* 等値判定と大小比較を使うコードではインライン化 (F#レベル/ILレベル) の恩恵を受けることができず, ユーザ作成の代数的データ型やレコードに用いたときに不必要な boxing/unboxing が発生する *ことがある* (事情が複雑過ぎてわたしには把握しきれない). これはユーザレベルでなら[workaround が存在](https://zeckul.wordpress.com/2015/07/09/how-to-avoid-boxing-value-types-in-f-equality-comparisons/)し, 包括的には [F# vNext で改善される見込み](https://github.com/Microsoft/visualfsharp/pull/5112). C# の標準ライブラリの `Dictionary` などを使えば, 現行バージョンでもこの問題は起きない(はず).

[^customcomparison]: 代数的データ型とレコードについては, 関数型などの比較不能な型を含んでいない限りは, 自動で `comparison` と `equality` が実装される.

よって, 集合を表すデータ型 `Set` には次のような制約が付いています[^functor].

[^functor]: ちなみに, F# ではモジュールは型ではないため, 型変数を取ってメンバ制約をかけたり, メンバ制約の対象にすることができないのですが, .NET の本来のクラスベースOOの機能を使って, ["open できないし内部で型を定義できない代わりに型扱いになるモジュールのようなもの"](https://stackoverflow.com/a/13419343) を作ることができます. これを使えば OCaml のファンクタも[一応模倣可能](https://wandbox.org/permlink/5wB2QI8SnqEfDuge)ですが(型変数でモジュールもどきを要求すれば良い), 徹底的にやってる人はわたしは見たことがありません. [FSharp.Compatibility.OCaml](https://github.com/fsprojects/FSharp.Compatibility/blob/master/FSharp.Compatibility.OCaml/) では型レベルではなく値レベルで模倣しています.

```fsharp
type Set< 'a when 'a: comparison > = ...
```

インライン関数と SRTP によるアドホック多相機能を存分に活かすことで, 上で何度も紹介している [FSharpPlus](https://fsprojects.github.io/FSharpPlus/) や [わたしの](https://7colou.red/blog/2018/02-14-fsharp-typeclasses/index.html)[ブログ記事](https://7colou.red/blog/2018/03-08-fsharp-type-level-computation/index.html) が実現されています.

MSDN に [インライン関数](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/functions/inline-functions), [SRTP](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/generics/statically-resolved-type-parameters), [型変数に加えられる制約](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/generics/constraints), [演算子のオーバーロード](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/operator-overloading) についてのドキュメントがあります.

### Type Providers

**型プロバイダ (Type Providers)** は, コンパイル時にわかる情報から自動的に型を生成する仕組みです.

メタプログラミングとしてはありがちな(ほんとか？)仕組みですが, 言語自体がサポートしているのが良い点です.

例えば JSON を返す URI を [FSharp.Data の JsonProvider](http://fsharp.github.io/FSharp.Data/library/JsonProvider.html) に渡すと, バックグラウンドで動作しているコンパイラサービスの働きにより, IDE 上で*リアルタイムで*補完が効き始めます!

![FSharp.Data ウェブサイトより, JSON Provider が動いている様子](http://fsharp.github.io/FSharp.Data/images/json.gif)

* JSON や XML などをサンプルデータから型を生成して扱える [FSharp.Data](http://fsharp.github.io/FSharp.Data)
* Azure Storage 上のアセットを補完付きで様々に操作できる [Azure Storage Type Provider](http://fsprojects.github.io/AzureStorageTypeProvider/)
* SQL データベースアクセスを型安全に行える [SQLProvider](https://fsprojects.github.io/SQLProvider/index.html)
* OpenAPI 2.0 に対して API ラッパーライブラリを自動生成できる [SwaggerProvider
](http://fsprojects.github.io/SwaggerProvider/) [^swaggerprovider] とその後継の [OpenAPI Type Provider](https://github.com/fsprojects/OpenAPITypeProvider)
* ローカルの R 言語の環境に入っているパッケージを読み取ってラッパーを生成する [R Type Provider](https://fslab.org/RProvider/index.html)

[^swaggerprovider]: 知らないうちに死んでた…

など, 様々なパッケージが存在します.

実装はあまり進んでいないようですが, [代数的データ型やレコード型の生成](https://github.com/fsharp/fslang-suggestions/issues/154) や [型の情報からの型生成](https://github.com/fsharp/fslang-design/issues/125) も approved in principle となっており, 将来的にサポートされる可能性があります. 

Type Provider の作り方のドキュメントは [MSDN にあります](https://docs.microsoft.com/en-us/dotnet/fsharp/tutorials/type-providers/).

### その他細かな機能

#### Code Quotation

F# は組み込みでコードをクォートする機能を持っていて, F# の構文木に変換されます.

```fsharp
<@ let f x = x + 10 in f 32 @>
// val it : Quotations.Expr<int> =
//   Let (f, Lambda (x, Call (None, op_Addition, [x, Value (10)])),
//      Application (f, Value (32)))
```

これによって F# のコードの生成や, F# のコードから他の言語のコードの生成が実装しやすくなっています.

[MSDN にドキュメントがあります](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/code-quotations). また構文木のコンパイル・実行には [FSharp.Quotations.Evaluator](http://fsprojects.github.io/FSharp.Quotations.Evaluator/) やサードパーティの [QuotationCompiler](https://github.com/eiriktsarpalis/QuotationCompiler) などを使うことができます.

#### Units of Measure

F# では数値型を修飾する(物理)単位を定義し, 使うことができます.

```fsharp
[<Measure>] type kg
[<Measure>] type m
[<Measure>] type s
[<Measure>] type N = kg * m * s^-2

let weight = 50.0<kg>
// val weight : float<kg> = 50.0

let acc = 9.8<m s^-2>
// val acc : float<m/s ^ 2> = 9.8

let power : float<N> = weight * acc
// val power : float<N> = 490.0
```

また, 単位の換算方法を定義しておけば, 物理量の次元チェックを自動的に行うことができます. 例えば, 拙作の [国際単位系ライブラリ](https://github.com/cannorin/misc/blob/master/uom.fsx) では, `mL` と `_milli L` と `cm^3` が同じ単位であることを認識することができ, SI接頭辞の換算を安全に行うことができます.

```fsharp
let a_1mL = 1.0<_milli L>
let b_1mL = 1.0<cm^3>
let c_1mL = 0.001<L>

let compareML (a: float<mL>) (b: float<mL>) =
  printfn "%AmL %s %AmL" a (if a = b then "=" else "<>") b
compareML a_1mL b_1mL
// compareML a_1mL c_1mL (* compilation error *)

let f = 5.0<N>
let m = 4.0<g>

let printAcc (a: float<m/s^2>) =
  printfn "%AN = %Ag * %Am/s^2" f m a
printAcc (f / (kilo * m))
// printAcc (f / m) (* compilation error *)
```

Units of Measure はコンパイル時に消去されるので, オーバーヘッドは発生しません.

#### Mutable Variables

F# には `ref` 型(ML 系言語で一般的な抽象化されたポインタ)とは別に, **変更可能な変数 (Mutable Variables)** があります.

```fsharp
let x = ref 10  // ref による再代入可能な変数
x := 42         // ref の値の変更
printfn "%d" !x // ref の値の読み出し

let mutable y = 10 // mutable による再代入可能な変数
y <- 42        // mutable 変数の値の変更
printfn "%d" y // mutable 変数の値の読み出し
```

このように `ref` を用いた場合は値への参照を変数に束縛して扱う形になるのに対して, `let mutable` で宣言した mutable variables は `<-` 演算子で値を変更できる以外は通常の変数と同じです.

また `ref` では変数がヒープに保存されるのに対し, `let mutable` ではスタックに確保されるので, パフォーマンス上多少有利です. ただしスタックの値は定義されたスコープを抜けると消えてしまうため, `let mutable` がクロージャにキャプチャされる可能性があるときは, コンパイル時に自動的に `ref` に置き換えられてしまいます. 例えば次のような場合です:

```fsharp
let newIncrCounter () =
  let mutable i = 0
  fun () ->
    i <- i + 1
    i
// val newIncrCounter : unit -> (unit -> int)
```

また, `ref` では同じ領域を参照する2つの変数を作ることができるのに対して, `let mutable` ではそれができません[^aliasing-ref].

[^aliasing-ref]: 出典: https://stackoverflow.com/questions/3221200/f-let-mutable-vs-ref

```fsharp
let a = ref 5  // ヒープに新しい領域を確保
let b = a      // bは同じ領域を指す
b := 10        // aの中身も同時に変更される

let mutable a = 5 // スタックに値を確保
let mutable b = a // b はスタックに現在の a の値を確保する
b <- 10           // b の中身だけが変わる
```

さらに, レコード型のフィールドも `mutable` キーワードを用いて変更可能にすることができます.

```fsharp
type SampleRecord = {
  field1: int
  mutable field2: int
}

let sr = { field1=0; field2=1 }
sr.field2 <- 42
sr.field1 <- 42 // コンパイルエラー!
```

実は, `ref` は mutable レコードで実装されています:

```fsharp
// https://github.com/fsharp/fsharp/blob/master/src/fsharp/FSharp.Core/prim-types.fs

type Ref<'T> = 
  { mutable contents: 'T }

and 'T ref = Ref<'T> 

let ref value = { contents = value }
let (:=) cell value = cell.contents <- value
let (!) cell = cell.contents
```

[Mutable variables](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/values/index#mutable-variables) と [reference cells](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/reference-cells) については [Wikibooks の F# Programming/Mutable Data](https://en.wikibooks.org/wiki/F_Sharp_Programming/Mutable_Data) もご覧ください(少し古く, `let mutable` がスコープを抜けるとコンパイルエラーになっていた時代のものですが).

#### Byrefs, `byref`-like Structs

##### Byrefs

**Byrefs** は*関数の引数のみ*に使うことができる `ref` 型の変種で, 次の3種があります.

1. `'t inref` ... 型 `'t` の値を*読み取る*ことだけができるポインタ
2. `'t outref` ... 型 `'t` の値を*書き込む*ことだけができるポインタ
3. `'t byref` ... 型 `'t` の値を*読み書き*できるポインタ

これにより, ポインタを受け取る際に細かなアクセス制御をかけることができます.

そして, Byrefs を引数に取る関数に実際に渡すことができるのは次の2つです.

1. `'t ref` 型の値
2. 変数 `v` があるとき, そのポインタ `&v`

2 に関しては, `let v = 10` のように再代入不可の変数として宣言された場合は `inref` としてのみポインタを取ることができ, `let mutable v = 10` のように再代入可能な変数として宣言された場合は `outref` や `byref` としてもポインタを取ることができます. また, 変数のポインタは変数が定義されたスコープから出ることができません.

##### `byref`-like Structs

**`byref`-like structs** とは, スタックに確保される値型です. `byref`-like structs は使用可能な場所が限られていたり[^lifetime], クロージャでキャプチャできなかったりなどの制限がある代わりに, 高いパフォーマンスを要求される処理に極めて有効です.

`byref`-like structs の例としては `Span<'T>` 型や `Memory<'T>` 型があります.

[`Span<'T>`型](https://docs.microsoft.com/en-us/dotnet/api/system.span-1) は配列や文字列, [`NativePtr.stackalloc` 関数](https://msdn.microsoft.com/en-us/visualfsharpdocs/conceptual/nativeptr.stackalloc%5B%27t%5D-function-%5Bfsharp%5D) でスタックに確保した領域や [`Marshal.AllocHGlobal` 関数](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.marshal.allochglobal?redirectedfrom=MSDN&view=netframework-4.7.2) で .NET のGCの管理外メモリに確保した領域などを, その全体でも*一部分でも*, 包括的かつ効率的に扱うことができる型です.

```fsharp
// https://github.com/fsharp/fslang-design/blob/69c7c47931f8205c1cdf28d5819d675de734bd8e/FSharp-4.5/FS-1053-span.md

let SafeSum (bytes: Span<byte>) =
  let mutable sum = 0
  for i in 0 .. bytes.Length - 1 do 
    sum <- sum + int bytes.[i]
  sum

let TestSafeSum() = 
  // managed memory
  let arrayMemory = Array.zeroCreate<byte>(100)
  let arraySpan = new Span<byte>(arrayMemory);
  SafeSum(arraySpan)|> printfn "res = %d"

  // native memory
  let nativeMemory = Marshal.AllocHGlobal(100);
  let nativeSpan = new Span<byte>(nativeMemory.ToPointer(), 100);
  SafeSum(nativeSpan)|> printfn "res = %d"
  Marshal.FreeHGlobal(nativeMemory);

  // stack memory
  let mem = NativePtr.stackalloc<byte>(100)
  let mem2 = mem |> NativePtr.toVoidPtr
  let stackSpan = Span<byte>(mem2, 100)
  SafeSum(stackSpan) |> printfn "res = %d"
```

[`Memory<'T>`型](https://docs.microsoft.com/en-us/dotnet/api/system.memory-1) は, `Span<'T>` 型の値がスタック上にしか存在できず boxing できなかったりクロージャでキャプチャできなかったり[^capturing]して不便なので, 少しパフォーマンスを犠牲にしてそれらを可能にするための型です.

[^capturing]: これは computation expressions の内部で使うことも含む

Byrefs と `byref`-like structs について詳しくは [MSDN のドキュメント](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/byrefs), `Span<'T>` の嬉しみは [ufcpp さんのブログ記事(C#での解説)](https://ufcpp.net/study/csharp/resource/span/) などを参照してください.

[^lifetime]: ライフタイムが有限かつ静的に決まらないといけない. つまり関数の引数やローカル変数には使えるが, クラスのメンバ変数などには使えない.

#### P/Invoke (FFI)

.NET は **P/Invoke** というネイティヴライブラリとの FFI 機構を搭載していて, F# からも使うことができます.

例えば, libc の `getpid` を呼びたい場合は次のように書きます.

```fsharp
open System.Runtime.InteropServices

[<DllImport("libc")>]
extern int getpid()

// 普通の関数のように使う
getpid() |> printfn "%i"
```

これは F# の機能ではなく .NET の機能なので, 詳しくは [MSDN を参照](https://docs.microsoft.com/en-us/dotnet/framework/interop/consuming-unmanaged-dll-functions) するなどしてください.

#### Active Patterns

[**アクティブパターン(Active Patterns)**](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/active-patterns) はパターンマッチで自作のパターンを定義することができる機能です. スマートコンストラクタ(実行時チェックの)と対比して "スマートデストラクタ" とでも言うべき機能です. 

Active patterns は定義したいケース名を `(|Name1|Name2|...|NameN|)` のように並べたものを変数名とした関数として定義され, その最後の引数でパターンマッチ時の分解対象を受け取ります. そして, それらのケース名を代数的データ型のコンストラクタようにみなして, それで作った値を返すことでケースの場合分けを行います.

これは実例を見たほうが分かりやすいので簡単なものを書いてみます. 例えば

```fsharp
let (|S|Z|) i =
  if i = 0u then Z else S (i-1u)
```

と書けば,

```fsharp
match 42u with
| Z -> printfn "zero"
| S n -> printfn "%i + 1" n
```

のようにパターンマッチ内で自作のケースを使うことができるようになります. なお, 網羅性検査もしてくれます. 加えて,

```fsharp
type nat = uint32
let S n = n + 1u
let Z = 0u
```

と型エイリアスとスマートコンストラクタを定義してあげれば, ただの unsigned int を

```fsharp
type nat = S of nat | Z
```

のようなデータ型と全く同じように扱うことができます.

```fsharp
let rec fact = function
  | Z -> S Z
  | S n -> S n * fact n
// val fact : _arg1:uint32 -> uint32
```

また, 網羅性検査ができなくなる代わりに, マッチすることもしないこともあるケースを定義できる partial active patterns もあります. この場合, 戻り値は `option` に包まれた形になります.

```fsharp
let (|Fizz|_|) n = if n % 3 = 0 then Some Fizz else None
let (|Buzz|_|) n = if n % 5 = 0 then Some Buzz else None

let fizzbuzz = function
  | Fizz & Buzz -> "fizzbuzz"
  | Fizz -> "fizz" | Buzz -> "buzz"
  | x -> sprintf "%i" x
```

引数を増やすと, パターンマッチの分解対象以外にもパラメータを受け取ることができます(parameterized active patterns).

```fsharp
let (|Mod|_|) n m = if m % n = 0 then Some Mod else None

let fizzbuzz2 = function
  | Mod 3 & Mod 5 -> "fizzbuzz"
  | Mod 3 -> "fizz" | Mod 5 -> "buzz"
  | n -> sprintf "%i" n
```

パターンマッチの分解対象は最後の変数で受け取ることに注意してください. またパターンマッチ内で使うときは active patterns の定義と同様にパラメータを先に渡し, 最後の引数に相当する場所で分解結果を束縛します. このため普通の代数的データ型に対するマッチからは逸脱した構文になります(パラメータの部分は curried form になる).

```fsharp
let (|Log|) a x = System.Math.Log(a, x)

match 16.0 with Log 2.0 x -> printfn "log_2 16 = %g" x
// log_2 16 = 4
```

アクティブパターンも通常の代数的データ型のデコンストラクタと同様に, パターンマッチ以外に関数の引数や let 束縛でも使うことができます.

```fsharp
let (Log 2.0 x) = 16.0
// val x : float = 4.0

let (|DefaultValue|) defVal xo =
  match xo with Some x -> x | None -> defVal
// val inline ( |DefaultValue| ) : defVal:'a -> xo:'a option -> 'a

let f (DefaultValue 0 x) (DefaultValue 0 y) = x + y
// val f : int option -> int option -> int

f (Some 4) None |> printfn "%i" // 4
```

## Further Reading

### [F# for fun and profit](https://fsharpforfunandprofit.com/site-contents/) 

この記事中でも何個かリンクを貼っていますが, F# の機能を1つ1つ紹介するシリーズやその他様々な話題を集めた内容が濃いサイトです.

チュートリアル的なシリーズ記事はどちらかというと既に C# などのオブジェクト指向メインの言語を知っていて F# をやってみたい人向けに書かれています.

しかし, [property-based testing](https://fsharpforfunandprofit.com/posts/property-based-testing/) の記事など, 既に ML 系言語に慣れている人向けの話題もあります.

特に, **[Troubleshooting F#](https://fsharpforfunandprofit.com/troubleshooting-fsharp/) は必見です！** F# でよくやってしまう間違いとその対処法が網羅されています.

### [Wikibooks/F# Programming](https://en.wikibooks.org/wiki/F_Sharp_Programming)

F# for fun and profit よりはお硬い感じですが, 内容の充実度とわかりやすい実行例でわたしはお気に入りのサイトです.

わたしは MSDN を見る元気がないときにリファレンスとしてよく使います.

### MSDN

[^msdn]: 一部は "Microsoft Docs" であって "MSDN" ではないのですが, [MSDN ですね. どこがとはいいませんが.](https://docs.microsoft.com/ja-jp/dotnet/fsharp/)

言わずと知れた公式ドキュメントです. *MSDN なので*[^msdn]英語版前提で話を進めます.

MSDN は歩き方を知っていないと, どこ見れば欲しい情報が乗ってるのかわからなく迷子になりがちなので[^maigo], 軽く解説します.

[^maigo]: docs.microsoft.com と msdn.microsoft.com に分裂しててリンクの貼られ具合によって行ったり来たりする. そのたびにページのレイアウトが変わるため迷子になりやすい

* [F# Language Reference](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/) ... F# の構文や言語仕様自体に関する解説

* [F# style guide](https://docs.microsoft.com/en-us/dotnet/fsharp/style-guide/) ... コード整形やよく使う手法などを集めたガイドライン

* [F# Core Library Reference](https://msdn.microsoft.com/en-us/visualfsharpdocs/conceptual/fsharp-core-library-reference) ... F# の標準ライブラリのリファレンス

* [Microsoft.FSharp.Core](https://msdn.microsoft.com/en-us/visualfsharpdocs/conceptual/microsoft.fsharp.core-namespace-%5bfsharp%5d) ... 組み込み型や組み込み関数など, 何も open してないのに最初から使えるものはだいたいここに入っている

* [Core.Operators](https://msdn.microsoft.com/en-us/visualfsharpdocs/conceptual/core.operators-module-%5bfsharp%5d), [Core.ExtraTopLevelOperators](https://msdn.microsoft.com/en-us/visualfsharpdocs/conceptual/core.extratopleveloperators-module-%5bfsharp%5d) ... デフォルトで定義済の演算子が見られる(実際に演算子が定義されているモジュールのリファレンス)