スクリプト言語のF#で Hello world

### Summary

スクリプト言語としての`F#`の簡単なメモ

間違い等、指摘してもられると嬉しいですっ (^_^)/


### Good point of F# script file

おてがるにコードがかけて実行できるところ


### Environment

```shell

$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.13.4
BuildVersion:	17E199

$ mono --version
Mono JIT compiler version 5.4.1.6 (tarball Mon Dec 11 14:59:42 GMT 2017)
Copyright (C) 2002-2014 Novell, Inc, Xamarin Inc and Contributors. www.mono-project.com
	TLS:           normal
	SIGSEGV:       altstack
	Notification:  kqueue
	Architecture:  amd64
	Disabled:      none
	Misc:          softdebug
	LLVM:          supported, not enabled.
	GC:            sgen (concurrent by default)

$ paket --version
Paket version 5.156.0
```

### Prepare

`F#`を実行するには`mono`が必要

ライブラリをダウンロードするには`paket`が便利

```shell

/// fsharpの実行環境であるmonoをインストール
$ brew install mono

/// ライブラリのダウンローダーであるpaketをインストール
$ brew install paket
```


### Hello world

こんな感じ

```shell

// F# のコードをかく
$ vim hello.fsx

    printfn "hello"

// スクリプトファイル hello.fsx を実行する
$ fsharpi hello.fsx

    hello
```


### ファイルがモジュール

