Unison で Hello World と FizzBuzz

## 概要

[Unison](https://www.unisonweb.org/) という開発中のプログラミング言語を触ってみました。
おそらく日本で最初の Unison の記事です。
"3-clause BSD" LICENSE のもとに配布されています。

## 言語の特徴

https://github.com/unisonweb/unison

- 静的型付き純粋関数型
- モダンで Haskell に似た文法を持つ
- 分散システムでも1つのプログラムで動かせる

## 環境構築

Unison の開発環境を構築します。

1. [Haskell Stack](http://docs.haskellstack.org/en/stable/README.html#how-to-install) のインストール
2. リポジトリの取得  
`git clone --recursive https://github.com/unisonweb/unison.git`
3. ビルド＆テスト  
`stack build && stack exec tests`
4. 初期設定  
`stack exec unison init`
5. 起動[^1]  
`stack exec unison`
6. ".base" ライブラリの取得  
起動したインタラクティブシェルで以下のコマンドを実行します。

```bash
.> pull https://github.com/unisonweb/base .base
```

エラーが出なければ環境構築は終了です。

## Hello World

unison を起動したディレクトリに `scratch.u` というファイルを作成します。
デフォルトではこのファイルが自動的に watch されます。
`scratch.u` をテキストエディタで開いて以下のコードを入力します。

```scratch.u
hello : '{IO} ()
hello = '(base.io.printLine "Hello world!")
```

ファイルを保存すると repl が実行されます。

```bash
.> 

  I found and typechecked these definitions in
  ~/projects/unison/scratch.u. If you do an `add` or `update`,
  here's how your codebase would change:
  
    ⍟ These new definitions are ok to `add`:
    
      hello : '{IO} ()
   
  Now evaluating any watch expressions (lines starting with
  `>`)... Ctrl+C cancels.
```

インタラクティブシェルでコマンドを実行してみましょう。

```bash
.> run hello
Hello world!
.> 
```

おめでとうございます。これであなたも unison を完全に理解できましたね！

## Namespace

利便性のために namespace を分けてみましょう。
新しい namespace を作成して、そこに hello 関数を移動します。

`.>` で始まる行は unison インタラクティブシェルで実行します。
**注意:** `.>` や `.mylibrary>` は**入力しません。**

1. namespace の作成  
`.> namespace mylibrary`
2. namespace の移動  
`.> cd .mylibrary`  
`.mylibrary>`
3. 関数の移動  
`.mylibrary> move.term .hello hello`
4. namespace の探索  
`.mylibrary> find`  

`  1. hello : '{IO} ()` が表示されれば "mylibrary" namespace に hello を移動できたことになります。

## Publish

コードを GitHub repository に namespace を publish してみましょう。

1. Github Repository の作成  
Github に repository を作成します。
仮に "myunisonrepo" という名前とします。
2. namespace の移動  
`.mylibrary> cd .`
3. release namespace の複製  
`.> copy.namespace mylibrary mylibrary.releases.v1`
4. 動作確認  
`.> cd mylibrary.releases.v1`  
`.mylibrary.releases.v1> find`
5. publish  
`cd .mylibrary`  
`push git@github.com:<yourgithubuser>/myunisonrepo`

エラーが出なければ成功です。
push に失敗した場合には github の認証を確認してください。

下記のような構成が repository に作られます。

<img width="428" alt="スクリーンショット 2020-01-28 0.16.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8421/92141496-c33c-44a5-5604-b7430fc7ac99.png">

## FizzBuzz

Unison で FizzBuzz を書いてみましょう。

```scratch.u
use .base

fizzBuzz : Nat -> Text
fizzBuzz n =
  case (mod n 3, mod n 5) of
    (x,y) | (x == 0) && (y == 0) -> "FizzBuzz"
    (x,_) | (x == 0) -> "Fizz"
    (_,y) | (y == 0) -> "Buzz"
    _ -> toText n

> map fizzBuzz (range 1 101)
```

`Nat` は自然数、`Text` は文字列の型です。
`fizzBuzz : Nat -> Text` は自然数を引数に取り文字列を返す関数 `fizzBuzz` を表しています。
`range 1 101` は1から100までの自然数の List です。
map 関数に fizzBuzz をつけて自然数の List に適用すると以下の出力が得られます。

```bash
           [ "1",
             "2",
             "Fizz",
             "4",
             "Buzz",
             "Fizz",
             "7",
             "8",
             "Fizz",
             "Buzz",
             "11",
             "Fizz",
             "13",
             "14",
             "FizzBuzz",
             "16",
             "17",
             "Fizz",
             "19",
             "Buzz",
             ...(以下略)
```

## 気になったこと

- Haskell が書ける人には書きやすい。
- ファイルモジュールではなくて namespace 単位で分別するなどの独特の思想が面白い。
- publish した repository 内のバイナリファイルに可読性がないのでドキュメントが必須。

## 注意

- Unison は開発中の言語なので仕様が大きく変わる可能性があります。執筆時点の最新バージョンを使いました。
- 情報が少なく仕様歴も浅いので仕様を完全には把握していません。間違っている記述を発見したらご指摘いただけると助かります。

[^1]: 起動まで成功するとインタラクティブシェルが立ち上がります。