Purescript (2)

かの昔、あのクレオパトラも食べたと言う、モロヘイヤ。それがスーパーの地元野菜コーナーに 置いてあったので買ってきた。今年の初物。

女房が調理に取り掛かって変な事を言う。あら、ピーマンが紛れ込んでいると。お菓子とかだと 異物混入でニュースになるだろうけど、野菜じゃそんなの事件にもならないよ。きっと農家の 人が、一緒に食べると美味しいですよって隠し味にどうぞの意味を込めて入れてくれたんじゃ ない。 それで、そのピーマンがもどきピーマンで、実は激辛な唐辛子だったりして。

で、食べた。いつものとは違って、ちと苦い。味付けを聞かれたんで、そう答えたよ。 えーー、と女房。まだ半分残っていた未調理の葉っぱをみたら。良くみると違う形状の葉っぱが 混じっていると。

大方、モロヘイヤとピーマンを混栽してる所から、子供が夏休みになったんで手伝いでむしった んじゃない。子供なら、同じような葉っぱに見えるんで、気にせずに採取したと。

クックパッドを早速調べた女房、ピーマンの葉っぱは佃煮にして食べるらしい。それしか出て こなかったとか。癖が有って、他の食べ方には適さないんでしょうな。まあ、毒でもないので 良しとするか。

夏の珍事でした。

後日、ノーキョーの野菜コーナーへ行ったら、正真正銘のピーマンの葉っぱが置いてあった。 佃煮目当てに買ったら、女房が増量するって言って、冷蔵庫に残っていたキクラゲを加えてくれた。(ていの良いガベコレだな。) キクラゲのコリコリ感が有って、なかなかの 珍味になったぞ。酒の肴にぴったりだ。

Debian9 でのPurescript環境

前回Debian9に整えたPurescriptの環境は、下記のようになった。

deb9:~$ purs --version
0.11.5
deb9:~$ npm list --depth 0
/home/sakae
├── bower@1.8.0
└── pulp@11.0.0

要のpurescriptは、バイナリーを取ってきたものだ。特に説明には、Linuxのデストリに 関するものが無く、どんなリナでも動くと思ってた。巨大なpursが一つと後は、引数を かませた短縮形のファイルが有った。

deb9:purescript$ ls
dependencies.txt  psc         psc-hierarchy   psc-ide-server  README
INSTALL.md        psc-bundle  psci            psc-publish
LICENSE           psc-docs    psc-ide-client  purs

その、巨大さから、スタティックになってるかと思ったら、ダイナミックファイルだったよ。 色々なリナで動くかどうかは運次第なんだな。INSTALL.mdにもやんわりと書いてあった。

ダメな場合は、

$ stack update
$ stack unpack purescript
$ cd purescript-x.y.z  
ed)
$ stack install

という具合にGHCを振り回してくださいって事だ。ClearLinuxは、そのだめなケースに 当たってしまい、30分近く、ウンウン言ってた訳ね。

試運転

deb9:ps$ mkdir hello
deb9:ps$ cd hello
deb9:hello$ pulp init
* Generating project skeleton in /home/sakae/ps/hello
  :
bower install       purescript-psci-support#3.0.0

purescript-psci-support#3.0.0 bower_components/purescript-psci-support
└── purescript-console#3.0.0
deb9:hello$ pulp run
  :
Compiling PSCI.Support
* Build successful.
Hello sailor!

やあ、水兵さんって事で、海原へ乗り出す準備完了。

deb9:hello$ cat -- > index.html
<!doctype html>
<html>
  <body>
    <h1>Hello sailor!</h1>
    <script src="/app.js"></script>
  </body>
</html>
deb9:hello$ pulp server -p 8080 --host xxx.xxx.xxx.xxx
* Server listening on http://xxx.xxx.xxx.xxx:8080/
* Building project in /home/sakae/ps/hello
* Build successful.
* Bundling JavaScript...
* Bundled.

今度は、ClearLinuxで失敗した、Webへ載せる事に挑戦。例によって、index.htmlを コピペの作法で ぺ した。サーバーをただ起動すると、ローカルホストになっちゃって、 Windows側からアクセス出来なくなる。そこで、xxx.xxx.xxx.xxxっていう、Debian9が 持ってる(VMWareのDHCPが配った)IPAddressを指定。

