TidalCyclesをstackで確実にインストールする

Posted by Yuji Yamamoto(@igrep) on January 17, 2019Tags: Windows, TidalCycles

Link to
here
背景

先日teratailHaskellタグを眺めていたところ、下記のような質問がありました。

Haskell - networkパッケージがうまく機能しない|teratail

TidalCyclesという、Haskell製の内部DSLでシンセサイザーの演奏ができるライブラリーのインストールがうまくいかない、という質問です。
networkパッケージがインストールできていない、ということなのでcabal hellにでもハマったのかな、と思ったのですが、類似しているとおぼしき報告(と、こちら)を読む限り、どうもGHCのインストール自体に何か問題があるように見えました。

もう当の質問者はHaskell Platformをインストールすることで解決したそうですが、いずれにしても、我々Haskellerとしては、stackなりcabal new-installなりといった、慣れた方法でインストールできた方がサポートしやすいですし、きっと確実です。
というわけで今回はstackでのインストールに挑戦してみました。
すでにstackをインストールしているというHaskell開発者は多いでしょうし、そうした方がTidalCyclesを使いたくなったときの参考になれば幸いです。

結論から言うとほとんど問題なくできたんですが、以下のtweetで述べたポイントにご注意ください。

Link to
here
実行した環境

  • Windows 10 Pro 64bit ver. 1809
  • stack --version: Version 1.9.1, Git revision f9d0042c141660e1d38f797e1d426be4a99b2a3c (6168 commits) x86_64 hpack-0.31.0
  • TidalCycles: 1.0.6
  • SuperCollider: 3.10.0, 64bit
  • Atom: 1.34.0
  • Atomtidalcyclesプラグイン: 0.14.0

Link to
here
各種依存パッケージのインストール

冒頭に挙げた質問をされた方が参考にしたページ TidalCyclesのインストール2018年版 - Qiita では、Chocolateyを使ったインストール方法を紹介していますが、この方法では、直接GHCWindows向けtarballをダウンロードしてインストールしているようです。
私が知る限り特にその方法でも問題はないはずなんですが、なぜか質問者が挙げたようなエラーが発生してしまいます。
また、TidalCyclesが実行時に依存しているSuperColliderSuperDirtといったソフトウェアを、別のChocolateyのパッケージに分けることなく、TidalCyclesのインストールスクリプトで直接インストールしているようです(詳細はChocolateyのパッケージ情報に書かれたchocolateyinstall.ps1を参照されたし)
そのため、ChocolateyTidalCyclesをインストールしようとすると、問題のあるGHCと、SuperColliderなどの依存パッケージを一緒にインストールしなければなりませんし、SuperColliderSuperDirtだけをChocolateyでインストールすることもできません。

なので、ここは素直にTidalCycles公式のWikiに書かれた方法に従ってSuperColliderSuperDirtをインストールしつつ、Haskell関連のものだけstackでインストールしようと思います。

Link to
here
TidalCycles公式のWikiそのままの手順

⚠️行く先々でWindowsのファイアウォールの警告が出るかと思います。適当に承認しちゃってください!⚠️

  1. SuperColliderを公式サイトからインストールします。
    今回は「Windows」の箇所に書いてある「3.10.0, 64-bit (no SuperNova)」というリンクをクリックしてダウンロードされた実行ファイルでインストールしました。
  2. Atomも公式サイトからインストールしました。
    後で触れますTidalCyclesの対話環境を、Atom上で呼び出すためのプラグインがあるためです。他のエディタ向けのプラグインもありますが、公式サイトで紹介していたのはAtomなので、一番これがサポートされているのでしょう。
  3. GitPrerequisitesとして挙げられていますが、すでに私の環境に入っているので今回は特に何もしていません。なければ普通にGit for Windowsを入れるのが無難かと思います。
  4. SuperDirtのインストール
    1. SuperColliderをスタートメニューから起動します。
    2. ウィンドウの左側にある「Untitled」と書かれた箇所の下がSuperColliderのエディタになっているようです(色がわかりづらい!)
      そこにinclude("SuperDirt")と書いて、「Shift + Enter」を押せば、SuperDirtのインストールが始まります。
    3. 次のセクションでSuperDirtを起動する前に、一旦SuperColliderを終了させましょう。
  5. Atom向けtidalcyclesプラグインのインストール
    • 面倒なので省略します。他のプラグインと変わらないはずなので適当に検索してください!

