F#楽しいですよ

こんなこと言ってる自分も勉強中の一人だけど。
でももっとF#の知名度上がったらいいなーと思って、Twitterとかもくもく会とかでF#の事言いまくってる。

##楽しいところ
F#はパズル組んでるみたいで書いててとても楽しい。
定数しか使えないという制約の元で、いかに読みやすくバグなく書けるか、めっちゃ頭をひねるので。[^1]

[^1]: 他の関数型言語でも同じやろうけど

高階関数と「|>」を使うとさらっと書けて、読みやすいコードが書ける。[^2]
[^2]: 個人差あり、最初は慣れが必要かも
[ポーカーの役判定を高階関数縛りで書いてた時](https://qiita.com/aoi_erimiya/items/efc6b2621603bfb658cb)とかも超楽しかった。


あとは以下みたいな悪しきコードが、すばらしく簡潔になったりしたときの感動はひとしお。[^3]

[^3]: インデックスアクセス怖い。でもList.removeがないと知ったときは、これしか思いつかなかった

```fsharp

// 特定の要素を取り除いた新しいリストを取得したい
// ※player.HandCardsはCard型のlistである
let askCardIndex =
(player.HandCards |> List.tryFindIndex(fun x -> (isKnown askCard x))).Value

List.append player.HandCards.[.. askCardIndex - 1] player.HandCards.[askCardIndex + 1 ..]

// ↓↓↓ 以下の1行だけで実現できる ↓↓↓
player.HandCards |> List.except [askCard]
```

F#について詳しく知りたい方は以下の記事が非常に良いと思います。
[F# を知ってほしい](https://qiita.com/cannorin/items/59d79cc9a3b64c761cd4)
[仕事と F# と私](https://qiita.com/u_1roh/items/eb90672fc40fc7f2ea86)

##ローカルの実行環境
個人的には.NET Core + vscode + ionide(プラグイン)でいいと思う。
大事なのは最新版のdotnetcoreを入れる事(執筆時点では3.1.1)。
2.1とか古いままにしてると、以下のionideの超嬉しい機能が使えなかったりする。

ionideは各行で型評価して出力してくれる
↓↓↓
![D584EC29-ED44-4B55-9804-EECFBE943646.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270579/c4bc2c40-615b-31e0-0d37-a27445ad5a2a.png)

冗長なコードをこう書き替えたら？とか聞いてくるのでとても賢い。
↓↓↓

```fsharp:修正案の例
// 書いてたやつ
[1..10] |> List.sortBy(fun _ -> Guid.NewGuid()) |> List.head

// ionideが提示した修正案
[1..10] |> List.minBy(fun _ -> Guid.NewGuid())
```

##オンライン開発環境
いろんな事情があってローカルで環境構築できない場合は、ここがインテリセンス効くので最強。
https://fable.io/repl/

記事の執筆時点当初では、まだ全機能がリリースされてるわけではなかったけど
メンバーの方が「リリースしたよ！」って報告してくれました。
Thanks！
https://github.com/fable-compiler/repl/issues/105
↓↓↓
ただ、Console.readLine()とか、どうしても一部動かせない処理は存在する。

こちらはインテリセンスは効かないけど、4.0相当の機能は確実に実行できる。
https://repl.it/languages/fsharp

てなわけで、fable.ioでコーディングして
実行できない関数にぶちあたってしまった場合はrepl.itでコンパイルすると吉。

##ドキュメント
[msdn](https://msdn.microsoft.com/ja-jp/visualfsharpdocs/conceptual/system.collections-namespace-%5bfsharp%5d)はドキュメントが古い。でも一覧性はとても良い。
なのでインテリセンスでどんな候補があるか見て、気になったやつをググるが確実っぽい。

自分はListとかの高階関数使うので、見るのはこれ。
[こんなにあった... F#ドキュメントには未掲載のArray関数](https://qiita.com/mokuba/items/50577c900afcc45aa6fa)
[これは知らなかった... F#ドキュメントには未掲載のコレクション関数(List編)](https://qiita.com/mokuba/items/ef7c8da59385a3198988)

あとは人が書いたF#のコード眺めてると、未知の記法がある事に気付いて輸入できたりする。

O'Reillyの[プログラミングF#](https://www.oreilly.co.jp/books/9784873114668/)を読みながら、徐々に理解を広げていってる感じ。

##個人的に詰まったところの覚え書き(主に文法面)
###型の注釈をどう入れるか
  * リストはType名 listの形で定義

```fsharp
// OK
let buildPlayer (handCards:Card list) =
    ...

// NG(別モノになってしまう)
let buildPlayer (handCards:List<Card>) =
    ...

```

  * 複数の引数に注釈入れる場合は()でそれぞれ囲む

```fsharp
// OK
let phase (player:Player) (enemy:Player) =
  ...

// 文法的にはOKだけどこれだとタプルを引数でもらうことになってしまう（カリー化できない)
let phase (player:Player, enemy:Player) =
  ...

// NG(シンタックスエラー)
let phase player:Player enemy:Player =
  ...

// OK 片側だけならこんな感じ
let rebuildAskPlayer (player:Player) askAfterHandCards =
　...
```

###意味のない変数をどうするか
  * _を使うと警告がでない

```fsharp

let shuffledCards =
  cardList
  |> List.sortBy(fun _ -> Guid.NewGuid())
```

###タプルの戻り値
  * まとめてletできる

```fsharp
// xには1が　yには7が
let x, y = (1, 7)
```

###たまに戻り値で返ってくるOptionってなに
  * Some(値)かNoneが入ってる

```fsharp
// 値があるならSomeの中にValueがある
let x = Some(1)
printfn "%A" x.Value
// => 1

// Noneを明示的に入れたらインテリセンスが.Valueを候補として出してくれない。賢い
let y = None
printfn "%A" y
// => null
```

###ifは式
 * 分岐で得られた値を束縛できる
 
```fsharp
let result = if playerCard.Number > enemyCard.Number then High else Low
// => resultはHighかLowが束縛される
```