このletは何をしているのか?

F#はMicrosoftの[Don Syme](https://twitter.com/dsyme "https://twitter.com/dsyme")氏が開発したプログラミング言語で、命令型、オブジェクト指向、関数型の要素を併せ持つマルチパラダイム言語と[位置付けられています](https://msdn.microsoft.com/visualfsharpdocs/conceptual/visual-fsharp "https://msdn.microsoft.com/visualfsharpdocs/conceptual/visual-fsharp")。実装はオープンソースの[MITライセンス](https://github.com/fsharp/fsharp/blob/master/License.txt "https://github.com/fsharp/fsharp/blob/master/License.txt")のもと、[githubのリポジトリ](https://github.com/fsharp/fsharp "https://github.com/fsharp/fsharp")で公開されています。

実行環境は.NET Frameworkおよび.NET CoreやMonoといったマルチプラットフォームに対応していますが、サポート状況については各プラットフォームごとの情報を参照してください(「[F# の概要](https://docs.microsoft.com/ja-jp/dotnet/fsharp/get-started/index "https://docs.microsoft.com/ja-jp/dotnet/fsharp/get-started/index")」など)。

なお、ここで対象とするF#のバージョンは4.1([FSharp.Core](https://github.com/fsharp/fsharp/tree/master/src/fsharp/FSharp.Core "https://github.com/fsharp/fsharp/tree/master/src/fsharp/FSharp.Core") 4.4.1.0)とします。

# letは定義するもの
F#の[let](https://docs.microsoft.com/ja-jp/dotnet/fsharp/language-reference/functions/let-bindings "https://docs.microsoft.com/ja-jp/dotnet/fsharp/language-reference/functions/let-bindings")は変数や関数を定義するのに使われます。どこに出てくるかによって役割が変わることがあります。また、`let`に追加するキーワードによっても定義されるものの使われ方が変わります。

変数に値を設定するとき、そのあとに値を変更できるようにするには`let mutable`としなければなりません。値を変更するときは`=`ではなく`<-`を使います。

```fsharp:letによる定義
(* 変数の定義 *)
let a = 1    // 変数に値を設定(値の変更不可)
let mutable b = 2    // 値の変更可
b <- 3    // 値を変更
```

関数の定義で`let mutable`を使うときは、関数を`fun args -> ...`のように定義しなくてはなりません。

```fsharp:letによる定義
(* 関数の定義 *)
let f a b = a + b  // 関数の内容を変更できない
f 2 3    // 5
let mutable g = fun a b -> a + b  // 変数の内容を変更できる
g 2 3  // 5
g <- fun a b -> a * b
g 2 3  // 6
```

また、繰り返し処理で使われる再帰関数(自分自身を呼び出す関数)を定義するときは`let rec`で定義します。

```fsharp:recで再帰関数を定義
let rec len n = match n with h :: t -> 1 + len t | [] -> 0  // リストの長さ(要素の数)を数える
len [5; 3; 7]  // 3
```

変数や関数の型を指定するときは、その名称に`:型名`を続けます。

```fsharp:変数や関数の型を限定
let n: int = 3
let f (a: int) (b: int) = a + b
let g: int -> int = fun x -> x * 2
```

`let`では複数の変数それぞれに値を定義できます。また、タプルの各要素の値を別々の変数に設定できます。

```fsharp:letで複数の値を同時に定義
let a, b, c = 3, 1, 2    // a = 3, b = 1, c = 2
let a, b, c = (3, 1, 2)  // a = 3, b = 1, c = 2
```

# letの中にブロック
letは変数や関数を定義するものですが、その中でより複雑な処理の内容を定義するために、2行目以降にインデントをつけて処理のまとまりを表すブロックを定義できます。インデントの深さ(行の書き始めの位置)が同じなら同じ層のブロックとなります。

```fsharp:letで処理の定義
let rec sum n =
  (* 2行目以降にインデントをつけるとブロックになる *)
  match n with
  | h :: t -> h + sum t
  | []     -> 0

sum [5; 3; 7]  // 15 (各要素の合計)
```

# letの中にlet
ブロック中には別の`let`も定義できます。ブロック内で定義された変数や関数はその中のみで使えます。また、ブロック内で最後に得られた結果がそのブロックの処理結果となります。

```fsharp:letの中にlet
(* リストの各要素の平均値を計算する関数 *)
let avg n =
  let rec len n = match n with h :: t -> 1 + len t | [] -> 0
  let rec sum n = match n with h :: t -> h + sum t | [] -> 0
  sum n / len n  // ブロックの最終結果 → この関数の処理結果

avg [5; 3; 7]  // 15

(* avg内のlenやsumはavgの外では実行できない *)
// len [5; 3; 7]
// sum [5; 3; 7]
```

# letでは終われない
こうして複数行にわたって書かれるブロックは`let`で終われないことになっています。そのため、ブロックは`let`の次にそれ以外の処理を実行してから終わるようにします。

```fsharp:letでは終われないブロックの例
let f a b =
  let x = a
  let y = b
  ()  // letでは終われない

for i in {1..5} do
  let a = i
  ()  // letでは終われない

type C(x: int) =
  do
    let a = x
    ()  // letでは終われない

#if INTERACTIVE
#nowarn "25"
#endif
try
  let a = None
  let (Some x) = a  // パターンが一致しない(withブロックへ)
  ()  // letでは終われない
with
  ex -> eprintfn "%s" ex.Message  // "一致条件が不完全でした"
```

# アクティブパターン
[アクティブパターン](https://docs.microsoft.com/ja-jp/dotnet/fsharp/language-reference/active-patterns "https://docs.microsoft.com/ja-jp/dotnet/fsharp/language-reference/active-patterns")は判定の結果を表す識別子を定義するもので、すべての結果に識別子をつけるものと、結果の一部のみに識別子を定義する[パーシャルアクティブパターン](https://docs.microsoft.com/ja-jp/dotnet/fsharp/language-reference/active-patterns#partial-active-patterns "https://docs.microsoft.com/ja-jp/dotnet/fsharp/language-reference/active-patterns#partial-active-patterns")があります。

アクティブパターンは`let`の次に`(|識別子|...|)`で識別子を定義し、右辺で識別子を割り振る処理を定義します。

```fsharp:letによるアクティブパターンの定義
let (|Odd|Even|) n = if n % 2 = 0 then Even else Odd  // アクティブパターンの定義
let a = 1
match a with Odd -> "odd" | Even -> "even"  // "odd"
```

パーシャルアクティブパターンは`let`の次の識別子の定義を`(|識別子|_)`とし、処理の内容を`Some 値`か`None`のどちらかが割り振られるように定義します。

```fsharp:letによるパーシャルアクティブパターンの定義
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 n =
    match n with
    | Fizz s ->
        match n with
        | Buzz t -> s + " " + t  // "Fizz Buzz"
        | _      -> s            // "Fizz"
    | _      ->
        match n with
        | Buzz s -> s         // "Buzz"
        | _      -> string n  // それ以外の数

[1..15] |> List.map fizzbuzz
// ["1"; "2"; "Fizz"; "4"; "Buzz"; "Fizz"; "7"; "8"; "Fizz"; "Buzz"; "11";  "Fizz"; "13"; "14"; "Fizz Buzz"]
```

# 列挙型の識別子と値の変換

`let`と`enum`を使うと、[列挙型](https://docs.microsoft.com/ja-jp/dotnet/fsharp/language-reference/enumerations "https://docs.microsoft.com/ja-jp/dotnet/fsharp/language-reference/enumerations")の識別子と、それに定義づけられた値とを変換できます。

```fsharp:letで列挙型の識別子と値の変換
(* 列挙体の定義 *)
type En =
| A = 1
| B = 2

(* 列挙体の値を変数に設定(以下のletはいずれも等価) *)
let ea = En.A        // 識別子を指定
let ea = enum<En> 1  // 値とジェネリックを指定
let ea: En = enum 1  // 変数に型を指定

ea = En.A  // true
```

# 判別共用体の値を取り出す
`let`には[判別共用体](https://docs.microsoft.com/ja-jp/dotnet/fsharp/language-reference/discriminated-unions "https://docs.microsoft.com/ja-jp/dotnet/fsharp/language-reference/discriminated-unions")の値を取り出す機能もありますが、パターンに当てはまらない場合があるときは「この式のパターン マッチが不完全です」という警告が出ることがあります。

```fsharp:判別共用体の値を取り出す
type DU = A of int
let (A a) = A 1    // a = 1

type DU = A of int * string
let (A (a, b)) = A (1, "uno")  // a = 1, b = "uno"

type DU = A of int | B of string
let (A a) = A 1      // 警告「この式のパターン マッチが不完全です」(Aしか当てはまらない)
let (B b) = B "uno"  // 警告「この式のパターン マッチが不完全です」(Bしか当てはまらない)
```

# mutableにはレコード型にも

`mutable`は`let`だけでなく[レコード型](https://docs.microsoft.com/ja-jp/dotnet/fsharp/language-reference/records "https://docs.microsoft.com/ja-jp/dotnet/fsharp/language-reference/records")にも定義できます。これにより、レコード型の値も後から変更できるようになります。

```fsharp:mutableはレコード型にも定義できる
type R = {mutable a: int; b: string}  // aを変更できるレコード型
let r = {a = 1; b = "uno"}            // レコード型の定義
r.a <- 2  // {a = 2; b = "uno"}       // aの値を変更
```

# 測定単位と型
[測定単位](https://docs.microsoft.com/ja-jp/dotnet/fsharp/language-reference/units-of-measure "https://docs.microsoft.com/ja-jp/dotnet/fsharp/language-reference/units-of-measure")を使うと、数値に付随する単位を加味した計算ができます。測定単位は`[<Measure>] type 単位`のように定義し、単位を持つ数値は`数値<単位>`のように表します。こうした単位が付随する数値の型名は`数値型<単位>`となります。単位は、たとえば`1<m>`を`1
`で割ると`1<m/s>`というように計算の結果が逐次反映されます。

数値に単位をつけるには`1<単位>`を掛けます。逆に数値から単位をはずすには`int`や`float`などでキャスト(型変換)します。

```fsharp:測定単位と型
(* 測定単位の設定 *)
[<Measure>] type m
[<Measure>] type cm
[<Measure>] type s

(* 単位を使った計算 *)
let v = 6.75<m> / 1.5
  // 4.5<m/s>
let a = v / 1.5
        // 3.0<m/s^2>

(* 単位を変換する関数 *)
let m2Cm (n: float<m>)  = n * 100.0<cm/m>
let cm2M (n: float<cm>) = n / 100.0<cm/m>

(* 関数の実行例 *)
m2Cm 1.5<m>     // 150.0 : float<cm>
cm2M 150.0<cm>  // 1.5 : float<m>

(* 単位の付加 *)
let setM n = n * 1.0<m>
setM 1.5  // 1.5<m>

(* 単位の除去 *)
let rejM (n: float<m>) = float n
rejM 1.5<m>  // 1.5
```

# module内のlet
[モジュール](https://docs.microsoft.com/ja-jp/dotnet/fsharp/language-reference/modules "https://docs.microsoft.com/ja-jp/dotnet/fsharp/language-reference/modules")内で`let`により定義された変数や関数は、モジュールの外からでも、モジュールにアクセス可能であれば利用できます。しかしモジュール内で`let private`により定義されたものはモジュールの外からアクセスできません。

```fsharp:module内のlet(モジュール並行)
module MA =
  let a = 1
  let private pa = 3

module MB =
  let b = 2
  let private pb = 4

MA.a  = 1  // true
// MA.pa = 3  にはアクセスできない
MB.b  = 2  // true
// MB.pb = 4  にはアクセスできない
```

```fsharp:module内のlet(モジュール内包)
(* コメント行で示したようにはアクセスできない *)
module MA =
  module MB =
    let b = 2
    let private pb = 4
  let a = MB.b
  // let private pa = MB.pb

let c = MA.a
let d = MA.MB.b
// let pd = MA.MB.pb
```

# クラス内のlet
プライマリコンストラクタを持つ[クラス](https://docs.microsoft.com/ja-jp/dotnet/fsharp/language-reference/classes "https://docs.microsoft.com/ja-jp/dotnet/fsharp/language-reference/classes")では、インスタンスを生成するごとに`type`内にある`let`が実行されます。これは抽象クラスと具象クラスのどちらでも行われます。この`let`ではコンストラクタ変数を使った処理も実行できます。`let`で定義された変数や関数はインスタンスのメンバーもアクセスできます。ただし、これらに対しインスタンスの外部からはアクセスできません。

```fsharp:クラス内のlet
(* プライマリコンストラクタのあるクラスではインスタンス生成時にletが実行される *)
(* 抽象クラス *)
[<AbstractClass>]
type AC(x: int, y: int) =
  let a, b = x, y
  let f a b = a + b
  do
    printfn "%i + %i = %i" a b <| f a b
  abstract member F: int with get

(* 具象クラス(ACを継承) *)
type C(x, y) as self =
  inherit AC(x, y)
  let a, b = (x + 1), (y + 1)
  let f a b = a * b
  do
    printfn "%i * %i = %i" a b <| self.F
  override this.F = f a b

let c = C(2, 3)  // "2 + 3 = 5", "3 * 4 = 12"の表示(doブロックの実行)
c.F  // 12
// c.a, c.b, c.fにはアクセスできない
```

---
ここをご覧いただいた方々の参考になればと思います。