Link to
here
TidalCycles公式のWikiとは異なる手順

ここからはこの記事特有の手順です。
最近のHaskell開発者は、stackというツールを使って開発環境を整えることが多いですので、冒頭の予告通りここではstackを使います。
ちなみに、現在はHaskell Platformにもstackが添付されていますが、Haskell Platformに含まれる、GHCstackを使うことでも簡単にインストールできるため、stackのみをインストールすれば十分です。
なお、stack自体のインストール方法については拙作の「失敗しながら学ぶHaskell入門」のREADMEをご覧ください。
Windowsではインストーラーをダウンロードして実行するだけで十分でしょう。

stackのインストールが終わったら、次の手順を踏んでください。

Link to
here
stackでのTidalCyclesのビルド

stackTidalCyclesのビルドをするには、C:\sr\global-project\stack.yamlというファイルを、下記でコメントしたように書き換えてください。

# ... 省略 ...
packages: []
resolver: lts-12.26 # <= ここを編集

extra-deps:         # <= この行と、
- hosc-0.17         # <= この行を追記

簡単に編集した内容について解説させてください。

まず、resolver:で始まる行ですが、これは「LTS Haskell」という、パッケージの一覧のバージョンを指定するものです。
LTS Haskell」は、「確実にビルドできるバージョンのパッケージをまとめた一覧」です。
LTS Haskellのメンテナーの方々は、毎日登録された大量のパッケージをまとめてビルド・テストしてみることで、実際に登録されたバージョンのパッケージのビルドとテストが成功することを確認しています。
なので、このLTS Haskellに登録されているバージョンのパッケージを使う限りは、私たちは安心してビルドができると言うことです。

なぜLTS Haskellのバージョンを書き換えたのかというと、それは、LTS Haskellには実際にはパッケージの一覧だけでなく、それらをビルドできるGHCのバージョンも含まれているからです。
したがって、LTS Haskellのバージョンを指定する、ということは、そのままインストールするGHCのバージョンも指定することになります1
実は特に今回の場合、インストールするGHCのバージョンを指定しなければ、ビルドできない可能性が高かったのです。
現在の最新のLTS Haskellに登録されているGHCのバージョンは「8.6.3」ですが、残念ながらこのバージョンのGHCには、Windows版のみにおいて深刻なバグがあります。
実際にTidalCyclesをビルドする際にこのバグに遭遇するかは確かめてませんが、内容からして遭遇する確率が高そうであるという点と、遭遇するとビルドができないという点を考慮して、念のため確実にビルドできるバージョンのGHCを指定しておきました。

そして、extra-depsという項目は、ビルドしようとしているパッケージ(今回の場合tidalパッケージ)が依存しているパッケージが、LTS Haskellに登録されていない場合に指定するものです。
tidalパッケージ ver. 1.0.6のパッケージ情報を確認すると、確かにhoscというパッケージに依存していると書かれていますね!
残念ながらこのhoscパッケージは今回指定した、LTS Haskellver. 12.26には登録されていないので、上記のとおりextra-depsに明記しておいてください。

C:\sr\global-project\stack.yamlの編集が終わったら、

stack build tidal

と実行しましょう。
初回はGHCのインストールも含めて行われるので、結構時間がかかると思います。

ちなみに、stack install tidalと実行してもいいですが、stackの仕様上、特に結果は変わりません。
stack installは、実行ファイルがついたパッケージをビルドしてPATHにインストールするためのコマンドなので、tidalのように実行ファイルがないパッケージでは意味がありません。

Link to
here
Atomのプラグインの設定

続いて、Atomtidalcyclesプラグインの設定をしましょう。
stackは使用するGHCを、前述のstack.yamlに書いたLTS Haskellのバージョンに応じて切り替える関係上、PATHの通ったところにGHCをインストールしません。
そのため、Atomtidalcyclesプラグインに、stackがインストールしたGHCを認識させるには、下記のように設定を書き換える必要があります。

  1. Atomを起動し、「File->Settings」の順にメニューをクリックして、Atomの設定画面を開きます。
  2. 画面左側の「📦Packages」と書かれた箇所をクリックすると、インストールしたAtomのプラグインの一覧が表示されるはずです。
  3. 一覧から「tidalcycles」を探して、「⚙️Settings」をクリックします。
  4. Ghci Path」という設定項目があるので、それをstack exec ghciに書き換えてください。

