ここは俺の備忘録だ

少なくとも日本語での言及が少ない話をするつもりです

runSTにおける関数合成の失敗—Higher Rank Typesにおける型推論の落とし穴

余談

2つの事柄を題に要約する言い回しとして「あるいは」以外を見つけないとブログの題があるいはだらけになってしまう…

本題

より高ランクな型を低いランクの型パラメータで表すことはできない。 従って通常のランク1であるような関数somethingについて、something . runSTのような関数合成は上記ツイートのようなエラーを起こす。 しかしそのままだとrunST $ doが使えず非常に不便であるため、GHC7から$について推論する特殊ルールが追加されている。

これもまたHaskellに数多ある知っていれば当然であるが、知らねば型エラーから類推するのは難しい一例と言える。 まぁともあれ、ランク2の型はSTのスコープ制限は勿論のこと、 AllowAmbiguousTypes拡張と組み合わせてfromIntegralした数値を使い回すのに非常に便利だったりする(申し訳程度の擁護)。

関数適用演算子$は空気の如く重要であるため、色々特別扱いを受けている。 例えばLevity Polymorphismが知られている。

stackoverflow.com

Haskell/GHCスクリプティング時の実行ファイルパス取得、あるいはGHCが公開するC言語APIは何処にあるか?

問題

runhaskell/runghcを用いることにより、Haskell/GHCではshebangを書き込んだスクリプティングが可能である。 特にStackの登場以降、依存性の解決も容易となり非常に気軽に扱えるようになった。

github.com

個人的にシェルスクリプトは避けていきたいため、これは渡りに船だ。しかし基準となる実行ファイルのパスが取得できないという問題があった。 System.EnvironmentのgetExecutablePath関数だとビルドツールの作業ディレクトリを見に行ってしまうのである。

解決策

FindBinパッケージのgetProgPath関数はスクリプティング時に実行ファイルのパスを取得し、コンパイルするプログラムであれば実行するバイナリのパスを取得する。一見その古さに驚くがlts-8.21(GHC 8.0.2)にて動作を確認した。

FindBin: Locate directory of original program

この実装が以下となる。

github.com

さて、ここでヘッダファイルの読み込み無しにforeign import ccallされているgetProgArgv関数らは一体何者だろうか? (なおgetProgArgvはSystem.Environmentでも使われている)

これらの正体はGHCghc/includes/RtsAPI.hにて公開している関数である。詳細は以下が詳しい。 この疑問はHaskell-jpのSlackにて@hiratara氏に答えをいただいた。

Commentary/SourceTree/Includes – GHC

実際のコードはgithubの方が見やすい。

github.com

そろそろ象牙の塔の外側が見えてきたようだ。

Haskell/GHCでプロジェクト上のデータファイルのパスを取得する

近況

GHCでWord2Vec(fasttextベース)を実装しており、詰まりと知見獲得を繰り返している。 新機能に関する言及は多いのに実際の開発知見となるとヒット率が目減りしてしまうのは苦しい所であるが、 現代ではStack OverflowやHaskell-jpという素晴らしい集まりがあり、ハードルも随分下がったと言えるだろう。 この詰まりはそのままネット上の情報の薄さでもあるため、これをブログに残していくことでコミュニティへの還元とする。

仮定

(間接的にでも)Cabalの使用を仮定する。現代的な環境であるStackを用いている人が殆どであるから問題無いだろう。

解決策

cabalのdata-filesフィールドを用いる。詳細は以下。

Cabal User Guide: Developing Cabal packages

data-filesフィールドにパスを記述するとPaths_<パッケージ名>モジュールにてgetDataFileName関数が使えるようになる。hpackでも同じフィールドが利用可能。 stackから入ったユーザ(私)はcabalで一回は躓くのでマニュアルはしっかり読んでおきたい。

Rustのreference/dereferenceまとめ

この記事は TUT Advent Calendar 2016 - Adventar 5日目の記事です.

