今更PHPでモナド的なものを書く
http://blog.dakatsuka.jp/2013/09/03/php-maybe-monad.html
この辺とか
http://d.hatena.ne.jp/kazu-yamamoto/20110413/1302683869
この辺とか見てたらちょっとやってみたくなったので。
○○言語でMonad実装って結構あるけど、MonadPlus使えないとあまり意味ないよね?的なところもある。
上記URLのTree探索するやつの
msearch :: MonadPlus m => (Int -> Bool) -> Tree -> m Int
これ、型推論、テンプレート、ジェネリクス、戻り値の型がないPHPでは無理目なので、コンテキスト(MonadPlus m)を引数として渡すことにする。
traitがあればreturnやmzeroを共通化できるので多少マシじゃないかな?と思いつつ。
<?php private function searchTree(Context $m, callable $f, Tree $tree) { if ($tree instanceof EmptyTree) return $m->mzero(); if ($f($tree->value)) return $this->searchTree($m, $f, $tree->left) ->mplus($m->ret($tree->value)) ->mplus($this->searchTree($m, $f, $tree->right)); else return $this->searchTree($m, $f, $tree->left) ->mplus($this->searchTree($m, $f, $tree->right)); } private function getTree() { $empty = new EmptyTree(); return new Tree(3, new Tree(2, $empty, $empty), new Tree(1, $empty, $empty)); } public function testTreeDataList() { $c = new DataListContext(); $f = function($a){ return $a % 2 == 1; }; $t = $this->getTree(); $ret = $this->searchTree($c, $f, $t); $this->assertEquals(Cons::fromArray([3,1]), $ret); } public function testTreeMaybe() { $c = new MaybeContext(); $f = function($a){ return $a % 2 == 1; }; $t = $this->getTree(); $ret = $this->searchTree($c, $f, $t); $this->assertEquals(new Just(3), $ret); }https://github.com/nishimura/laiz-monad/blob/master/test/src/Laiz/Test/Monad/ContextTest.php
タイプ量が多い…。
traitってこういう使い方で良かったんだっけ?ファイルの置き場所とかセオリーが分からん。
composer.json置いてるけど未登録。
ぼんやり振り返り
顧客は本当に欲しいものは知らない、と言うけれど同じような症状だ。
かゆいところに手が届かない。
自作フレームワークがつぎはぎになってきたのでカッとなって作ったやつ。
PHPの小規模用テンプレートエンジンを作った - より良い環境を求めて
なんかアノテーションがマイブームだったっぽい。
PHPのフレームワークを作った - より良い環境を求めて
ただ利用しているZendのアノテーション解析が、あるバージョンでとても遅くなったので、アノテーションを使うことに不安を覚える。
言語レベルでやることをPHPで頑張ってやるべきじゃないな、などと思い直す。
@varアノテーションによるリクエスト変数のcastは結構便利だったんだけど。
ふと、Haskellっぽく書きたいよなー、前にそういうこと書いたよなーと思い出す。
バリデーターあれこれ - より良い環境を求めて
やっぱり書いてた。
ロジックの中の方でDBを呼び出すのは良くないよね、という話。
バリデーター繋がりで、Scalaでも何か書いてたなーと思い出す。
LiftでWebアプリ(4): フォーム用共通処理を作る 1 - より良い環境を求めて
この投稿の最後の方のコード、今見てもこんな風に書きたいなーと思える。
演算子が定義できないとどうしようもない…かな?
例えば、こんなの。
<?php class SomePage{ public function somePost(Request $req, Db $db){ $form = $req->fragment('form1'); $form->key('mail')->name('メールアドレス') ->trim()->required('{name}を入力してください') ->email('{name}の形式{input}が正しくありません'); $form->key('body')->required('本文を入力してください') ->maxLength(1000, '{param0}文字以上は送信できません'); $form->key('zip1')->key('zip2')->name('郵便番号') ->toHalf()->required('{name}を入力してください') ->length([3, 4])->numeric('{name}の形式が正しくありません'); try { $values = $form->get(); $account = $db->from('Account')->eq(['mail' => $values->mail])->getOne(); .... }catch (IoException $e){ $errors = $e->getErrors(); .... } } }
万能RequestクラスをTraitの寄せ集めで作って、Eitherの変わりに例外にして…。うーん…。
やっぱり演算子や結合がないとタイプ量が多い。
で、PSR-7もあるのでどうしようとか。
アプリは、Input => App => Output っていうのは確定で、これは
Request => App => Response に置き換えるとちょっとまずい。Outputがそのまま次のInputになった方がシンプルになりそう。
正確には Context & Request => App => Request | Response かなあ。
アプリがRequestを返したらリダイレクトなどをして、Responseを返したら終了。
いや、でもテンプレートエンジンを使うだろうしやっぱりResponseを返すのは特殊な場合だけになるのか。
まとまらないけど眠いので一旦投稿。
頭の体操
CodeIQ ホリエモンからの挑戦状
https://codeiq.jp/magazine/2015/07/26213/
何故か体調を崩して頭ふらふらのときに解説編の記事を見てHaskellでコードを書いていたので記念メモ。
module Main where import Control.Applicative import Data.Set (fromList, member) main::IO () main = prog <$> getInputs >>= print -- main = print $ prog (10000, [1..5000]) prog :: (Int, [Int]) -> Int prog (ln, xs) = length $ [(a, b, ln - a - b) | a <- [x | x <- xs], b <- [x | x <- xs, a < x, x < ln - a - x, member (ln - a - x) $ fromList xs] ] getInputs :: IO (Int, [Int]) getInputs = (,) <$> readLn <*> (readLn >>= readContents) readContents :: Int -> IO [Int] readContents n = take n <$> map read <$> lines <$> getContents
ghcの環境が壊れててそれを戻すだけで1時間以上かかった記憶がある。あとリスト操作部分よりもIO操作とApplicativeの使い方調べるのに時間がかかって、問題にある「想定時間10分」は余裕で超えた。1時間以上はかかった気がする。
そして https://gist.github.com/yancya/3c5fa7c0f09ad85f5230 こちらのRubyより遅い。リストのindexを使ったりListじゃないものを使えば速くなりそうだけども放置で。
http://www.sampou.org/cgi-bin/haskell.cgi?%A5%B0%A5%ED%A1%BC%A5%D0%A5%EB%CA%D1%BF%F4%A4%AC%CD%DF%A4%B7%A4%A4%CD%FD%CD%B3%A1%A9
こちらを読み返しつつの練習。
Web開発時のGitでのゆるいワークフロー
Gitで何か低レベルの高度な操作が必要だっていうときは、大抵の場合は作業は一つのブランチでしかしない症候群にかかっている。
Gitはブランチやマージが軽いので、作業中のブランチを次々作っても上手い具合にマージしてくれる。
masterが本番、developがテストサーバー。
この辺はreleaseでもstagingでも何でも良い。
一人開発でも効果ある。
masterからissue-1ブランチを切る
issue-1でcommit => developにマージ => テストサーバーで確認 => ちょっとここ直して〜
issue-1でcommit => developにマージ => テストサーバーで確認 => ちょっとここ直して〜
issue-1でcommit => developにマージ => テストサーバーで確認 => 確認中...時間がかかりそう
暇なのでmasterからissue-2ブランチを切る
(※ issue-1 を残したまま masterからissue-2 を作る。issue-1 の中で、あるいはdevelopから派生して別の作業をしてしまうと後で面倒なことになる。)
issue-2でcommit => developにマージ => テストサーバーで確認 => ちょっとここ直して〜
issue-2でcommit => developにマージ => テストサーバーで確認 => OK、本番に反映して〜
masterにissue-2をマージ => 本番反映
issue-1 はあとここだけ直して〜
issue-1でcommit => developにマージ => テストサーバーで確認 => OK、本番に反映して〜
masterにissue-1をマージ => 本番反映
masterとdevelopの差異が無い場合は適度にmasterにdevelopをマージ、developにmasterをマージ(単に履歴のグラフ表示を見やすくするため)
ちなみにPHPなので、デザイナーがサーバーに直接画像をアップしたりHTMLを書き換えたりすることもある。
その場合は適当なタイミングでテストサーバーあるいは本番サーバー上で直接commit => push する。
次回pullせずに気付かずにpushしても弾かれるので特に問題は起きない。
Redmineの少機能設定
以前はあれもこれもプラグインでできるなどと色々入れた結果、やることがぼやけたりRedmineのバージョンを上げづらくなったりということになっていた。
今回はとにかくメニューやリンクを減らす方向で設定。
- admin以外はシステム管理者にしない
- ロールと権限で開発者・報告者にメンバー管理を追加して、普段は開発者で使って他のユーザーの確認に報告者になったりチケット整理時だけ管理者になったりする
- トラッカーで使うのは「担当者」「対象バージョン」「進捗率」とカスタムフィールドのみ
- 進捗率はステータスから自動計算(設定のチケットトラッキング)
- ワークフローは初期設定のものから更に削る
- チケットのステータスは「要求待ち」「要件確定」「テスト待ち」「リリース待ち」など表記を変える
- フィールドに対する権限で、一定のステータス以降は「担当者」「対象バージョン」を必須
- バージョンは「6月リリース分」「7月リリース分」「未定」「随時」など
- ここで期限を区切るのでチケットでは日付や工数を管理しない
- プロジェクトのモジュールで使うのは「チケットトラッキング」「リポジトリ」「デフォルトカスタムクエリ」
カレンダーやガントチャートは、必要なときほど情報が入力されていないことが多いので保留。
- 「Redmine Default Custom Query」プラグインで、チケット一覧の初期表示を変える
- 「Redmine Startpage plugin」でトップをマイページまたはチケット一覧に変える
- 「View Customize plugin」で
- サイドメニューを隠す ( http://blog.enjoyxstudy.com/entry/2015/03/23/000000 )
- ヘッダーのhomeやhelpを隠す
- プロジェクトへのリンクに /issues を付け足して、初期画面はチケット一覧にする
- issues/new$ の条件で、新規チケットの初期値を設定する
他にもマイページに固定リンクやお知らせを入れたりメニューの順番を入れ替えたりできるView Customize pluginは思った以上に何でもできる。すごい。
担当者のデフォルト設定が〜〜とか色々記事があるけど、issues/new$ に対する設定で全部jQueryでやってしまえ的な。
$(function(){ var val = 0; // 担当者初期値(自分) $('#issue_assigned_to_id option').each(function(){ var v = $(this); if (v.text() == '<< 自分 >>'){ val = v.attr('value'); return false; } }); $('#issue_assigned_to_id').val(val); // バージョン初期値(一番上) val = 0; $('#issue_fixed_version_id option').each(function(){ var v = $(this); if (v.attr('value')){ val = v.attr('value'); return false; } }); $('#issue_fixed_version_id').val(val); });
グローバルな設定にロジックを埋め込んでも気にならないなら、かなり入力の手間を省けそう。
リポジトリへのリンクも、初期画面をmasterではなくdevelopに変更するとか。
サイドメニューを隠すJavaScriptの前にこれを入れて
// プロジェクトタブの移動 if ($('#main-menu a.overview')[0]){ $('#sidebar').append('<h3>プロジェクト</h3>'); var ul = $('<ul>'); $('#sidebar').append(ul); $.each(['overview', 'activity', 'repository', 'settings'], function(k, v){ ul.append($('#main-menu a.' + v).parent()); }); }
たまにしか見ないプロジェクトの概要、活動、リポジトリ、設定タブをサイドメニューに移すとか。
Redmine3.0を入れた
プラグインや共通設定リセットなどの関係でRedmine3.0を入れた。
既にRuby1.9.3でRedmine2.4.3が動いていたのだが、それは残したままrvmで。
念のため別のLinuxユーザーを作ってrvmも新規で入れる。
Rubyのバージョンはデフォルトで2.2.1だった。
Passengerは5.0がインストールされた。
古いPassenger4.0のapacheモジュールではうまく動かなかったので、今回インストールしたPassenger5.0をApacheで読み込んだ。
古い方はPassengerRootなどを設定。
Ruby2.2.1で作ったPassenger5.0のApacheモジュールでもRuby1.9.3のRedmine2.4.3は動くっぽい。
というメモ。
あとついでに今更だけども、DebianのExim4でのメール設定
production: email_delivery: delivery_method: :sendmail sendmail_settings: arguments: "-i"
http://www.redmine.org/projects/redmine/wiki/EmailConfiguration
ちゃんとDebianの例が書いてあるのでマニュアルを読みましょう。
流れるようなインターフェースをtraitで
ぼんやり思ったことのメモ。
たとえばDB接続なら
<?php $item = Item::select() ->from('item') ->join('category') ->on()->over(['category.status' => 'item.status']) ->like(['name' => 'my item']) ->order('code') ->offset(100) ->limit(10) ->get();
とかやりたい。
今までさくっと実装しようとすると、メソッド全部入りのDBクラスを作れば良かったんだけども、traitを使うと各メソッドを細切れで作っておいてtraitで
<?php class CalledSelect { use Join; use Where; use Order; use Get; use Offset; } class CalledFrom { use Join; use On; use Where; use Order; use Get; use Offset; } class CalledWhere { use Order; use Get; use Offset; }
のようにすると、選べるメソッドが制限されて書きやすいんじゃないかと。PHP7では戻り値も書けるようだし。( https://wiki.php.net/rfc/return_types )
まあ今はIDE使ってないし恩恵ないんだけども。