スクリプトファイル(`.fsx`）はファイルそのものが一つのモジュールになります

`module`の名前はファイル名になります（イニシャルが大文字になる）

`module = namespace` みたいな感じ

| File Name     | module name |
| ------------- | :-------------: |
| foo.fsx       | Foo             |

see also : [F#, namespaces, modules, fs and fsx](https://stackoverflow.com/questions/2354984/f-namespaces-modules-fs-and-fsx?answertab=active#tab-top)
see also : [namespace](https://docs.microsoft.com/ja-jp/dotnet/fsharp/language-reference/namespaces)
see also : [module](https://docs.microsoft.com/ja-jp/dotnet/fsharp/language-reference/modules)
see also : [module と namespace](https://gitter.im/fsugjp/public?at=5ae111e01130fe3d3618e504)

イメージをつかむための例

```shell

// たとえば 下記のようなコードを書いてみる
$ cat foo.fsx

let aaa =
    printfn "bbb"

module Bar =
    let hello =
        printfn "hello"

module Baz =
    let world =
        printfn "world"


$ cat main.fsx


// foo.fsx をロードする
#load "./foo.fsx"

// モジュールをオープンする
open Foo

aaa       // bbb
Bar.hello // hello
Baz.world // world
```

### コンパイルするとはやい

```shell

$ cat foo.fsx
printfn "hello"

$ time fsharpi foo.fsx
hello

real	0m2.194s
user	0m2.100s
sys	0m0.128s

$ fsharpc foo.fsx
Microsoft (R) F# Compiler version 4.1
Copyright (c) Microsoft Corporation. All Rights Reserved.

$ time mono foo.exe
hello

real	0m0.054s
user	0m0.036s
sys	0m0.014s
```

### スクリプト内で`dll`への参照設定ができる

コンパイラの `--reference: / -r:` とおなじ

| compiler            | script file           |
| -------------       | -------------         |
| -r:/path/to/foo.dll | #r "/path/to/foo.dll" |


### `dll`への参照設定一覧ファイル

`paket`でダウンロードした`dll`の参照一覧ファイルを作成することができます

これを作成することで`dll`への参照設定の手間をぐんと減らすことができますっ！


参照設定一覧ファイルの作り方

```shell
// 作業用フォルダを作成、移動
$ mkdir foo/
$ cd foo/

// paketをイニット
$ paket init

/// dependencies file に generate_load_scripts を書き込む
$ vim paket.dependencies

    source https://www.nuget.org/api/v2
    generate_load_scripts: true // この一文を追加するだけでオーケー
    nuget System.Data.SQLite
    nuget fsharp.data == 3.0.0-beta3
    nuget Selenium.webdriver
    nuget Selenium.Support

ライブラリーをインストールするとファイルができる
$ paket install
```

参照一覧ファイルの場所

```shell

$ tree -d .paket/
.paket/
└── load
    ├── net47
    └── net471
        ├── EntityFramework.csx
        ├── EntityFramework.fsx
        ├── FSharp.Data.csx
        ├── FSharp.Data.fsx
        ├── main.group.csx
        └── main.group.fsx // このファイル！
```

スクリプトファイルの記入例

```fsharp

// 参照一覧ファイルをロードするだけで参照設定が完了
#load "../.paket/load/net471/main.group.fsx"

open System.Data.SQLite
open System.Collections.Concurrent

module Util =

    type LOTO  = Loto6 | Loto7
    type Loto  = { number: int ; lst:int list ; kind: LOTO }
    let  loto6 = { number = 6 ; lst = [1..43] ; kind = Loto6 }
    let loto7 = { number = 7 ; lst = [1..37] ; kind = Loto7 }
```

### おてがるビルド

上記の`dll`への参照ファイルとコンパイラオプション`--standalone`を利用することでお手軽に`exe`ファイルを作成することができます

こんな感じ

```shell

// ビルド用のbash script を書く
$vim build.bash

SCRIPT_PATH=./foo.fsx

declare -a arr=(
    "${SCRIPT_PATH}"
    --standalone  # ここがポイント！
    --nologo
    --simpleresolution
)

fsharpc "${arr[@]}"

// foo.fsx をビルドする
$ bash build.bash

// 実行する
$ mono foo.exe
```

ただし、`interope` を使ってるスクリプトがある場合は `--standalone` が使えない

### interop がある場合のビルド

たとえば下記のようなコードだと`interop`を使ってるため`--standalone`が使えません

```fsharp
#load "../.paket/load/net471/main.group.fsx"
open System.Data.SQLite
open System.Runtime.InteropServices

module Database =

    /// Path to libSQLite.Interop.dylib
    [<DllImport(@"./libSQLite.Interop.dylib" , CallingConvention = CallingConvention.Cdecl)>]
    let sqlite_connection : System.Data.SQLite.SQLiteConnection  =
        /// Path to sqlite3
        ( new SQLiteConnection( @"Data Source=./loto.sqlite3;Version=3;foreign keys=true" ))
```

この場合は`dll`を`exe`と同じフォルダにコピーしてくる必要があります

```shell

/// こんな感じ

.foo/
└── bin/
    ├── foo.exe
    ├── foo.dll
    ├── bar.dll
    └── baz.dll
```


ビルド用の`bash script`

```bash
#!/usr/bin/env bash
# ===========================================================================
#  FILE    : build.bash
#  AUTHOR  : callmekohei <callmekohei at gmail.com>
#  License : MIT license
# ===========================================================================


# fit your file path
FSX_PATH=./foo.fsx
Lib_PATH=./.paket/load/net471/main.group.fsx

function create_exe_file () {
    local arr=(
        "${FSX_PATH}"
        --nologo
        --simpleresolution
        --out:./bin/$(basename "${FSX_PATH}" .fsx).exe
    )
    fsharpc "${arr[@]}"
}

function arrange_text () {
    local line
    while read -r line
    do
        echo "${line}" \
        | sed -e 's/#r //g' \
              -e 's/"//g'   \
        | grep --color=never -e "^\." \
        | sed -e 's|^.*packages|\./packages|g'
    done
}

function copy_dll_to_bin_folder() {
    local line
    while read -r line
    do
        cp "${line}" ./bin/
    done
}

if [ -e ./bin/ ] ; then
    echo 'do nothing!'
else
    mkdir ./bin/
    create_exe_file
    if [ "$?" = 0 ] ; then
        cat "${Lib_PATH}" \
        | arrange_text \
        | copy_dll_to_bin_folder
    fi
fi

```

`foo.fsx` をビルドする

```shell
$ bash build.bash
```

実行する

```shell
$ cd bin/
$ mono foo.exe
```

### 参考

`--standalone` に関して

```shell

$ fsharpc --help
```

`paket`に関して

https://fsprojects.github.io/Paket/


`FSharp`のいろいろなことに関して（すごく有用！）

https://gitter.im/fsugjp/publi://gitter.im/fsugjp/public