F# Interactive(fsi)で新しいパッケージマネジメントの記法を使う。ついでにCanopyで遊ぶ。

## はじめに
今までfsiではパッケージ管理に`paket`を使う必要があったり、DLLへのパスを明記しないといけなかったり面倒だった。

```fsharp:head.fsx
// ↓パッケージはpaket.dependenciesで別途落としてきてもらわないとけない
#r "packages/FSharp.Data/lib/netstandard2.0/FSharp.Data.dll"
open FSharp.Data

```


`dotnet 3.1.300+`からは`--langversion:preview`オプションで、ドラフトだったfsiでのパッケージマネジメント記法が使えます。
https://docs.microsoft.com/en-us/dotnet/fsharp/tutorials/fsharp-interactive/#package-management-in-f-interactive

```fsharp:head.fsx
// DLと参照をよしなにやってくれる
#r "nuget: FSharp.Data"
open FSharp.Data

```

というわけで早速試しましょう！！

## Canopyで試す

What's Canopy? →https://lefthandedgoat.github.io/canopy/
公式を参考にしつつ、Googleでボーダーコリーを検索するテストコードを書きます。
ボーダーコリー、賢いし可愛いので。

```fsharp:search-collie.fs
#r "nuget: Selenium.WebDriver"
#r "nuget: canopy"
#r "nuget: Selenium.WebDriver.ChromeDriver, 83.0.0"

open canopy
open canopy.runner.classic
open canopy.configuration
open canopy.classic
open canopy.reporters
open System

let driverDir = System.Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + "\\.nuget\\packages\\selenium.chrome.webdriver\\83.0.0\\driver"
canopy.configuration.chromeDir <- driverDir
//せっかくなのでレポートも出力する
reporter <- new JUnitReporter("./TestResults.xml")


//start remote
start chrome

"Google検索でボーダーコリーを検索" &&& fun _ ->
    //検索ページのURLを開く
    url "https://www.google.co.jp/"
    //検索ボックスに文字を入力
    "input[name='q']" << "ボーダーコリー"
    //検索ボタンをクリック
    click "Google 検索"

run()
quit()

```

注意点として、
公式ではchromeDirに`System.AppContext.BaseDirectory`を使っていますが、このスクリプトはfsiが実行するため、
`System.AppContext.BaseDirectory = fsi実行ファイルがあるディレクトリ`になってしまいます。
※公式はdotnet CLIがビルド時にchromedrive.exeを実行ファイルと同じとこに置いてくれるので動く

真っ先に思いついたのはスクリプトと同じディレクトリにchromedriver.exeを置いて、
ビルトイン変数の`__SOURCE_DIRECTORY__`を使うことですが、それだとコード上でバージョン管理ができません。

いろいろ調べてみると、
`#r "nuget: ~"`の記法で書くとF#5.x(もしくはdotnet)はパッケージを`~/.nuget/packages`に展開するようなので、
`$env:USERPROFILE\\.nuget\\packages\\selenium.chrome.webdriver\\{version}\\driver"`を直書きして
ドライバーを参照させています。

他にいいやり方を知っている人がいたら、ぜひ教えて下さい。
ちなみに、previewなのでこのあたりの挙動は正式リリースで変わるかもしれません。


話を戻して、先程書いたプログラムを動かします。

```powershell
dotnet fsi --langversion:preview ./search-collie.fs
```

動きました！！(スクショ取り忘れ)(君の目で確かめてくれ)
もうこれで.fsxファイル単体を配布するだけでみんな幸せです。
さよならpaketくん…君のことは忘れないよ…

ちなみに`--langversion:preview`は.NET5でF#5.xが正式リリースされたら不要になるらしいです。
.NET5が待ち遠しいですね。


## 余談
その他の注意点として、
csv.Load等で相対的にファイルパスを書いた場合、作業用に作られるテンポラリディレクトリから探そうとします。
`$HOME\AppData\Local\Temp\nuget\<なんかよくわからないハッシュ値>\`

```zsh
> #r "nuget: FSharp.Data";;
> open FSharp.Data;;
[読み込み中 C:\Users\iranika\AppData\Local\Temp\nuget\24068--45e63f04-34e0-4cb9-aafd-4ba346693aff\Project.fsproj.fsx]
namespace FSI_0002.Project

> type csv = CsvProvider<"params.csv">;;
type csv = CsvProvider<...>

> let data = csv.Load("params.csv");;
セッションを 'C:\Users\iranika\.nuget\packages\fsharp.data\3.3.3\lib\netstandard2.0\FSharp.Data.dll' にバインドしています...
System.IO.FileNotFoundException: Could not find file 'C:\Users\iranika\AppData\Local\Temp\nuget\24068--45e63f04-34e0-4cb9-aafd-4ba346693aff\params.csv'.
File name: 'C:\Users\iranika\AppData\Local\Temp\nuget\24068--45e63f04-34e0-4cb9-aafd-4ba346693aff\params.csv'
   at System.IO.FileStream.ValidateFileHandle(SafeFileHandle fileHandle)
   at System.IO.FileStream.CreateFileOpenHandle(FileMode mode, FileShare share, FileOptions options)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options)
   at FSharp.Data.Runtime.IO.asyncRead@219-5.Invoke(Unit unitVar) in C:\GitHub\dsyme\FSharp.Data\src\CommonRuntime\IO.fs:line 220
   at Microsoft.FSharp.Control.AsyncPrimitives.CallThenInvoke[T,TResult](AsyncActivation`1 ctxt, TResult result1, FSharpFunc`2 part2) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\async.fs:line 386
   at Microsoft.FSharp.Control.Trampoline.Execute(FSharpFunc`2 firstAction) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\async.fs:line 105
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.FSharp.Control.AsyncResult`1.Commit() in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\async.fs:line 338
   at Microsoft.FSharp.Control.AsyncPrimitives.RunSynchronouslyInCurrentThread[a](CancellationToken cancellationToken, FSharpAsync`1 computation) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\async.fs:line 858
   at Microsoft.FSharp.Control.AsyncPrimitives.RunSynchronously[T](CancellationToken cancellationToken, FSharpAsync`1 computation, FSharpOption`1 timeout) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\async.fs:line 878
   at Microsoft.FSharp.Control.FSharpAsync.RunSynchronously[T](FSharpAsync`1 computation, FSharpOption`1 timeout, FSharpOption`1 cancellationToken) in F:\workspace\_work\1\s\src\fsharp\FSharp.Core\async.fs:line 1142
   at <StartupCode$FSI_0005>.$FSI_0005.main@()
エラーのため停止しました
>

```

今のところは`__SOURCE_DIRECTORY__`を使って相対参照を回避していますが、
なんかいい感じの解決方法がほしいです。

```fsharp
let [<Literal>] paramsCsv = __SOURCE_DIRECTORY__ + "\\params.csv"
type csv = CsvProvider<paramsCsv>
```