後は、コピペ用に表示されてる、httpをWindows10のブラウザーに ぺ する。 ちゃんと見えるよ水兵さん。何の問題もない。 一つのOSに凝り固まらない方が、身のためだ。

purescript-book

purescript-book

deb9:ps$ cp -a purescript-book/exercises/chapter3 ch3
deb9:ps$ cd ch3/
deb9:ch3$ ls
bower.json  src  test
deb9:ch3$ bower update
deb9:ch3$ pulp build
* Building project in /home/sakae/ps/ch3
Error 1 of 21:

  at bower_components/purescript-tuples/src/Data/Tuple.purs line 176, column 1 -
 line 176, column 1

    Unable to parse module:
    unexpected comma in constraints.

Class constraints in type annotations can no longer be grouped in parentheses.
Each constraint should now be separated by `=>`, for example:
    `(Applicative f, Semigroup a) => a -> f a -> f a`
  would now be written as:
    `Applicative f => Semigroup a => a -> f a -> f a`.

    expecting indentation past column 1, (, qualifier, proper name, operator or
->


  See https://github.com/purescript/documentation/blob/master/errors/ErrorParsin
gModule.md for more information,
  or to contribute content related to this error.
  :

Error 21 of 21:
  :

* ERROR: Subcommand terminated with exit code 1

盛大にエラーが出てくるのは、clearLinuxと一緒だな。

emacs君が文法チェックして、まずい所は赤く怒ってくれるんで、機械的に修正するだけ なんだけど、あちこちに有って、かなわん。

-- | Test whether a value is an element of a data structure.
elem :: forall a f. (Foldable f, Eq a) => a -> f a -> Boolean
elem = any <<< (==)

この例だと、3行目のelemが赤くなってる。この前でエラーが発見されましたって事。

-- | Test whether a value is an element of a data structure.
elem :: forall a f. Foldable f Eq a => a -> f a -> Boolean
elem = any <<< (==)

前行のタプルを止めれば、怒りは収まる。

ざっと数えたら、修正箇所がとんでもなく有ったぞ。

deb9:bower_components$ find . -name '*.purs' | xargs fgrep '=>' | wc
    448    5673   48030

これはもう、機械的に書き換えるしかないな。そうなるとsed君の出番だ。 面倒な正規表現と格闘する事になる。書き換えるべきポイントを、正規表現する。 変換部分を後で分かり易いように、コメントとして残す事にしよう。

deb9:tmp$ cat conv.sed
#elem :: forall a f. (Foldable f, Eq a) => a -> f a -> Boolean
#-------------------  ---------- -----     -------------------
#   \(1\)                2         3            4

s/\(.*\) (\(.*\),\(.*\)) => \(.*\)/\1 \2 \3 => \4/

ドット・アスターで任意長の文字を表せる。それと固定文字列とを組み合わせて、一行を きっちり表現する。任意長の文字を後で使えるようにグループ化しておける。そのグループは 左側から1,2..のように番号付けされる。 後は、その番号を使って、再び一行を組み立てる。s/old/new/ って、いうsedの文字列 置換の応用。

そして、そのsedスクリプトを、実験ファイル(aa.purs)に適用して、上書き。

deb9:tmp$ sed -i -f conv.sed aa.purs

上の実行系を、findと組み合わせる。勿論、conv.sedは、ch3と言うプロジェクトdirの トップに配置しておく。波カッコとセミコロン(shellに特別な意味に解釈されないよう エスケープが必要)は、findが探し当てた、書き換えが必要(と思われる)な、pursファイルを 意味する。

deb9:ch3$ find bower_components -name '*.purs' -exec sed -i -f conv.sed {} \;

これで、再コンパイルしてみると、今度は違ったエラーが出てきたぞ。

Error 15 of 15:

  at bower_components/purescript-console/src/Control/Monad/Eff/Console.purs line
13, column 1 - line 13, column 1

    Unable to parse module:
    The `!` symbol is no longer used for the kind of effects.
  The new equivalent is the named kind `Effect`, defined in `Control.Monad.Eff` in the `purescript-eff` library.


  See https://github.com/purescript/documentation/blob/master/errors/ErrorParsingModule.md for more information,

前回から少しは減ったね。でも、いつまで続くモグラ叩き。根本的に考え直した方が良さそうだ。いくらなんでも、こんなに目立つエラーを放置しておかないだろう。それに、やあ水兵さんは 何の問題もなく動いたしね。

deb9:ps$ cat hello/bower.json
{
  "name": "hello",
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "output"
  ],
  "dependencies": {
    "purescript-prelude": "^3.1.0",
    "purescript-console": "^3.0.0"
  },
  "devDependencies": {
    "purescript-psci-support": "^3.0.0"
  }
}