Link to
here
使い方・動作確認

Link to
here
TidalCyclesを起動する度に必要になる手順

公式サイトのこちらのページに対応しています。

  1. SuperDirtの起動
    1. SuperColliderをスタートメニューから起動します。
    2. 先ほどinclude("SuperDirt")と入力した、SuperColliderのエディタに、今度はSuperDirt.startと入力して、同じく「Shift + Enter」しましょう。
      SuperDirtが起動します。
  2. Atom上でのTidalCyclesの起動
    1. Atomを起動して、拡張子が.tidalなファイルを開くか作成します。
    2. メニューを「Packages->TidalCycles->Boot TidalCycles」の順に選択してください。
    3. 画面下部でGHCiが起動し、TidalCyclesの式を実行するのに必要なパッケージのimportや、importでは賄いきれない関数の定義などが自動的に行われます。
      • BootTidal.hsというファイルの中身をGHCiに貼り付けているみたいです。
  3. 動作確認のために、適当なTidalCyclesの式 — 例えば公式サイトのWikiどおりd1 $ sound "bd sn" — を入力して、入力した行にカーソルを置き、「Shift + Enter」を押しましょう。
    1. 入力した式が画面下部で起動したGHCiに送信され、実行されます。うまくいっていれば音が鳴るはずです。
    2. 停止させたいときは、d1 silenceと入力して同じく「Shift + Enter」を押してください。
  4. より詳しいTidalCyclesの使い方は、TidalCyclesのチュートリアル1 - Qiitaなど、他の方が書いた記事を検索してみてください。

Link to
here
ハマったこと

Link to
here
SuperDirtが見つからない!」という趣旨のエラーが出た

正確なエラーメッセージは申し訳なくも忘れてしまったのですが、SuperCollider上でSuperDirt.startと入力した際、エラーになることがあります。
この場合、SuperColliderを再起動するのを忘れている可能性がありますので、再起動してみてください。
SuperDirtのインストールを終えた直後では、まだSuperDirtは利用できないのです。

Link to
here
Atom上でTidalCyclesを起動した際、parse error

先ほどの「Atom上でのTidalCyclesの起動」という手順で、parse error (possibly incorrect indentation or mismatched brackets)というエラーに出遭うことがあります。
そのままTidalCyclesの式を入力して「Shift + Enter」しても、Variable not in scope: d1 :: ControlPattern -> tなどというエラーになってしまうでしょう。
これは、前のセクションで触れたBootTidal.hsというファイルをGHCiが読み込む際に、エラーになってしまったからです。

原因はいろいろあり得るかと思いますが、私の場合、~/.ghciというGHCiの設定ファイルに:set +mという行を加えていたためでした。
まず、~/.ghciは、GHCiが起動するときに必ず読み込まれるファイルです。
必ず有効にしたい言語拡張や、:set +mのようなGHCiの設定を記載しておくファイルとなっています。要するに~/.vimrcなどと似たようなものですね。
そして:set +mは、GHCiで複数行の入力を有効にするためのものです。
GHCi上で:set +mと実行すると、GHCiは入力した行を見て「あっ、この入力はまだ続きがありそうだな」と判断したとき、次の行を自動で前の行の続きとして扱うようになります。
そして、その場合入力の終了をGHCiに伝えたい場合は、空行を入力しなければなりません。
結果、BootTidal.hsを読み込む際に、空行が入力されないため、意図しない行が「前の行の続き」とGHCiに認識されてしまい、parse error (possibly incorrect indentation or mismatched brackets)となってしまうようです。

仕方ないので、直すために~/.ghciを開いて:set +mと書いた行をコメントアウトするか削除しちゃいましょう。
再びAtomで「Packages->TidalCycles->Boot TidalCycles」の順にメニューをクリックすれば、今度は該当のエラーがなく起動するかと思います😌。

