PureScript の Getting Started を読む & 続 `psci` を試す

この記事は [(bouzuya) PureScript Advent Calendar 2016][purescript-advent-calendar] の 4 日目。 (bouzuya) PureScript Advent Calendar 2016 は bouzuya の PureScript 学習の記録だ。

- ← 3 日目『 [PureScript の build tool`pulp` を動かしてみる - Qiita][2016-12-03] 』
- → 5 日目『 [PureScript by Example 1 章を読む - Qiita][2016-12-05] 』

## 概要

4 日目にして、いまさらだけど  [Getting Started with PureScript][getting-started] を見る。見ながら、 `psci` の残りのコマンドを見ていく。

[公式][purescript] の LEARN を開いたらリンクがあったので、漏れを確認する意味で見ていく。

## 準備

環境構築まわりの次の手順は [1][2016-12-01] / [2][2016-12-02] / [3][2016-12-03] で触れたのでスルー。

```
$ npm i -g purescript pulp bower
$ pulp init
$ pulp build
$ pulp test
```

ここまでは[前回][2016-12-03]までとほとんど同じなので、環境は前回のものを使用する。念のため再掲しておくと次のような感じ。

```
$ mkdir project3
$ cd project3/
$ npm init -y
$ npm install -D bower pulp purescript
$ \vi package.json # scripts に `pulp` 関連のものを追加
$ npm run pulp:init
$ git init
$ git commit -m 'initial commit'
```

## `bower install`

Getting Started に従って `purescript-lists#3.2.1` をインストールする。元ページではバージョン指定はないが、動きが変わるとややこしいのでここでは明示的に指定する。

```
$ $(npm bin)/bower install -S purescript-lists\#\^3.2.1
```

また上記 `purescript-lists#3.2.1` のほかに [`pulp init` を実行しているので、次のコマンドが合わせて実行された扱いになる][pulp-init] ので、いくつかのパッケージが依存関係に追加される。

- `bower install --save purescript-lists purescript-console`
- `bower install --save-dev purescript-psci-support`

バージョン指定はないのだけど、今回は結果的に `purescript-prelude#2.1.0` / `purescript-console#2.0.0` / `purescript-psci-support#2.0.0` が依存関係に入り、 `bower.json` は次のようになった。

```
$ cat bower.json
{
  "name": "project3",
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "output"
  ],
  "dependencies": {
    "purescript-prelude": "^2.1.0",
    "purescript-console": "^2.0.0",
    "purescript-lists": "^3.2.1"
  },
  "devDependencies": {
    "purescript-psci-support": "^2.0.0"
  }
}
```

Pursuit:

- [purescript-lists - Pursuit][pursuit-purescript-lists]

`purescript-lists` は `Data.List` などのモジュールを提供するようだ。

## `pulp psci` で `import` やコマンドをためす

### `import Prelude` と (psci) `:type`

とりあえず `pulp psci` をして、`import Prelude` してみる。

```
$ npm run pulp:psci
> project3@1.0.0 pulp:psci /Users/bouzuya/project3
> pulp psci

PSCi, version 0.10.2
Type :? for help

> import Prelude
> :type map
forall a b f. (Functor f) => (a -> b) -> f a -> f b

> :t map
forall a b f. (Functor f) => (a -> b) -> f a -> f b
```

前回もそうだけど `:type` は `:t` で良い。

次は `import Data.List` 。

```
> import Data.List
> :type zip
forall a b. List a -> List b -> List (Tuple a b)
```

Getting Started によると `Tab` で補完が効くようだ。`import ` まで打ったときで候補が変わるので、文脈も考慮されているようだ。

### (psci) `:show import`

ここは Getting Started にないのだけど、 `:show import` を試してみる。`import` されたモジュールを表示できるようだ。

```
> :show import
import Data.List
import Prelude

> import Data.List

> :show import
import Data.List
import Data.List
import Prelude
```

### (psci) `:show loaded`

ついでに `:show loaded` を試す。こちらは読み込まれたモジュールを表示できるようだ。

```
> :show loaded
Control.Alt
(中略)
```

予想以上に表示された。

### (psci) `:reset`

次は `:reset` で読み込まれたモジュールをリセットしてみる。

```
> :reset
> :show import

> import Prelude
> :show import
import Prelude

> :reset
> :show import

>
```

なるほど、たしかに消える。

### (psci) `:kind`

`:kind` 。型の種類を表示する。あまり理解できていない。型変数の数 (?) みたいな認識で居る。

```
> import Data.List
> :kind List
* -> *

> :kind (List Int)
*
```

### (psci) `:browse`

`:browse` 。モジュールに含まれる関数を表示する。

```
> :browse Data.List

(中略)

zipWith :: forall a b c. (a -> b -> c) -> List a -> List b -> List c

zipWithA :: forall m a b c. (Applicative m) => (a -> b -> m c) -> List a -> List b -> m
 (List c)
```

### (psci) `:paste`

`:paste` 。複数行の場合に使えるっぽい。

```
> import Prelude
> :paste
… 1
… +
… 2
3

>
```

## Getting Started の続き

大きく脱線したけど、ここから戻る。

