コミケ告知

サークル活動の詳細は circle タグの記事へ。
2016年3月5日土曜日

Cowboyの使い始めではまったところメモ (privとは何ぞや)

ElixirでWebサーバーといえばPhoenixですが、既存のElixirアプリケーションにちょっとWebサーバーの機能を足すだけの場合にはちょっと大規模すぎたので、PhoenixよりもコンパクトなCowboyを使うことにしました。Phoenixのサーバーコア部分はCowboyなので、比してコンパクトという表現は実はちょっとおかしいですね。

ソースコードのexamples以下には、いくつもサンプルプロジェクトがあり、それをそのまま動かすのも、参考にして動かすのも簡単です。ドキュメントもErlangなのに十分にあり、必要なハンドラを定義して差し込むだけで動きの把握もしやすく、素晴らしいライブラリですね。

ただしひとつだけ罠があり、それはサンプルで用いられるファイル読み出し指定が priv_dir や priv_file であることです。例えばstatic_worldのサンプルでは…

Dispatch = cowboy_router:compile([
 {'_', [
  {"/", cowboy_static, {priv_file, static_world, "index.html"}},
  {"/[...]", cowboy_static, {priv_dir, static_world, "", 以下略
 ]}
]),
Elixirで書くとこんな感じ:
dispatch = :cowboy_router.compile([
  {:_, [
      {"/", :cowboy_static, {:priv_file, :static_world, "index.html"}},
      {"/[...]", :cowboy_static, {:priv_dir, :static_world, ""}, 以下略}
  ]}
])

これをiexで走らせると動いて安心していたのですが、escriptにしたりアプリケーションとしてリリースしようとしたりすると全然動かない。index.htmlなどのファイルが見つからないときの挙動をする…。 どこに置いてもindex.htmlなどのファイルをアプリケーションが見つけてくれない。
同様にpriv方面ではまっている英語の記事があり、そちらではそのまま力技で解決していましたが、そんなに頑張らなくても、単にprivディレクトリを使う指定をやめればOKでした。

dispatch = :cowboy_router.compile([
  {:_, [
      {"/", :cowboy_static, {:file, "web/index.html"}},
      {"/static/[...]", :cowboy_static, {:dir, "web/static"}}
  ]}
])

実コードから引っ張ってきたので若干パス指定が違いますが、要点は:priv_fileではなく:file、:priv_dirではなく:dirを使うこと。アプリケーションからの相対位置で無事読めました。privディレクトリがErlang的に特別なものらしいのです。ぐぐるとほとんど情報がないなかに、ユーザーズガイド邦訳から以下の説明が見つかります。

アプリケーション固有のファイルの格納に使用されます。例えば、Cの実行ファイルがここに置かれます。code:priv_dir/1関数を使用すると、このディレクトリにアクセスすることができます。

単にprivという名前のディレクトリではないことはわかりました。Cの実行ファイルを格納するようなところに、Webのリソースを置くのはなんか違うんじゃないかなという感覚があるのですが…

# 2016/04/20追記
公式のMix.Tasks.Escript.Buildの項目に
Note: escripts do not support projects and dependencies that need to store or read artifacts from the priv directory.

とありました。
2016年1月22日金曜日

一時ファイルのサイズはどこまで許容されるのか

ここで一時ファイルとしているのは、linuxなら/tmp、Windowsなら%TMP%および%TEMP%のことです。特にWindowsだと、GetTempPathのように安直にパスを得られて、気軽に使えます。

遭遇した問題 

昔かなり遊んでいた音楽ゲームにbeatmania IIDXというものがありまして、そのクラウドバージョン、PC上でプレイできる beatmania IIDX Infinitasというサービスが始まりました。トライアル版が無事動いたので、さっそく製品版を遊んでみようと思ったんですが、インストール中にエラーが出るんです。
何度やっても同じところで停止。そんな重大不具合ならば騒ぎになるはずですが、Twitterで検索してもそんな話は出てこない。つまり、こちらの環境の問題である可能性が極めて高くなりました。インストーラーの動作中にディスクの書き込みを監視すると、どうやら一時ディレクトリに書き込んでいる…
そのサイズ、3GB弱。マジか。明らかに曲データが入っているでしょうから、今後サービスが継続していくにつれ、どんどん大きくなっていく可能性もあります。うちのメインPCでは、テンポラリはRAMディスクに割り当てているので、こんな大きなサイズは入りませんでした。

# 追記: 本件はset %TMP%して実行することで回避は可能っぽいけども、エラーが発生しました!としか出ないので、そこそこ詳しい人でないと解決できなそう。

一時ディレクトリは何GBあればよいのか

デフォルトではWindowsではシステムドライブ、Linuxも律儀に分けていなければ、/binや/usrなどと同じパーティションになるのではないかと思います。HDDにどかんと置いている場合には問題にならないでしょうが、うちのようにRAMディスクを割り当てている場合、システムSSD+HDD構成でシステムドライブの容量が限られている場合などは問題になる可能性がありそうです。
果たして一時ディレクトリにどのくらいまで置いていいのか、という議論は、あまり見たことがありません。適当にGoogleで検索すると、ユーザー側の対処がたくさん引っかかって難しい。はたして、どれくらいまで置いてよいものでしょうね?

さらに追記 

2015年12月31日木曜日

C89 お疲れ様でした

大晦日のコミックマーケット89、当サークルまで足を運んでくださった皆様、ありがとうございました!

新刊は12:50より前には完売してしまいました。コピー本としては過去最多の印刷数でしたが、それでも見込みが少なく、その後にいらっしゃった方にはご不便をおかけしました。次回があれば、またVol.5 は体裁を整えつつ、Vol.6のネタも収集しておきます。(申込みはする予定ですが、当選するかどうかは神のみぞ知る…)
2015年12月9日水曜日

Elixir ゆるデスクトップアプリ のすすめ

Elixirは、実績のあるErlangのVMやライブラリを使うための、人当たりのよいDSLのようなものです。そのため、得意な領域はErlangと変わらないはずで、Erlang系の本領は、高信頼・高い並行性という特性を生かしたバックエンドです。しかしそれに加えて、コードが書きやすくなったがゆえに射程範囲に入った分野もあります。流行りのPhoenixもそうだ、と言えなくもないでしょう。本記事では、そんな変わった使い方のひとつを紹介します。

この記事は、Elixir Advent Calendar 2015 9日目です。前日は Elixirでリバーシ でした。実は昨年のElixir Advent Calendar 2014にも参加させていただいておりまして、2014年のElixir1.0初心者 という題で、触り始めで良かった・困ったことなどを書くソフトな記事を書いておりました。

何を作っていたのか

  • ネットワークゲームの通信を入力
  • JSONをばらして内容を解析
  • 解析内容を組み合わせて現在の状態を計算
  • 適当に表示

ネトゲの(非友好的な)解析としては基本的なやり方のひとつです。

何が便利なのか

  • アプリケーションを動かしたままコードを入れ替えられる
  • アプリケーションは部分的に死ぬ (全滅しない)
この組み合わせが、趣味のツールであってもたいへん快適です。まずコード入れ替えについて実例をあげると…



真ん中の行、処理していないAPIは単にコンソールに名前を吐いています。この api_get_member/ship_deck が来たときにもコンソールに情報出しとくかー、と思いたったとしたら、ささっとコードを追加あるいは更新してリロード。メモリ上に保持したアレコレをそのままに、さらりと次回叩いたときから処理が動くように組み込めます。

(enemyとmap infoの間に追加情報が2行出るようになった!)

コード上では、 Kernel.ParallelCompiler のfiles/2に対象のファイル名を渡しています。保持しているデータ型を変えようとすると面倒になるはずですが、処理ロジックの入れ替えだけであれば、大したことはありません。

そして次の、アプリケーションが部分的に死んでくれるという特性について。ゲームしながら空き時間にいじるような注意力散漫なコーディングをしていると、よくエンバグしてこけます。言語に不慣れならなおさらのこと。あるいは、ある日突然JSONの構造が変わって、処理がうまくいかなくなることもあります。そういったときに、アクターモデル(Erlangプロセス)の仕組みは便利なものでして、JSONをパースしていくらかの計算をする部分、構成図でいうparseの部分が死んだところで、アプリケーション全体としてはなんともありません。その失敗した処理要求は、何も工夫しなければどこかに吹き飛んで消えてしまいますが、必要ならオペレーションを保持しておいて、処理関数を更新した後にリトライすれば復旧も可能です。Event sourcing的な。
Erlangを用いたシステムの高信頼性とは、「落ちてもいいように作る(そういう仕組みを作りやすい)」という面があって、趣味でだらだら遊ぶにも向いているのでは…!

Elixirによる自作ツールの可能性

以上、ちょっとしたツールにも便利ですよーという話でした。単純なスクリプトにお勧めはしませんが、処理を待ち受ける or 何かを監視する、生存期間がそれなりにあるツールには向いています。GUIが欲しければ、Phoenixを組み込んでしまえば良さそう。
Elixirという言語&Erlangの実行系に慣れるという副次効果まで考慮して、遊びのツールを作るという路線、布教あるいは学習方法のひとつとして、いかがでしょうか。
2015年12月2日水曜日

Advent Calendarは時期が悪い

日本のIT界隈ではAdvent Calendarという、どこぞの神様のお祝いカウントダウンに便乗して、テーマに沿った記事をアップしていく、謎の慣習があります。しかし12月というのは、年度末ほどではないけれども忙しい。年度末リリースの仕事が佳境であったり、忘年会が平日にまで入っていたり…。さらにオタク的には冬コミ入稿時期直撃、声優やアイドルのイベントが多い、昨今はソシャゲのイベントも多い、とあります。

もっと他の時期にやったらいいのになあ。他の人のお祝いしましょう。竹達彩奈(6/23)あたりどうですか。Java竹達Advent Calendarとか、Go言語竹達Advent Calendarとかね。
2015年11月3日火曜日

C89 配置されました

◎貴サークル「浜風もっこす」は、木曜日 東地区“ム”ブロック-36b に配置されました。

ありがたいことに今回もサークルとして参加させていただけるようになりました。
仕事が12月〆になったので、作業時間的に後ろにのばすほどやばい感じです。今月半ばには形が見えるくらいでないと死んでしまうでしょう。

現在のメインであるNetwork Maniacsは、原点回帰という感じで、プロトコル周りの話を書きたいです。ネタ精査中。なにか工作して現物展示も考えたのですが、前述のように仕事がヤバイのでつらそうです。工作方面ではESP8266が今年とてもホットだったのですが、みんなこれを使う(使っている)ので、生半可なことをしても面白くありませんよね。アイデアが浮かぶかどうか次第。
毎度中途半端に終わってしまっている艦これ攻略関係、もう艦これのブラウザゲームを続ける気力が尽きかかっているので、分量は少ないとしても、今回で形にして完結しないといけません…。夏まではモチベーション続かないな。
2015年9月13日日曜日

tryする末尾再帰関数が普通には書けないScala

TL;DR

try~catch内で値を返す再帰関数を書くには、scala.util.Tryを使う。

説明


Scalaでは末尾再帰関数は最適化してくれるし、それをtailrecアノテーションでチェックすることができます。(最適化が効かない場合にコンパイルエラーが出るようになる)
@scala.annotation.tailrec
def f(n: Int): Int = {
  ...  // (略)
  f(n - 1)
}
ただこれ、try~catchを付けて、そこで値を返そうとするとうまくいかないんですよね~
@scala.annotation.tailrec
def f(n: Int): Int = {
  try {
    ...  // (略)
    f(n - 1)
  } catch {
    // どの場合も返す型は正しいものとする
  }
}
scalac Main.scala
Main.scala:3: error: could not optimize @tailrec annotated method f: it contains a recursive call not in tail position
  def f(n: Int): Int = {
エラーメッセージも正しくなくてなんだこりゃって感じですが、stackoverflowに回答がありました。

何度も回答がEditされていますが、その一番下。try~catch節ではなく、scala.util.tryを用いることで回避できるようです。
@scala.annotation.tailrec
def f(n: Int): Int = {
  scala.util.Try(例外の出る処理内容) {
    case Success(処理の返り値) => ...
    case Failure(e: 例外の型) =>
    case Failure(e: 例外の型) =>
  }
}

知っていて最初からこれで書けば面倒くさいことはないんだけど…Scala、あちこち珍妙なとこあるよな。