このエラーは、特にすでにHaskellの開発環境を導入している方で遭遇するケースが多いかと思います。ご注意ください。

Link to
here
SuperDirtを起動し忘れていても何もエラーが起きない

表題の通りです。
困ったことにSuperDirtを起動し忘れた状態でd1 $ sound "bd sn"などの式を実行しても、特に何のエラーもなく、音も鳴りません。
(サーバーとして起動しているべき)SuperDirtに接続し損ねたんだから、何かしらエラーが表示されてもいいはずなんですが、困ったことにウンともスンとも言いません😰。
と、言うわけで、何のエラーもなく音も出なかった場合は、SuperDirtを起動し忘れてないか確認しましょう。

Link to
here
おわりに: Haskell開発者として見たTidalCycles

※ここから先はおまけ + 宣伝です。TidalCyclesをインストールしたいだけの方は適当に読み飛ばしてください

ここまで、stackという、昨今のHaskellerの多くが好んで利用するツールで、TidalCyclesを利用する方法を説明しました。
TidalCyclesの公式サイトのWikiにはこの方法は書かれてませんが、より確実なインストール方法として、覚えておいていただけると幸いです。
すでにHaskellの開発環境をインストールしている方にも参考になるかと思います。

ところで、ここまでTidalCyclesを自分でインストールしてみて、Haskellerとしていくつか気になった点があります。
TidalCyclesは、Haskell製の内部DSLとしては、ちょっと変わっているように感じました。

それは、TidalCyclesが「標準」として提供している関数を実行する際、tidalパッケージに含まれるモジュールをimportするだけでなく、BootTidal.hsというファイルを読んで、追加の関数を定義する必要がある、という点です。
大抵のHaskell製の内部DSLは、そんなことしなくてもモジュールをimportするだけで使えるようになっていますHspecとかlucidとかclayとかrelational-recordとか)
つまり本来ならばわざわざ、BootTidal.hsのような、GHCiが読み込む専用のファイルを用意しなくとも良いはずなのです。
このBootTidal.hsAtomのプラグインの設定で簡単に切り替えることができるものなので、もし間違ったファイルに設定してしまったら、言語の標準にあたる関数がおかしな動作をすることになりかねませんし、あまり良いやり方だとは思えません。本来なら設定に混ぜて書くべきものではないでしょう。

なぜTidalCyclesはこんな仕様になっているかというと、それにはある意味Haskellらしい制約が絡んでいると推測されます。
Atom上でTidalCyclesを起動する、というのは、実際にはGHCiを起動して、BootTidal.hsというファイルを読み込ませる、ということなのでした(事実、Atomなどのエディターを介さなくとも、お使いのターミナルエミュレーターからghciコマンドを起動してBootTidal.hsファイルの中身をコピペするだけで、TidalCyclesは利用できます)
そのBootTidal.hsの中身を見てみると、サンプルで実行したd1という関数が、下記のように定義されていることがわかります。

-- ... 省略 ...
import Sound.Tidal.Context

-- total latency = oLatency + cFrameTimespan
tidal <- startTidal (superdirtTarget {oLatency = 0.1, oAddress = "127.0.0.1", oPort = 57120}) (defaultConfig {cFrameTimespan = 1/20})

let p = streamReplace tidal

-- ... 省略 ...

let d1 = p 1
let d2 = p 2
let d3 = p 3
-- ...

tidal <- startTidalで始まる行で、TidalCyclesの初期化を行っていると思われます。
初期化の際には、サーバーとして起動しているSuperDirtへの接続設定(この場合127.0.0.157120番ポートへ接続している)を渡しているようです。
恐らくこのstartTidal関数が、SuperDirtへ接続し、代入したtidalという変数に、SuperDirtへの接続を含んでいるんでしょう。
そして、let p = streamReplace tidalという行で、そのtidalstreamReplace関数に部分適用することで、pSuperDirtへの接続を参照できるようにしています。
さらに、let d1 = p 1などの行で、前の行で定義したpに整数(シンセサイザーの番号だそうです)を部分適用することで、結果、d1d2などの関数へ、間接的にtidalを渡すことになります。

