F# 5.0の新機能

# はじめに

2020/11/11(水)にリリースされた F# 5.0 の新機能について簡単に紹介します。
抜け漏れ誤りがあったらごめんなさい🙇

※現在まだプレビューの機能については触れません

# F# 5.0 新機能

## F#スクリプトでのNugetパッケージ参照

今までF#スクリプトではNugetパッケージを直接参照することができませんでした。
それがF# 5.0から可能になりました。

```fsharp
#r "nuget:Flavedo.Zest"
open Flavedo.Zest
// do something
```

詳しくは **[こちらの記事](https://qiita.com/Midoliy/items/75ef70b701eae5df83aa)** を参照してください

## String Interpolation

**[RFC FS-1001](https://github.com/fsharp/fslang-design/blob/master/FSharp-5.0/FS-1001-StringInterpolation.md)**

C# でいうところの **挿入文字列リテラル** が F# にも追加されました。
これは文字列リテラル中に値を挿入できるというものです。

```fsharp:定義
$"文字列 {挿入したい値} "
```

```fsharp:サンプル
let name = "Midoliy"
let age = 31
let message = $"{name} is {age} years old."
printfn "%s" message
```
> Midoliy is 31 years old.

また、**sprintf関数** と同様にフォーマット指定子を利用することで挿入する値の型を指定することが可能です。
フォーマット指定子を利用することで **printf関数** や **sprintf関数** と同様、コンパイル時に意図しない型が挿入されることを防ぐことができます。

```fsharp:定義
$"文字列 フォーマット指定子{挿入したい値} "
```

```fsharp:サンプル
let name = "Midoliy"
let age = 31
let message = $"%s{name} is %d{age} years old."
printfn "%s" message
```
> Midoliy is 31 years old.



## nameof

**[RFC FS-1003](https://github.com/fsharp/fslang-design/blob/master/FSharp-5.0/FS-1003-nameof-operator.md)**

C# の **nameof式** と同様のものが F# にも追加されました。
nameofを使うことで変数・型・メンバーなどの名前が文字列定数として生成されます。

```fsharp:定義
nameof 対象
```

```fsharp:サンプル
let f x = nameof x
let y = 10

printfn "%s" (f y)
printfn "%s" (nameof y)
printfn "%s" (nameof int)
```
> x
> y
> int

この機能はいろいろな利用シーンがありますが、例えば WPF のバインディングに必須な INotifyPropertyChanged を実装する際、文字列リテラルを直指定ではなく C# と同様に nameof式 で指定することが可能になったりします。

```fsharp:サンプル
open System.ComponentModel

type Rectangle () =
    let mutable height = 0f
    let mutable width = 0f
    let propertyChanged = Event<_, _>()

    interface System.ComponentModel.INotifyPropertyChanged with
        [<CLIEventAttribute>]
        member __.PropertyChanged = propertyChanged.Publish

    member __.Height
        with get() = height
        and set(value) = 
            height <- value
            propertyChanged.Trigger(__, PropertyChangedEventArgs(nameof height))
            
    member __.Width
        with get() = width
        and set(value) = 
            width <- value
            propertyChanged.Trigger(__, PropertyChangedEventArgs(nameof width))

    member __.Area with get() =
        height * width
```



## Open Type declarations

**[RFC FS-1068](https://github.com/fsharp/fslang-design/blob/master/FSharp-5.0/FS-1068-open-type-declaration.md)**

C# でいうところの **using static ディレクティブ** に似た機能が F# にも追加されました。
Open Type declarations は型名を指定せずに静的メンバにアクセスできるだけでなく、特定のモジュール内にある判別共用体やenum、レコードなどにアクセスすることが可能となっています。

```fsharp:定義
open type 型名
```

```fsharp:サンプル1
open type System.Math

let x = Min(1.0, 2.0)
printfn $"%f{x}"
```
> 1.000000

```fsharp:サンプル2
module M =
    type Foo = { X:int; Y:int; }
    type Bar = { X:int; Y:int; }

// open type を利用した場合
open type M.Foo

let foo = { X = 10; Y = 20; }
printfn $"%A{foo} : %A{foo.GetType()}"
```
> { X = 10; Y = 20 } : Program+M+Foo

```fsharp:サンプル3
module M =
    type Foo = { X:int; Y:int; }
    type Bar = { X:int; Y:int; }

// 通常の open を利用した場合
open M

let v = { X = 10; Y = 20; }
printfn $"%A{v} : %A{v.GetType()}"
```
> { X = 10; Y = 20 } : Program+M+Bar



## Sliceの機能強化

### 組み込みデータ型に対してスライスした際の挙動を統一

**[RFC FS-1077](https://github.com/fsharp/fslang-design/blob/master/FSharp-5.0/FS-1077-tolerant-slicing.md)**

```fsharp:F#5以前
let list = [ 0..10 ]
let array = [| 0..10 |]
let str = "hello!"

list.[-2..(-1)] |> printfn("%A")
array.[-2..(-1)] |> printfn("%A")
str.[-2..(-1)] |> printfn("%A")
```
> [ ]
> throw exception
> throw exception

```fsharp:F#5
let list = [ 0..10 ]
let array = [| 0..10 |]
let str = "hello!"

list.[-2..(-1)] |> printfn("%A")
array.[-2..(-1)] |> printfn("%A")
str.[-2..(-1)] |> printfn("%A")
```
> [ ]
> [| |]
> ""


### 3次元・4次元配列に対するスライスの強化

**[RFC FS-1077](https://github.com/fsharp/fslang-design/blob/master/FSharp-5.0/FS-1077-3d-4d-fixed-index-slicing.md)**

以下のような3次元配列を考えます。

`z=0`

| xy | 0 | 1 |
|:---:|:---:|:---:|
| 0 | 0 | 1 |
| 1 | 2 | 3 |

`z=1`

| xy | 0 | 1 |
|:---:|:---:|:---:|
| 0 | 4 | 5 |
| 1 | 6 | 7 |

このときの `y = 0, z = 1` の `x行 = 4, 5` をスライスで簡単に取得できるようになりました。

```fsharp:サンプル
let dim = 2
let m = Array3D.zeroCreate<int> dim dim dim

let mutable cnt = 0

for z in 0..dim-1 do
    for y in 0..dim-1 do
        for x in 0..dim-1 do
            m.[x,y,z] <- cnt
            cnt <- cnt + 1

// F# 5.0 から以下のようなスライスが可能に！
m.[*, 0, 1]
|> printfn "%A"
```
> [| 4; 5 |]



## F# Quotationsの機能改善

**[RFC FS-1071](https://github.com/fsharp/fslang-design/blob/master/FSharp-5.0/FS-1071-witness-passing-quotations.md)**

F# Quotationsは強力な機能ですが、SRTP制約が課されている場合などでそのセマンティクスを十分に表すような呼び出しが行われていませんでした。
F# 5.0となりその機能が改善されました。

```fsharp:サンプル
open FSharp.Linq.RuntimeHelpers

let eval q = LeafExpressionConverter.EvaluateQuotation q

let inline negate x = -x
// negateは以下のようなシグニチャを持つ関数です。
//    val inline negate: x: ^a ->  ^a when  ^a : (static member ( ~- ) :  ^a ->  ^a)


<@ negate 1.0 @>  |> eval
// 上記のコードはnegateに課せられているSRTP制約を守っていますが、以前のバージョンでは例外が発生していました。
// F# 5.0からはこのコードが有効となります。
```


## Applicative Computation Expressions

**[RFC FS-1063](https://github.com/fsharp/fslang-design/blob/master/FSharp-5.0/FS-1063-support-letbang-andbang-for-applicative-functors.md)**

「すべての処理が独立」しており「その結果を蓄積させる」だけのコンピュテーション式を作成する場合に、より高パフォーマンスとなる機能が追加されました。
この機能はライブラリ作成者向けの機能ですが、利用者側にとっても今までのコンピュテーション式となんら変わらない使い心地のままパフォーマンスが上がるので両者にメリットがある機能アップです。

大きく分けると **"BindReturn"** と **"MergeSources"** の2種類が追加されました。

### BindReturn

F＃ 4.7 までは `let!` と `return` を別々に定義していましたが、これを1つにまとめたようなものが `BindReturn` です。

```fsharp:F#4.7まで
type OptionBuilder() =
  member _.Bind(x, f) = Option.bind f x
  member _.Return(x) = x
  
let option = OptionBuilder()
  
let r : int option = 
  option {
    let! a = Some 10
    return a
  }
  
printfn $"%A{r}"
```
> Some 10

```fsharp:F#5.0から
type OptionBuilder() =
  member _.BindReturn(x, f) = Option.map f x
  
let option = OptionBuilder()
  
let r : int option = 
  option {
    let! a = Some 10
    return a
  }
  
printfn $"%A{r}"
```
> Some 10

厳密には上記のコードは同じではありません。
たとえば、`BindReturn` のみを定義した場合には2個目の `let!` を利用することはできません。

```fsharp:BindReturnのみの定義ではダメな例
let r : int option =
  option {
    let! a = Some 10
    let! b = Some 20  // コレはダメ！Bindを定義する必要あり
    return a + b
  }
```

### MergeSources

「独立した処理」を表現するための構文が **`BindReturn`** であるならば、「結果を蓄積する」ための構文が **`MergeSources`** になります。
結果を蓄積させるために **`and!`** を利用する必要があることに注意してください。

```fsharp:F#5.0から
type OptionBuilder() =
  member _.BindReturn(x, f) = Option.map f x
  member _.MergeSources(x, y) = (x, y) ||> Option.map2 (fun x y -> (x, y))

let option = OptionBuilder()

let r : int option = 
  option {
    let! a = Some 10
    and! b = Some 20
    return a + b
  }

printfn $"%A{r}"

let r2 : int option = 
  option {
    let! a = Some 10
    and! b = None
    return a + b
  }

printfn $"%A{r2}"
```
> Some 30
> None


## .NET相互運用の改善

C# に追加されているインターフェースのデフォルト実装への対応やnull許容型との相互運用性の向上などが行われました。

### 異なる型でのジェネリックインターフェース実装

**[RFC FS-1031](https://github.com/fsharp/fslang-design/blob/master/FSharp-5.0/FS-1031-Allow%20implementing%20the%20same%20interface%20at%20different%20generic%20instantiations%20in%20the%20same%20type.md)**

異なる型で同じインターフェースを実装できるようになりました。

```fsharp
type IFoo<'T> =
  abstract member Function : 'T -> 'T

type Foo() =
  interface IFoo<int> with
    member _.Function x = x + x

  interface IFoo<string> with
    member _.Function s = $"string: %s{s}"

let foo = Foo()
printfn $"%d{ (foo :> IFoo<int>).Function(10) }"
printfn $"""%s{ (foo :> IFoo<string>).Function("abc") }"""
```
> 20
> string: abc



### インターフェースのデフォルト実装

**[RFC FS-1074](https://github.com/fsharp/fslang-design/blob/master/FSharp-5.0/FS-1074-default-interface-member-consumption.md)**

C# で書かれたインターフェースにデフォルト実装がなされている場合に、F# でもそのデフォルト実装されたものを呼び出すことができるようになりました。

```cs:C#側
using System;

namespace Sample
{
    public interface IFoo
    {
        int Value => 100;
    }
}
```

```fsharp:F#側
open Sample

type Foo() =
  interface IFoo

let foo = Foo() :> IFoo
printfn $"%d{foo.Value}"
```
> 100



### null許容型との相互運用性の改善

**[RFC FS-1075](https://github.com/fsharp/fslang-design/blob/master/FSharp-5.0/FS-1075-nullable-interop.md)**

今まで Nullable でラップしなければならなかったところが必要なくなりました。
特に F# から C# のライブラリを利用する場合に便利になります。

```cs:C#側
using System;

namespace Sample
{
    public class Foo
    {
        public int Function(int? x)
            => (x ?? 0) * 100;
    }
}
```

```fsharp:F#側
open Sample

let foo = Foo()
// Nullable<int> でラップしなくても良い
let x = foo.Function(100)
printfn $"%d{x}"
```
> 10000


## その他

### float32

**[RFC FS-1080](https://github.com/fsharp/fslang-design/blob/master/FSharp-5.0/FS-1080-float32-without-dot.md)**

今まではfloat32型を定義する場合に "." が必要でしたが、F# 5.0からはサフィックスのみでよくなりました。

```fsharp:F#5以前
let x = 123.f
```

```fsharp:F#5
let x = 123f
```


### uint32

**[RFC FS-1082](https://github.com/fsharp/fslang-design/blob/master/FSharp-5.0/FS-1082-uint-type-abbreviation.md)**

int32 は int と省略できますが、uint32 は uint と省略できませんでした。
F# 5.0からはそれが可能となりました。

```fsharp:F#5以前
let f (x:uint32) = x * 10
```

```fsharp:F#5
let f (x:uint) = x * 10
```


# おわりに

この他にも現在プレビュー中の機能があったりして、今後も目が離せないですね！
今回の F# 5.0 に関するアナウンスは **[ココで](https://devblogs.microsoft.com/dotnet/announcing-f-5/)** 確認すると良いと思います。