```
$ npm run pulp:psci
> project3@1.0.0 pulp:psci /Users/bouzuya/project3
> pulp psci

PSCi, version 0.10.2
Type :? for help
> import Prelude ((==), (||), mod)
> import Data.List (filter, range)
> import Data.Foldable (sum)
> let ns = range 0 999
> let multiples = filter (\n -> mod n 3 == 0 || mod n 5 == 0) ns
> :type multiples
List Int

> sum multiples
233168

> :quit
See ya!
```

説明なんて冗長な感じもするけど……

1. `range` で 0 以上 999 以下の整数の List をつくり、変数 `ns` を束縛する
2. `ns` の各要素に対して、 `mod` で 3 の剰余をとって 0 、または 5 の剰余をとって 0 のみを `filter` で選択する
3. `sum` でそれらの要素を合計する

という感じ。

JavaScript と文法事項を比較すると (差分が大きすぎる気もするけど) …… 、ここでは関数の呼び出しに `()` は要らないとか `\x -> x` が `x => x` くらいかな？あとは雰囲気で読めるはず……。

## `src/Euler.purs`

Getting Started では `src/Euler.purs` を作成し、それを `import Euler` で読み込んでいる。

`src/Euler.purs`:

```
module Euler(answer) where

import Prelude ((==), (||), mod)
import Data.List (filter, range)
import Data.Foldable (sum)

ns = range 0 999
multiples = filter (\n -> mod n 3 == 0 || mod n 5 == 0) ns
answer = sum multiples
```

`psci` とは違い、 `let` は不要だ。

ぼくの趣味で import する関数を限定し、 module 外に export するものを `answer` に限定した。このあたりは次の Language Guide を参照すると良い。

Language Guide:

- [Language Guide - Importing Modules][language-guide-importing-modules]
- [Language Guide - Module Exports][language-guide-module-exports]

```
$ npm run pulp:psci
> project3@1.0.0 pulp:psci /Users/bouzuya/project3
> pulp psci

Compiling Euler
PSCi, version 0.10.2
Type :? for help

> import Euler
> answer
233168

> :t answer
Int

> :q
See ya!
```

## `src/Main.purs`

`Euler` モジュールの実行のために `src/Main.purs` を編集している。

`src/Main.purs`:

```
module Main(main) where

import Prelude (Unit, (<>), show)
import Euler (answer)
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Console (CONSOLE, log)

main :: forall e. Eff (console :: CONSOLE | e) Unit
main = do
  log ("The answer is " <> show answer)
```

これもまた、さきの例と似たような、ぼくなりの改変を入れた。 `do` とかなくても通る気はするけど、そこは維持した。

```
$ npm run pulp:run

> project3@1.0.0 pulp:run /Users/bouzuya/project3
> pulp run

* Building project in/Users/bouzuya/project3
* Build successful.
The answer is 233168
```

よしよし。これで Getting Started はおしまい。

## まとめ

[2][2016-12-02] で残していた `psci` のコマンドをためしたのと [Getting Started with PureScript][getting-started] を片付けた。

もっと基本的な言語仕様やパッケージなどを知らないと書けなさそうだ。

## 参考

- [PureScript][purescript]
- [Getting Started with PureScript][getting-started]
- [Language Guide][language-guide]
- [Language Guide - Importing Modules][language-guide-importing-modules]
- [Language Guide - Module Exports][language-guide-module-exports]

## 次回以降の TODO

- `psc-package` を試す
- [PureScript Language Guide][language-guide] を読む
- [PureScript by Example][purescript-by-example] を読む
- [24 Days of PureScript, 2016][paf31/24-days-of-purescript-2016] を読む
- [24 Days of PureScript, 2014](https://gist.github.com/paf31/8e9177b20ee920480fbc) を読む
- 某氏の記事を紹介する

[2016-12-01]: http://qiita.com/bouzuya/items/a5be85ec3e496e5bf436
[2016-12-02]: http://qiita.com/bouzuya/items/806335ea74ead0e33d95
[2016-12-03]: http://qiita.com/bouzuya/items/86cc9aaaddcc1aa790cf
[2016-12-04]: http://qiita.com/bouzuya/items/b2b80c22c50d0c1e739c
[2016-12-05]: http://qiita.com/bouzuya/items/f19ee0b86e9aec3f0afa
[bodil/pulp]: https://github.com/bodil/pulp/tree/v10.0.0
[getting-started]: http://www.purescript.org/learn/getting-started/
[language-guide-importing-modules]: https://github.com/purescript/purescript/wiki/Language-Guide#importing-modules
[language-guide-module-exports]: https://github.com/purescript/purescript/wiki/Language-Guide#module-exports
[language-guide]: https://github.com/purescript/purescript/wiki/Language-Guide
[paf31/24-days-of-purescript-2016]: https://github.com/paf31/24-days-of-purescript-2016
[pulp-init]: https://github.com/bodil/pulp/blob/v10.0.0/src/Pulp/Init.purs#L97-L98
[purescript-advent-calendar]: http://www.adventar.org/calendars/1494
[purescript-by-example]: https://leanpub.com/purescript/read
[purescript]: http://www.purescript.org/
[pursuit-purescript-lists]: https://pursuit.purescript.org/packages/purescript-lists/3.2.1