つまりd1d2などの関数は、何らかの形で、SuperDirtへの接続情報を持っているのです。
DSLとして、d1d2などの関数に毎回接続情報を渡すのは煩雑だと考えたためでしょう。
残念ながら、通常のHaskellがそうであるように、外部のサーバーに接続した結果取得されるものを、関数が暗黙に参照できるようにしたい場合、 — つまり、今回のようにユーザーが接続情報を明示的に渡すことなく使えるようにしたい場合 — 少なくともパッケージをimportするだけではうまくいきません2
BootTidal.hsのように、SuperDirtのような外部に接続する処理を、GHCiの実行時に書かなければならないのです。

しかし、tidal <- startTidalの行で作られるSuperDirtへの接続情報をd1などの関数が暗黙に利用できるようにすることは、実際にはBootTidal.hsで行っているような方法を使わなくともできます。
そうすることで、BootTidal.hsを変なファイルに切り替えてしまって、d1などの関数の定義が間違ったものになってしまう(あるいはそもそも定義されなくなってしまう)リスクを回避できます。
具体的には、下記のような方法が考えられます。
申し訳なくも私はこれ以上TidalCyclesに入れ込むつもりもないので、誰かTidalCyclesを気に入った方が適当に提案するなりパッチを送るなりしてみてください(他力本願😰)

  • GHCiの中でReaderTを使う
    • Haskellで「関数に渡した引数を暗黙に利用できるようにする」といえば、やはりReaderTモナドトランスフォーマーが一番オーソドックスな方法でしょう。
      実はGHCi上では、IO以外のモナドのアクションでprintすることができます。
      You can override the monad that GHCi usesというRedditのスレッドでは、ReaderTを使ったサンプルが紹介されています。
      これと同じ要領で、GHCi-interactive-printというオプションに、tidalReaderT経由で渡してから結果をprintする関数を設定しましょう。
      あとはd1などをReaderTのアクションにするだけで、それらをBootTidal.hsから消し去ることができます。
      残念ながらこの方法を使うと、GHCiに与えた式の結果がすべて当該のモナドのアクションになっていなければならなくなるため、例えば単純な計算結果でさえreturnをいちいち書かないといけなくなります。しかし、TidalCyclesの利用方法を見る限り、大きな問題にはならないだろうと思います。
  • ImplicitParamsというGHCの言語拡張を使う
    • GHCには、ImplicitParamsという、もっと直接的にこれを実現する言語拡張があります。文字通り、暗黙の引数を実現するための拡張です参考
      これを利用して、例えばd1?tidal :: Stream => ControlPattern -> IO ()のように型宣言しておき、?tidal(頭に?を付けたものが暗黙の引数となります)を暗黙の引数として参照するようにしましょう。後はGHCiの起動時に?tidalを定義すれば、?tidalの後にd1などを定義する必要がなくなるので、BootTidal.hsはもっとコンパクトに済むはずです。
  • その他、unsafePerformIOTemplate Haskellなど、ちょっと危ない手段を使う
    • こちらについては詳細を割愛します。d1などの再利用性が下がるので、おすすめしません。

TidalCyclesの技術的な側面で気になった点は以上です。
ちょっと難しい話になってしまいましたが、これを機会に、Haskellそのものへの興味を持っていただけると幸いです。
素晴らしいことに、TidalCyclesそのものはHaskellを知らなくてもそれなりに使えるようになっているようですが、Haskellを知った上で使えば、より簡単にトラブルシューティングができるようになりますし、TidalCyclesをより柔軟に使えるようになるでしょう。

もし、今回の記事やTidalCyclesをきっかけにHaskellを勉強してみたいと思ったら、Haskell-jp Wikiの日本語のリンク集を読んで、自分に合う入門コンテンツを見つけてみてください!
それから、何か困ったことがあればHaskell-jpSlack Workspaceにある、#questionsチャンネルで質問してみてください。
登録はこちらからどうぞ!

それでは2019年もHaskellTidalCyclesHappy Hacking!! 🎶🎶🎶


  1. どのバージョンのLTS HaskellでどのバージョンのGHCがインストールされるかは、LTS Haskellを管理しているStackage」というウェブサイトのトップページにある、「Latest LTS per GHC version」というセクションをご覧ください。↩︎

  2. 後で軽く触れる、Template Haskellという邪悪なテクニックを使わない限りは。↩︎