これ、自動で作られた使うモジュールリスト。次は、bookの付属品。

deb9:ps$ cat purescript-book/exercises/chapter3/bower.json
   :
  "dependencies": {
    "purescript-lists": "^3.0.0",
    "purescript-console": "^2.0.0"
  },
  "devDependencies": {
    "purescript-psci-support": "^2.0.0"
  }
}

どうもこれを見ると、モジュールが3系に上がってるみたいだ。 で、3系にしてみたけど、やはりエラーが出て来る。変にバージョン指定してるから、地獄を 見るんじゃなかろうか。きちんとパッケージがメンテナンスされてる事を期待して、 一番新しいのを指定したらどうよ、、、って野生の感が働いた。 npm package.json 取扱説明書あたりが、ちゃんとしたものかな。

一番新しいのって、英語で言うとlast。変な英語を指定して怒られるのも癪に障るので、 空文字にした。(文字列を省くのはジェイソンが怒り出してくるだろうから避けたよ) こんな感じね。

  "dependencies": {
    "purescript-lists": "",
    "purescript-console": ""
  },
  "devDependencies": {
    "purescript-psci-support": ""
  }

bower list ってやると、モジュールの階層関係がバージョン付きで出てくるぞ。

無事にコンパイル出来たので、replに入ってみる。

deb9:chapter3$ pulp repl
PSCi, version 0.11.5
Type :? for help

> :?
The following commands are available:

    :?                        Show this help menu
    :quit                     Quit PSCi
    :reload                   Reload all imported modules while discarding bindings
    :clear                    Discard all imported modules and declared bindings
    :browse      <module>     See all functions in <module>
    :type        <expr>       Show the type of <expr>
    :kind        <type>       Show the kind of <type>
    :show        import       Show all imported modules
    :show        loaded       Show all loaded modules
    :paste       paste        Enter multiple lines, terminated by ^D

Further information is available on the PureScript documentation repository:
 --> https://github.com/purescript/documentation/blob/master/guides/PSCi.md
> import Prelude
> add x y = x + y
> :type add
forall t3. Semiring t3 => t3 -> t3 -> t3

後は、本と首ったけになって実習していけばいいのだな。

Flare

purescriptのHome-pageに、試してみてってのがある。それを辿ると、 Try PureScript Librariesなんてのに出くわす。その中に、Flareってのが置いてある。 シンプルなやつだけど、面白い。

Simple Drawing example

deb9:hello$ pulp build -m Main -t app.js

こうやってコンパイルするみたいだけど、Webに載せるにはどうしたらいいの?

purescript-flare

Flareここ、EUはドイツっぽいけど、 英語で、例がいろいろと載ってる。下記は一番簡単なやつ。

flare-example

これは、アメリカの田舎風温度表示、華氏を世界標準に変換する例だ。 まるで、C語の教科書に載ってた、あれみたいだな。

PureScriptのUIライブラリまとめをやった方がおられて、Webで動くって素敵と思うぞ。

Tips

これからpurescriptを使って、Webに結果を出す事も多かろう。そうなると、 pulp server するよりは、pythonに任せてしまった方が汎用性が高そうだ。

alias server='python3 -m http.server 8080'

こんなのを登録しとく。これ、可搬型でどこでもWebサーバーに出来ちゃうっていう優れもの。 ドキュメントルートにしたい場所へcdして、serverって叩くだけ。

よく、/usr/share/docの下にhtmlでドキュメントが置いてあるが、これもすぐに使い慣れた ブラウザーで閲覧出来るぞ。

但し注意点が一つ。ブラウザーには同じURLを渡す事になる。 (http://linuxMachineIP:8080/)最初にアクセスしたものがキャッシュされちゃうので、 場所を移動しても、最新のコンテンツを表示しない。ゆえに、ブラウザーでリロードする事。

VirtualBoxで使っている場合は、設定の所でネットワークを選び、高度な設定をクリックして ホスト側、ゲスト側のポート共、8080を指定する。そして、LinuxMachineIPの所は、WindowsのIPを指定してあげれば良い。