F#の例外


組み込み例外以外もちゃんと使うために、例外のお勉強。

* .NETの例外ももちろん使える
* try-catch-finallyではなくtry-withとtry-finallyに分ける
* オリジナルの例外作成がお手軽すぎてマジで凄い

##F#組み込みの例外
F#で簡単に例外起こすにはfailwith、failwithf、invalidArg関数がある。

```fsharp
let divide x y = 
    if y = 0 then failwith "Zero divide!!"
    x / y

divide 10 0 |> printfn "%A"
```


```txt
Unhandled Exception:
System.Exception: Zero divide!!
  at <StartupCode$main>.$Main.main@ () [0x00036] in <5e4f8a354fc9e525a7450383358a4f5e>:0
[ERROR] FATAL UNHANDLED EXCEPTION: System.Exception: Zero divide!!
  at <StartupCode$main>.$Main.main@ () [0x00036] in <5e4f8a354fc9e525a7450383358a4f5e>:0
```

##.NETの例外を投げる
.Netで適切な例外あるなら、そっちをraise関数で投げるべき。

```fsharp
let divide x y = 
    if y = 0 then raise <| new System.DivideByZeroException()
    x / y

divide 10 0 |> printfn "%A"
```

```txt
Unhandled Exception:
System.DivideByZeroException: Attempted to divide by zero.
  at <StartupCode$main>.$Main.main@ () [0x00036] in <5e4f8ae34fc9e525a7450383e38a4f5e>:0
[ERROR] FATAL UNHANDLED EXCEPTION: System.DivideByZeroException: Attempted to divide by zero.
  at <StartupCode$main>.$Main.main@ () [0x00036] in <5e4f8ae34fc9e525a7450383e38a4f5e>:0
```

##try-catchではなくtry-with
try-catchはtry-withで表現する。

```fsharp
let multi x y = 
    if x = 0 || y = 0 then raise <| new System.DivideByZeroException()
    if x > 1000000 then raise <| new System.OverflowException("x")
    x * y

try
  multi 1000001 103 |> printfn "%A"
with
  | :? System.DivideByZeroException
    -> printfn "That's zeke!"
  | :? System.OverflowException as e
    -> printfn "Nice boat %s!" e.Message
  | _ as e
    -> printfn "other!:%s" e.Message
```

```txt
Nice boat x!
```

##try-catch-finallyはないのでtry-finalyとtry-withに分ける

```fsharp
let multi x y = 
    if x = 0 || y = 0 then raise <| new System.DivideByZeroException()
    if x > 1000000 then raise <| new System.OverflowException("x")
    x * y

let finalyTest =
  try
    multi 1000001 103 |> printfn "%A"
  finally
    printfn "Final...Fusion!!"

try
  finalyTest
with
  | :? System.DivideByZeroException
    -> printfn "That's zeke!"
  | :? System.OverflowException as e
    -> printfn "Nice boat %s!" e.Message
  | _ as e
    -> printfn "other!:%s" e.Message
```

```txt
Final...Fusion!!

Unhandled Exception:
System.OverflowException: x
  at Main.multi (System.Int32 x, System.Int32 y) [0x00027] in <5e4f8f154fc9e525a7450383158f4f5e>:0
  at <StartupCode$main>.$Main.main@ () [0x00000] in <5e4f8f154fc9e525a7450383158f4f5e>:0
[ERROR] FATAL UNHANDLED EXCEPTION: System.OverflowException: x
  at Main.multi (System.Int32 x, System.Int32 y) [0x00027] in <5e4f8f154fc9e525a7450383158f4f5e>:0
  at <StartupCode$main>.$Main.main@ () [0x00000] in <5e4f8f154fc9e525a7450383158f4f5e>:0
```

##例外の再スロー
再スローはこうする。
finallyにはもちろんreraise()は書けない。

```fsharp
let multi x y = 
    if x = 0 || y = 0 then raise <| new System.DivideByZeroException()
    if x > 1000000 then raise <| new System.OverflowException("x")
    x * y

let finalyTest =
  try
    multi 1000001 103 |> printfn "%A"
  with
  | _ as e
    -> reraise()

try
  finalyTest
with
  | :? System.DivideByZeroException
    -> printfn "That's zeke!"
  | :? System.OverflowException as e
    -> printfn "Nice boat %s!" e.Message
  | _ as e
    -> printfn "other!:%s" e.Message
```

```
Unhandled Exception:
System.OverflowException: x
  at Main.multi (System.Int32 x, System.Int32 y) [0x00027] in <5e4f901e4fc9e525a74503831e904f5e>:0
  at <StartupCode$main>.$Main.main@ () [0x00032] in <5e4f901e4fc9e525a74503831e904f5e>:0
[ERROR] FATAL UNHANDLED EXCEPTION: System.OverflowException: x
  at Main.multi (System.Int32 x, System.Int32 y) [0x00027] in <5e4f901e4fc9e525a74503831e904f5e>:0
  at <StartupCode$main>.$Main.main@ () [0x00032] in <5e4f901e4fc9e525a74503831e904f5e>:0
```

```txt:もし間違ってfinallyにreraise()書いたら
error FS0413: Calls to 'reraise' may only occur directly in a handler of a try-with
```

##オリジナルの例外をお手軽に作る
withのところで:?を入れないのがポイント。

```fsharp
exception NoPower
exception LowPower of int * string

let run power =
  try
    if power < 1 then
      raise <| NoPower
    if power < 10 then
      raise <| LowPower(power, "Low power. Please refill!")

    printfn "last:%d" power
  with
    | NoPower
      -> printfn "No power!"
    | LowPower(power, message)
      -> printfn "last:%d %s" power message

run 5
```

```txt
last:5 Low power. Please refill!
```

一見わかりにくいけど、もし:?を付けてしまうと文法エラーになる。

```fsharp:ダメな例
    | :? LowPower(power, message)
      -> printfn "last:%d %s" power message
```

```txt:こうなる
error FS0010: Unexpected symbol '(' in pattern matching. Expected '->' or other token.
```

また、raiseを忘れるとこんな感じのエラーになる。

```fsharp:ダメな例
  try
    if power < 1 then
      NoPower
    if power < 10 then
      raise <| LowPower(power, "Low power. Please refill!")
```

```txt
error FS0001: This 'if' expression is missing an 'else' branch. Because 'if' is an expression, and not a statement, add an 'else' branchwhich also returns a value of type 'exn'.
```

内部的にはexn型っていうのか。