はじめに
今までfsiではパッケージ管理にpaket
を使う必要があったり、DLLへのパスを明記しないといけなかったり面倒だった。
// ↓パッケージは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
// DLと参照をよしなにやってくれる
# r "nuget: FSharp.Data"
open FSharp.Data
というわけで早速試しましょう!!
Canopyで試す
What's Canopy? →https://lefthandedgoat.github.io/canopy/
公式を参考にしつつ、Googleでボーダーコリーを検索するテストコードを書きます。
ボーダーコリー、賢いし可愛いので。
# 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なのでこのあたりの挙動は正式リリースで変わるかもしれません。
話を戻して、先程書いたプログラムを動かします。
dotnet fsi --langversion:preview ./search-collie.fs
動きました!!(スクショ取り忘れ)(君の目で確かめてくれ)
もうこれで.fsxファイル単体を配布するだけでみんな幸せです。
さよならpaketくん…君のことは忘れないよ…
ちなみに--langversion:preview
は.NET5でF#5.xが正式リリースされたら不要になるらしいです。
.NET5が待ち遠しいですね。
余談
その他の注意点として、
csv.Load等で相対的にファイルパスを書いた場合、作業用に作られるテンポラリディレクトリから探そうとします。
$HOME\AppData\Local\Temp\nuget\<なんかよくわからないハッシュ値>\
> #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__
を使って相対参照を回避していますが、
なんかいい感じの解決方法がほしいです。
let [<Literal>] paramsCsv = __SOURCE_DIRECTORY__ + "\\params.csv"
type csv = CsvProvider<paramsCsv>