なんかActorができたので

qiita.com 23日目


Rustのコードを書いて遊んでいたら、「あっこれActorじゃん」となって、いつの間にかActorができていたのでそれについて書きます。

[2016/12/24追記] 指摘ありましたが、これをActorと呼ぶのはまずかったです。

今回作ったものは以下のcruxというリポジトリです。 github.com

Actorの作りかた

Actorトレイトを実装する事によって作ります。

enum PrintAction {
    Set(i32),
    Print,
}

#[derive(Clone)]
struct PrintActor {
    value: i32
}
impl Actor for PrintActor {
    type Action = PrintAction;
    fn receive(&mut self, action: Self::Action) {
        match action {
            PrintAction::Set(value) => self.value = value,
            PrintAction::Print => println!("{}", self.value),
        }
    }
}

このPrintActorではPrintAction::Printを受け取った時に、最後に受け取ったPrintAction::Set(value)valueを表示します。

Actorの起動はspawn_actorで行います。

let mut print_actor = spawn_actor(PrintActor { value: 0 });
print_actor.dispatch_sync(PrintAction::Print); 
// => 0
print_actor.dispatch_sync(PrintAction::Set(3);
print_actor.dispatch_sync(PrintAction::Set(5);
print_actor.dispatch_sync(PrintAction::Print); 
// => 5

構造体の中に別のアクターを含める事でActor同士での通信が行えます。

#[derive(Clone)]
struct SumActor {
    sum: i32,
    next: ActorStore<PrintActor>,
}
impl Actor for SumActor {
    type Action = i32;
    fn receive(&mut self, action: i32) {
        self.sum += action;
        self.next.dispatch_sync(PrintAction::Set(self.sum));
    }
}

SumActorは受け取った値を次々に足していって、最新の値をPrintActionに送り続けます。

さらに、1を足して別のActorに送るIncrementActorと二乗するSquareActorを作ります。

#[derive(Clone)]
struct IncrementActor {
    next: ActorStore<SquareActor>
}
impl Actor for IncrementActor {
    type Action = i32;
    fn receive(&mut self, action: i32) {
        self.next.dispatch_sync(action + 1);
    }
}

#[derive(Clone)]
struct SquareActor {
    next: ActorStore<SumActor>
}
impl Actor for SquareActor {
    type Action = i32;
    fn receive(&mut self, action: i32) {
        self.next.dispatch_sync(action * action);
    }
}

最後にこれまでに作ったActorを使って0(0+1)^2 + (1+1)^2 + (2+1)^2 + (3+1)^2を表示するプログラムを作ると以下のようになります。

let mut print_actor = spawn_actor(PrintActor { value: 0 });
let sum_actor = spawn_actor(SumActor {
    sum: 0, next: print_actor.clone()
});
let square_actor = spawn_actor(SquareActor {
    next: sum_actor.clone()
});
let mut increment_actor = spawn_actor(IncrementActor {
    next: square_actor.clone()
});

print_actor.dispatch_sync(PrintAction::Print);
// => 0
increment_actor.dispatch_sync(0);
increment_actor.dispatch_sync(1);
increment_actor.dispatch_sync(2);
increment_actor.dispatch_sync(3);
print_actor.dispatch_sync(PrintAction::Print);
// => 30

全体のソースコードcrux-examples/main.rs at master · ryo33/crux-examples · GitHubにあります。

このような感じでActorっぽいものが簡単に作れて楽しいです。

ありがとうございました。

最近やりたいと思ったこと

www.adventar.org

これの22日目です。


ネタが思いつかなかったのでやりたい事の一覧を書くだけの記事にします。 共通のやりたい事があって、共同でやりたいというものがあれば是非やりましょう。

  • ロックスターになる
  • ゾンビゲームを作る
  • ゾンビ映画を撮る
  • アヒル言語を作る
  • なんかいろいろあって起業する
  • 新しいコミュニケーション様式を考える
  • 新しい曖昧表現を考える
  • 1000℃に熱した金床の動画を投稿して人気YouTuberになる
  • フリフラ
  • ゲーム実況動画をニコニコに上げる
  • ファボ欄2Vec
  • ログイン機能がないサイトで投稿の作者が自分であることを表明する方法
  • 同人誌を作る
  • なんかSNSを作る
  • 新しいコミュニケーション用の言語を発生させる
  • 君の名は。を観る
  • ドントブリーズを観る
  • バイオハザードファイナルを観る
  • ウォーキングデッド最新シーズンを観る
  • 廃墟探検をする
  • 競技プログラミングサーバーフレームワークを作る
  • ニコ生配信
  • Desel言語のvimプラグイン
  • なんかいろいろ入れて長時間加熱したら美味しかったみたいな料理
  • 完全食
  • 昔作ったゲームのソースコードの発掘
  • 鯨とおよぐ
  • 鯨のゲームで鯨を手に入れる
  • カレー屋を作る
  • 詩人になる
  • サトウのご飯半年分
  • 未成年飲酒ツイートかるた
  • Twitter朗読会
  • いいね欄朗読会
  • 未来予知Bot
  • 世界滅亡Bot
  • 「〜についてまとめよ」レポートの自動化
  • カードゲームを作る
  • ボードゲームを考案する
  • 小説を書く
  • Twitterをやめる
  • サンタのコスプレをしなくていいコンビニを探してバイ

redux-micro-actionsを作った

qiita.com これの17日目です。

なにをつくったか

middlewareで何かのactionに対して複数の小さなaction(この記事ではmicro actionと呼ぶことにします)をdispatchするようなコードで、 micro action単体がdispatchされてしまうとデータの整合性が崩れてしまうような場合があると思います。

今回作ったのは、それを防ぐためのものです。

できたもの

github.com

当初は静的型チェックを用いてやるつもりでしたが、時間が足りなかったため、 micro actionかどうかをactionのmetaプロパティーに入れてエラーは実行時に出すという方法になってしまいました。

つかいかた

使うのは以下の3つの関数です。

  • micro: actionCreator => microActionCreator
    actionCreatorがmicro actionを返すようにする
  • allowMicro: middleware => middleware'
    middleware内のstore.dispatchでmicro actionを許可する
  • denyMicro: option => storeEnhancer
    store.dispatchでmicro actionを禁止する
import {
  micro,
  allowMicro,
  denyMicro
} from 'redux-micro-actions'

const action1 = () => ({type: 'ACTION1'})
const action2 = micro(() => ({type: 'ACTION2'}))

const store = createStore(
  reducer,
  compose(
    applyMiddleware(
      middleware1,
      allowMicro(middlware2)
    ),
    denyMicro()
  )
)

こんな感じになります。

この例ではaction2がmicro actionになっているため、 micro actionのdispatchが許可されているmiddleware2内のstore.dispatch以外からdispatchするとエラーが出るようになっています。

さいご

いつかflowtypeとTypeScriptの型定義を追加できたらいいなとは思っていますが、 まだどちらも使ったことがないので結構後になると思います。

ありがとうございました。

ログイン機能がないサイトで投稿の作者が自分であることを表明する方法の考察

はじめに

もしかすると、僕はかなり見当違いなことを言っているのかも知れない。

もしそうなら教えていただけるとありがたい。Twitterはこちら@ryo33music

ここから

先日、またしょうもない新しいサイトのアイデアが浮かんできて「面倒臭いけど今度作るか〜」となっていた。

何が面倒くさいって、ログイン処理を作るのが面倒臭い。 勝手にOAuthとかをやってくれるライブラリを利用してGoogleとかのアカウントでログインできるようにしても、それでも面倒臭い。 なんでこんな面倒くさいことを、サイトを作るたびにやらないといけないのか。

今日の午前中のことだ。ふと、僕はなんでこのサイトにログイン機能をつけないといけないかということを考えてしまった。

考えてみると、僕がこのサイトにログイン機能をつけたい理由は 「コンテンツの作成者が自分が投稿したということを表明できる手段を用意したいから」 のただ1つしかなかった。(著作権とかは考えていない)

なんで、このたった1つの理由のためだけに、わざわざこんな面倒くさいことをしないといけないのか。

そして、今日の午前中は、これをログイン機能の実装以外で行うにはどんな方法があるのかを考えることに費やされた。 もちろん、授業はかなりしっかり聞いている。

すぐに、投稿を行うサイトの他に、ログインを行って使用するサイトを使用すればいいと気づいた。 ログイン機能のないサイト(以下サイト1)で投稿を行った後、ログインを行っている別サイト(以下サイト2)を用いて表明するという方法だ。

その表明方法を以下の条件と前提の上で考えた。

  • 手軽であること
  • 好きなタイミングで表明できる
  • 誰でも表明が正当か確かめることができる
  • 自分のコンテンツを勝手に他人が作ったことにされない
  • 他人のコンテンツを勝手に自分が作ったことにされない
  • どちらのサイトもテキストデータが投稿でき、それは他の人に書き換えられることがない
  • どちらのサイトもテキストデータ以外が投稿できるとは限らない
  • どちらのサイトも投稿前にURIが分かるとは限らない
  • 先に投稿したほうが表明として有効とみなされる

なんの難しいこともなく、ただハッシュアルゴリズム等を利用すればいい。

具体的な方法はこうだ。

方法1

  1. 他人から分からないような文字列を決める
  2. (1)で決めた文字列をハッシュ関数にかけ、それをサイト1での投稿内にそれとわかるように明記する
  3. 表明したくなったら、サイト2で(1)で決めた文字列と(2)で投稿したコンテンツのURIの二つを用いて表明する

非常にシンプルな方法だ。

ただ、「先に投稿したほうが表明として有効とみなされる」という前提が気持ち悪い。現代の技術では先に投稿したことを完全に証明することは不可能だ。 これを解消するためには「好きなタイミングで表明できる」か「どちらのサイトも投稿前にURIが分かるとは限らない」のどちらかの前提を捨てる必要がある。

投稿日中に追記: 「証明することは不可能」は言い過ぎだった。先に投稿した方が著作権侵害として消される動画サイトとか、パクツイばかりのツイッターとか、なんかそういうことを言いたかった。

「好きなタイミングで表明できる」という条件が無い場合は、このようにすればいい。

方法2

  1. 他人から分からないような文字列を決める
  2. (1)で決めた文字列をハッシュ関数にかけ、それを用いてサイト2で表明する(この時点ではコンテンツは投稿されていない)
  3. サイト1で(1)で決めた文字列と(2)で投稿した表明のURIの二つを明記する

そして「どちらのサイトも投稿前にURIが分かるとは限らない」が無い場合はこうだ。

方法3(サイト2はURIが投稿する前にわかる)

  1. 他人から分からないような文字列を決める
  2. (1)で決めた文字列とサイト2で投稿する時のURIハッシュ関数にかけ、それをサイト1での投稿内にそれとわかるように明記する
  3. 表明したくなったら、サイト2で(1)で決めた文字列と(2)で投稿したコンテンツのURIの二つを用いて表明する

方法2では投稿する順番を逆にして、コンテンツに表明のURIを含めることで、方法3ではハッシュの中身にURIを含めることでそれぞれ解決している。

方法3で表明のURIをサイト1で明記するのではなく、ハッシュする文字列に含めているのは「好きなタイミングで表明できる」という条件があるためだ。 それに、サイト2のURIが先願的に決まる可能性もある。

自動化

もっと言うと確認が自動でできると良い。 コンテンツと表明のURIを渡すだけで表明が有効であるか確かめてくれるサイトがあれば最高だ。

そのためには、やり方を規格化するしか無い。もちろん方法1〜3の全てをサポートしたもので、付加情報もつけられると良い。

友人が競技プロコン用のサーバーフレームワークを作る話

www.adventar.org

これの14日目です。


僕と友人で、競技プログラミングサーバーを作ることになっているので、その話をしようと思います。

経緯

僕たちが所属している部活で、何回か部内での競プロの大会的なものを開催しています。 しかし、現在使用しているサーバーには、対応言語を追加することが難しいこと、手元のマシン以外で動かすことが難しいこと、そもそも自動採点をしていないなどの問題がありました。

そのサーバーは、継続的に利用されることが想定されていなかった状況でその場限りのものとして作られたため、しょうがのないことでした。

そこで僕と友人(@NotFounds8080)で話し合って、簡単に扱える競プロサーバーを作ることになりました。 名前はなんとなくEagleになりました。 それが、一年以上前のことです。

特に開発が進むことなく、こんなにも時間が経ってしまいました。

Eagleが目指すところ

  • コンパイル、実行、採点が自動で行われる
  • 簡単にPaaSなどに置ける
  • 対応言語の追加が容易
  • 問題の定義が容易
  • プログラムの実行場所が可変(設定すればサンドボックス環境などでの実行もできる)
  • ライブラリとして利用できる(ログイン機能をつけただけの上位互換システムとかが簡単に作れる)

このような感じで作るつもりです。

進捗

現在の進捗は以下のような感じです。

この通り、開発は全く進んでいません。

これから

開発にはある程度の時間を要すると思います。

あまり時間があるわけではありませんが、これが完成して似たような活動を行っている部活動やサークルなどで使用してもらえたら嬉しいので、できるだけ頑張るつもりです。

最後に

タイトルが「友人が」となってますが、これは僕があまり競技プログラミングをやらないため、最後まで僕のモチベーションが保つか分からなかったからです。 本当に僕のこうゆうところ良くないと思ってます。本当に申し訳ないです。どうか許してください。

最後まで、読んでいただきありがとうございました。

残りカス

※ この下は最初に勢いで書いていた時、うっかり生成してしまった文章を自分用に残しているだけです。

今から1年くらい前のこと、僕たちシステム研究部は、パソコン室で部内競技プログラミングコンテストを行っていた。

競技が順調に進んでいく中、不満そうな顔をした部員が二人いた。

一人は僕である。僕は、その時使われていたコンテストサーバーではCとC++のプログラムしか使うことができないことに腹を立てていた。*1

そして、もう一人こそが、その時使われていたコンテストサーバーを作った男、池田(@NotFounds8080)だ。 彼は、自分が前に作ったシステムが継続して利用するつもりで作ったわけではなかったために、サポートする言語の追加などに手間がかかることを不満に思っていた。

不穏な空気が流れる中、僕の一言でその空気が一変した。「汎用的な競技プログラミングコンテストサーバーフレームワークを作らないか」と池田に持ちかけたのだ。

それからすぐに作り始めることが決まった。そして、それは後にEagleプロジェクトと呼ばれることになる。*2

*1:腹を立てていたのは嘘

*2:このセリフを書きたかっただけでした

退屈が創造を生む ~ まずはパンツを脱いでSNSを止めよう ~


Playing with a... comb - Mattias Eklundh Guitar Lesson

この動画を見た。 異常なギタープレイもさることながら、クリエイティブな思考に対する彼の考えが興味深かったので共有することにした。


彼によると、クリエイティブな発想のためには退屈が重要だという。

常にオンライン上にいてTwitterFacebookを見てメッセージを送るようなことをしていると、 退屈になるための時間も新しいアイデアを考えるための時間も何もかもを失ってしまう。

退屈であれば、頭を動かして退屈から抜け出す方法を見つける必要がある。そこで何かが生まれる。


実際に、この動画で紹介されている奏法(櫛奏法とでも呼ぶのだろうか?)は、 インターネットもパンツもない状況で、ギターを手にしているときに思いついたとのことだ。

僕は、ギターを弾くのが趣味で曲を作ったりもするのでこれに共感したわけだが、 これは他のいろいろな分野にも言えるのではないだろうか。


例えば、僕の趣味にはプログラムを書くこともあるが、それに対しても似たようなことが言えるだろう。

プログラムというのは、たいてい何かしらの目的や作りたい物があるわけだから、「何を作るか」というのを考えないといけない。 また、実装するときにも「どのように書けば良いコードになるか」ということを考える必要がある。 これらが"クリエイティブ"に相当するのかは、正直どうとも言えないないが、少なくとも、自分で考えて"アイデア"の形にするわけだ。

それらが思いつかないときに僕は、Twitterを見たり、ニコニコ動画のランキングを眺めたりしがちだが、それではいつまで経っても思いつかない。 最終的には、マウスから手を離してキーボードと向き合い、よく考える必要がある。


あとは、技術的な退屈も重要な気がしていて、「つまんねーから自分で作るか」みたいな気概がある人のほうが、面白いものを作れるのかもしれない。*1


兎にも角にも、良いアイデアを得るためには、考える時間を長くするしかない。

しかし、何をするにしても同じように時間は流れるし、人生はとても短い*2。時間は有限だ。

まずはパンツを脱いで*3SNSを止めよう*4

*1:Twitter等ですごい方々を見ていると、そんな気がしてくる

*2:こんな感じの表現何処かで聞いたような気がするけど、なんだったかな

*3:違う

*4:違う

ElixirでDesel-langのパーサを書いた

qiita.com これの11日目です。

はじめに

今回作ったのはDesel言語を扱うためのコマンドラインツールです。(以下desel-cli
github.com

動作例

$ echo "%A a b c\n" | desel - %A
a
b
c
$ echo "%A -a b c\n" | desel - %A
desel: failed to parse the input

0: %A -a b c
      ^ unexpected token
suggestion: element or expression

本記事では、その実装方法などを解説します。
Desel言語自体についてはこの記事では特に触れないので、そちらに興味のある方はryo33/desel-langを参照していただけると幸いです。

コマンドラインツール部分

Elixirでコマンドラインツールを作るのは結構簡単です。
mix.exs内に

  def project do
    [...
     escript: [main_module: YourCLIModule],
     ...]
  end

として、YourCLIModule内でmain/1を定義するだけです。
desel-cliYourCLIModuleに該当するのはlib/desel/cli.exです。
ファイルや標準入力から読み込んだあと、パーサなどの他モジュールにそれを渡してその結果を出力しています。
ここのコードは、重要な処理は特にしていないうえに、かなり雑に書かれてあるのであんまり見る価値はないです。

字句解析と構文解析

desel-cliではパーサコンビネータryo33/Parselix) を用いて字句解析と構文解析を同時に行っています。
コードはlib/desel/parser.exです。
ParselixParselix.Basicからimportしてきた関数、謎の命名規則などが大量に存在しているので何が何だかわからない事になっていますが、 BNFと対比させてみると何をやっているかはわかりやすくなると思います。
BNFgrammer.ebnfにありますが、 ここも雑なので合っているか保証できないです。
ほとんどがBNFから持ってきただけですが、パース結果を扱いやすい形に変換するmap/2や パースに失敗したときのメッセージを'unexpected token'みたいな感じに置き換えるexpect/2などが色々頑張ってます。

パース結果の処理

この処理がこのコマンドラインツールのメインの処理です。
コードはlib/desel/data.exにあります。
ここでやっている処理は主に2つで、パース結果の変換(from_ast/1)と式の評価(elements_by/2)です。
式の評価では、集合に限って(今のところ*1)キャッシュを保存する用にしたり、 いい感じの再帰にしたりして結構頑張っています。*2

Parselixについて

Parselixは僕が「Elixir面白そうだし入門がてら何か作るか」となったときに、 ちょうどパーサコンビネータに興味を持っていたため作ったものです。
今になって読んでみると結構ひどい見た目で、 Elixir初心者だったからと言い訳できないようなところもあったので、この機会に色々書き換えました。
parserマクロで定義できる関数のarityが任意になったのが一番でかくて、パイプで処理を書くときに

[left, parser, right]
|> sequence
|> (&(pick({&1, &2}))).(1)

と、非常に読みにくいコードになっていたところが*3

[left, parser, right]
|> sequence
|> pick(1)

とできるようになりました。

これから

CLIモジュールのテストは面倒くさくて書いていなかったのでそれと、近いうちにREADMEを書きます。
あと、適切でないパース時のエラー表示がまだ多いので、それを直していくつもりです。

最後に

Elixir v1.4はmix escript.installでhexやgithubからコマンドをインストールできるのが最高です。

*1:多分これ以外をやってもあんまり意味ない

*2:この再帰Enum.reduceなどで書いたらかなり読みづらいコードになるはず

*3:そもそも、これを無理やりパイプで書こうとするな