3日目の記事: 5mmくらいわかる競馬講座~国庫から出金~(CC他 - 自分用

(@naruhodo2015さんへ: Rustは関数型プログラミング言語では無いですよ ><)

最近,友人et al.と適当な映像を垂れ流しながらもくもくと作業する会が不定期で開かれる様になりました.そこでRustを書いていた友人曰く「Rustのreference/dereferenceの対応とその演算子がわかりにくい」との事で,まとめてAdCに貼ることに.

f:id:Nnwww:20161205002404j:plain

(mut &になってるのは徹夜で脳死していた影響です (´・_・`))

一番わかりにくいのはLHSとRHSに現れる&の役割が異なる事です.LHSで登場する「パターン」では参照の付加をrefパターンで行えるようになっており, let ref x = ...等とすると右辺の評価結果に参照を付加した物をxとして束縛します.一方でdereferenceを行うのが&xパターンで,これはref xと比べれば自然な使われ方でしょう. ある値xの参照が入ってくるので,&xに分解されると考える訳です.

RHSで登場するのは単項演算子である事に注意します.するとこれがC/C++に由来するreference/dereferenceの記法である事が分かると思います.

構造体に結び付くメソッド構文の場合,参照を打ち消すようにdereferenceが行われます*1.実用では参照が付加される関数がどれなのか(iterなど),ライフタイムがある場合はどの引数に基づくのか*2,use after freeが起きる場合スコープをどう限定するかなどなどを気にしつつreference/dereferenceの対応を取っていく必要があります.コンパイラと協力しながらdata race安全なコードを詰めていくプログラミングがRustの醍醐味でありつらみでもあるわけですね.

filterのように,イテレータには二重に参照を付加して述語へ渡してくるものが幾つかありますが,この理由は分かっていません.詳しい型がいらっしゃったら是非コメント宜しくお願いします.

それでは,皆さん良いRustプログラミングを!

6日目は @jp3cyc さんの Ogaki Mini Maker Fairに行ってきた | jp3cyc's blog です.

*1:`Deref` による型強制

*2:HashMap等,使い回しが起きる所で重要になります.e.g. コンテナの値を取って→入れて→エラー (lifetimeとかの話) - Qiita

Rustでmpsc::Receiversを1スレッドかつ並行に待つ方法

イベントハンドリング等である程度込み入ってくると単一の膨れ上がったenumを分割し,それぞれのモジュールに分けたりする.すると複数のenumが一堂に会する場が何処かに発生する筈だ.

ただ.std::sync::mpsc::Receiverのrecvやiterブロッキングであるため,enum毎に用意した複数のReceiversで逐一止まってしまうのは効率が悪い.

ではReceivers毎にスレッドを立てれば良いかというと,これも所有権が持っていかれる等面倒が多いし,そもそも1つのReceiversを待つ為だけにそれぞれカーネルスレッドを立てるのは無駄な気がする.

というわけで以下は個人的に分散処理のOSSを眺めていた際提案されていたそういうモチベの解決策,のアルゴリズムの再現である.

これはこのままでは動かない.流れと実装しなければならないものの備忘録という事にしてほしい.

#[derive(Debug, Clone)]
enum EvCat {
    Net,
    UI,
}

#[derive(Debug)]
enum NetEv {
    Success,
    Failure,
}

#[derive(Debug)]
enum UiEv {
    ScreenTransition,
    Terminate,
}

struct EvSender<Cat, Subset> {
    ev_tx: mpsc::Sender<Subset>,
    cat: Cat,
    cat_tx: mpsc::Sender<Cat>,
}

fn main() {

    let (cat_tx, cat_rx) = std::sync::mpsc::channel();
    let (net_ev_tx, net_ev_rx) = std::sync::mpsc::channel();
    let (ui_ev_tx, ui_ev_rx) = std::sync::mpsc::channel();

    let ui_ev_sender = EvSender::<EvCat, UiEv>::new(ui_ev_tx, EvCat::UI, cat_tx.clone());

    let nw_ev_sender = EvSender::<EvCat, NetEv>::new(net_ev_tx, EvCat::Net, cat_tx);

    let joinh = thread::spawn(move || {
        for it in cat_rx.iter() {
            match it {
                EvCat::Net => {
                    if let Ok(net_ev) = net_ev_rx.try_recv() {
                        match net_ev {
                            NetEv::Success    => { /* Do Something */ },
                            NetEv::Failure => { /* Do Something */ },
                        }
                    }
                },
                EvCat::UI => {
                    if let Ok(ui_ev) = ui_ev_rx.try_recv() {
                        match ui_ev {
                            UiEv::Terminate       => break,
                            UiEv::ScreenTransition => { /* Do Something */ },
                        }
                    }
                }
            }
        }
    });

    nw_ev_sender.send(NetEv::Connected).unwrap();
    ui_ev_sender.send(UiEv::CreateDirectory).unwrap();
    ui_ev_sender.send(UiEv::Terminate).unwrap();
}

Receiverの try_recv 関数だけは唯一ノンブロッキングメソッドであり, 値が届いて無ければResult::Errを返してくれる.cat_rxのiterブロッキングは仕方ないのでスレッドで打ち消す.

EvSenderの実装だが,これはnew関数の値を保持するだけの構造体である. 使われているのは最後のsendの部分で(このページでは実装されていないので想像して欲しい),先のEvSenderに対して以下の様なimplになる.

fn send(&self, event: Subset) -> Result<(), /*error types*/> {
    if let Err(err) = self.ev_tx.send(event) {
        /*error handling*/
    }
    if let Err(err) = self.cat_tx.send(self.cat.clone()) {
        /*error handling*/
    }
    Ok(())
}

ev_txへ分割されたenumのサブセットをsendして様子を見た後,続けてcatを流す. すると理想的にはmatchする値が流れ着くのに続いてforのcat_rx.iter()が作動し,enumをハンドリングするという流れになる.

この方法ならばenumのサブセットを各モジュールに分散させてそれぞれ使っている場合でも, 全体の集合を定義しておく事で,まとめてハンドリングできるようになる. 今回の題材はenumによるイベントのハンドリングであったが,それに限らず複数のReceiversの集約として有用なモデルだと言えるだろう. これまで代数的データ型を使う機会は頻繁に有ったが,所有権を意識させられるとまた新しい用途や工夫が現れ中々面白いものである.

Mac版SpacemacsのPowerlineが正しく描画されない問題

この問題についてだがissueが上がっており既知の問題ではあるものの, あの長大なQ&Aの深奥に書かれているためここに残す.

(というか何故既知なのに公式のPrerequisitesが更新されないんだろうか)

emacs-mac-portをbrewで導入し,後は公式の通りspacemacsをgit cloneするプロセスを踏めば良い.

github.com

brew tap railwaycat/emacsmacport
brew install emacs-mac --with-gnutls --with-imagemagick --with-spacemacs-icon

before:

after:

最近はspacemacsを常用している. 一時期evil + emacsを試していた自分としては非常に理想的な環境であり, かれこれ5年程(neo)vimを使っていたもののすっかり乗り換えてしまった. レイヤについて調べたらまとめて記事にしたい…と考えている.

merilnの補完パッケージ初期化スクリプト

merilnは便利だがプロジェクト毎に一々書くのもだるい。 そう思って前に調べたら調度良いタイミングでそういうスクリプトを書いてくれている人が居た。

Create .merlin file for a project with all your ocamlfind packages and .opam sources in there · GitHub

このgistのスクリプトはopam内のパッケージ全てを入れてしまうので、コメントにて他の方が手を加えてくれた指定版を用いると良い。 埋もれてしまうには勿体無いので、分散としてここでも言及しておく。

#!/bin/sh

# Add PKG's:
ocamlfind list \
    | awk '{ print "PKG "$1 }'

# See https://github.com/the-lambda-church/merlin/wiki/Letting-merlin-locate-go-to-stuff-in-.opam
find ~/.opam -name '*.cmt' -print0 \
    | xargs -0 -I{} dirname '{}' \
    | sort -u \
    | awk '{ print "S "$0"\nB "$0 }'

# e.g.) merlin-init.sh | grep batteries > .merlin

私の環境では以下のようになる。

PKG batteries
S /Users/nnwww/.opam/4.02.3/build/batteries.2.4.0/_build
B /Users/nnwww/.opam/4.02.3/build/batteries.2.4.0/_build
S /Users/nnwww/.opam/4.02.3/build/batteries.2.4.0/_build/build
B /Users/nnwww/.opam/4.02.3/build/batteries.2.4.0/_build/build
S /Users/nnwww/.opam/4.02.3/build/batteries.2.4.0/_build/src
B /Users/nnwww/.opam/4.02.3/build/batteries.2.4.0/_build/src

単純な方法なのでこれでcoreを指定するとppx_core等も対象に入れてしまうが、実用には十分だろう。