おっさんくさいこと言ってたのかもしれないと思って勝手に反省していた

はじめに

特にきっかけがあるわけでもないが、自分が技術的選択においておじさんくさいことを言っていたのではないか? ということをつらつらと書くという内容です。 具体的には、「実質的にリターンがあることに対してコストがある事柄について、これは多くの人間にとってリターンにコストが見合わないんじゃないか」とか言っちゃうことをおっさんくさかったのでは、と反省する記事です。

この話の前提となる私の思考癖

技術選定や設計など、技術的選択を行う時に、以下のことを私は考えています。

  1. 現在の要求される仕様に技術・設計がマッチしているか、オーバーではないか、貧弱ではないか?
  2. この先、どのような機能の改修が入るか、どういう進化の道を辿るか?(そんなに遠くを考え過ぎないようにはするが)
  3. それは今考えるべきことか?あとから問題起こった時に考えるということでもいいのではないか?

1について

自明な話なので、説明は割愛。

2について

「機能の改修や進化」は、サービスの性質あるいは人間から生まれるものですよね。 これらは、サービスの性質や、組織や発言権の強い人間の思考癖、あるいは日常の中に出る発言によって、 ある程度予測可能な部分があると思っています。 そして、これらの予測範囲はサーバーからGUIに及ぶが、今後予測される複雑性に対する拡張性や技術的難易度の上がり方などに影響すると考えています。 また、組織や人間の癖がより色濃く出やすいのがGUIであると考えていて、みんなやりたいことがあって、だいたい頻繁に手が入るので、 なんも考えてないとGUIは比較的困ったことになりやすいと考えています。 よって、この項目についても考えることにしています。

3について

あまり早い段階で間違った最適化をしないように気をつけているというだけです。 1しか考えてなくても間違った最適化をするかもしれないし、2の考えに寄りすぎると、間違った最適化をすると考えているためです。

「コストにリターンが見合わない」のコストとリターンとは?

上記の前提において、コストとはなんなのか、と考えると、 例えば、学習コスト(技術選定の場合でも、アプリケーションコードの実装についても)、 あるいは、単純に実装コスト(これは単純に実装にかかる工数とする)、ということになると思う。

リターンは、例えば、より複雑な要求に耐えうる、とか、はやくなるとか、 まぁなんでもありそうですね。

私の思考癖で考えることは、選択の裏付けになっていない

上述した思考癖のうち、1は「現在」という確度の高い情報に基づいて考えることになるので、1で考えたことは裏付けとして使ってもいいと考えている。

しかし、2についてはどうか。 まず、そもそも、入社したてで機能改修の傾向がつかめていなかったり、極めて理屈のない思いつきでものを言う強権者もいたりするし、 あるいは、あらたな強権者がチームに入ってくるなどもあり、 私の考える「システムの進化予測」はかなり不確実性が高いということになる。 そして、2の項目における技術的判断のリターンは、この不確実な「システムの進化予測」から影響を受ける。 つまり、この、リターン予測も不確実ということになる。

また、コストについてだが、学習コストや実装コストは、人によって大きく変わることがある。 これは、どういう経験をしてきたかにもよるし、メンタルな部分もあると思っている。 これらについて、私が他人のことを考えて「きっと大変だろう」とか思っても、 結局わからないことを勝手に決めつけて考えているに過ぎない。

つまり、「コストに見合うリターン」というのを、コスト面についてもリターン面についても、私は雰囲気で考えているということになる。 また、結果的に思考癖の3も雰囲気とか感覚で考えていることになる。

(とはいえ、これらの事柄について、全く考えないべきだというのもまずい気がするので、一応考えることには変わりない)

雰囲気で考えているのにもかかわらず

あまり理のないことを言っているのにもかかわらず、なんでこんなことを言っちゃうのか?

これは、私が「コストにリターンが見合わないんじゃないか」と言う時に「他人のことを考えて大人っぽいことを言っていてえらい」という感覚がどこかあって悪いことをしているという感覚がなかったからなのではないかと思っている。

そもそも、理があろうがなかろうが、他人のことを考えたからといって、それがいいことかどうかは他人が決めることなのでわからない。 他人のことを考えたから良いことをしている、というのはかなり傲慢な心理に思える。

私はこれを「おっさんくさかったのではないか?」と思ってこんなポエムを書くに至った。

結局どうすればいいのか

このことについて、今後、私はどういう態度をとるべきかというと、「だれでも気軽に脊髄反射で反論しやすい空気感の言葉を使い、私の考えてることに対して、意見をもらうようにする」ということくらいしか思いつかない。

結局これで誰からも意見がもらえないということもあるだろうし、意見をもらったからと言って、最高の選択ができるかというとそれは最終的には未来にしかわからない。そのまた未来では、クソだったということもあるだろう。これは時間経過で評価が変わっていく。

だが、「コストにリターンが見合わないんじゃないか」と言いたくなった時に、「俺はおっさん臭くなってるかもしれないから、ちょっと色々聞いてみるか」という気持ちとか表情を出力したほうが誠実な振る舞いだろう。

「あいつめっちゃ加齢臭するよな!!」とか言われるのも悲しいですし...

purescript-conveyorにLogger実装足しといた

追記

本記事のパッケージは現在メンテされておりません。

サーバー向けパッケージは再実装されたものがこちらにあります。

Bucketchain


足した

あくまでデフォルト実装としての簡易的なLoggerです。

基本的にはこの実装を参考にして、みなさんのお好きなLoggerをつくってServableにしてくださいということでひとつ。

椅子を高級品にした

いきさつ

ある日、自宅作業中に背もたれによっかかったら、小さく「ミシッミシミシミシ」と音を立てていたのだが、その瞬間はあまり気にしていなかった。 そのまましばらくよっかかっていると、どうも少しずつ背もたれが倒れていっているようなので、これはおかしいと思い、椅子をよく見てみると、 背もたれの留め具の部分が完全に破損して飛び出ていた。床をみると破片がめっちゃ落ちていた。 サンワサプライのやっすい椅子を使っていたのだが、私は体格がいいほう(178cm 90kg弱)なので私の体重に耐えきることができなかったっぽい。 たぶん2年くらいしかつかっていない。

そういうわけで、私もフリーランスになったため、そろそろ良い椅子を買おう、ということにしました。

何買うか

実は以前の職場でコンテッサを使っていて結構よかった記憶があるのでコンテッサが第一候補ではあったが、せっかくなので調査と視座をすることにした。

事前調査としては、椅子の基本方針として、「前傾」をサポートするか「後傾」をサポートするかというのがあり、そこがわりと大きな違いにはなりそうな感じであった。 また、ハーマンミラー製品は他のメーカーと比べて大きな違いがあり、他のメーカーよりも保証の条件が厳しい代わりに、保証期間が12年でとても長いということもこのタイミングで知った。

試座

試座は自分の普段のデスクワーク時の姿勢の変化やスタイルを意識してやると良いという事でそういう感じでやった。

近場を何店舗か回りながら視座してみたが、私はちょっと困っていた。 というのも、ぶっちゃけ数分程度の試座だと、高級なオフィスチェアは、どれもとても座り心地がよく、どれがいいという感じにならなかったのである。これは以前の職場で椅子を選ぶときに全く同じ感想があったのを思い出した。

私は作業中かなり前傾姿勢になるので前傾チルトは絶対にほしいのでそれがついていることと、保証が12年ということでハーマンミラーの製品にするかーという感じで決めた。

結局何買ったのか

相当悩んだが、結局セラチェアを買った。 セラチェア - オフィスチェア - ハーマンミラー

f:id:oreshinya:20180204162912j:plain

実は、今回椅子を買うときにひどいギャンブルをしてしまっていて、ハーマンミラー製品のうち、このセラチェアだけは全く座ったことがない。 この椅子は置いてあるところが少ないのだ。

なぜ、そんな頭の悪いことをしてしまったかというと、高けりゃだいたいある程度満足感があることは試座でわかっていたのもあるが、 とてもほしくなってしまったからなのだ...。

買う前に何がそんなに気に入ったかというと、

  • 布を使っていないので汚れにつよく、ヘタリに対しても特にかなり強そうだったこと
    • できることならノーメンテで長く使いたいから
  • メッシュすらないとか、唯一無二感あって、めっちゃかっこいいなこれって感じだったこと
    • 私はこういう感じの製品をめっちゃかっこいいと思ってしまうタイプ

こんな感じでビビッときてしまった。

ほんで、地味に調整機能は高機能で必要な調整機能は全て揃っていたこと、体格のいい人間にはかなりマッチする(らしい)というインターネットの情報から、「まぁ座ってないけど買っちゃお!えい!!!!!!!」って感じで買っちゃった感じです。

ただし、調査では、「座面が硬く感じる、冷たく感じる」という意見もあり、正直到着するまでビクビクしていた。

届いてから

到着時はとてもでかいダンボールに入って届けられるので、家に入らず、廊下で開封作業を行った。

んで、実際使ってみての感想だが、丸1日座っているがかなり最高で、座面の件も硬くないし、(たぶん暖房つけてるからだけど)冷たくもない。 クッションもないのに、不思議とケツも痛くならない。私はこの椅子にマッチするタイプだったらしい。これはセラチェア独自のセルラーサスペンションとかいう仕組みによるものっぽい。

ギャンブルに勝った。買ってよかった。

PureScriptで静的ファイル配信できるパッケージ書いた

追記

本記事のパッケージは現在メンテされておりません。

サーバー向けパッケージは再実装されたものがこちらにあります。

Bucketchain

はじめに

静的ファイルを配信するときはとりあえずnginxなどにお任せすればいいかと思いつつも、 昨今ではTwelve-Factor Appの考え方もあり、静的ファイルの配信もアプリケーションサーバーでしようということで、 PureScriptで静的ファイル配信するパッケージを書きました。

本番ではCDNのオリジンとして動いてもらうという感じにはなると思います。

どんなやつ?

リポジトリGitHub - oreshinya/purescript-static-serve: Serve static filesです。

ランタイムはnodejs想定です。

とりあえず最低限ということで、雑に言うと以下の3つのみ対応しています。

  • 静的ファイル配信
  • maxageの設定
  • 条件付きGETの対象については、Last-Modifiedのみ対応しておりETagは対応していません。

使い方

module Main where

import Prelude

import Control.Monad.Eff (Eff)
import Data.JSDate (LOCALE)
import Data.Maybe (Maybe(..))
import Node.FS (FS)
import Node.HTTP (HTTP, ListenOptions, createServer, listen)
import StaticServe (staticHandler)



config :: ListenOptions
config =
  { hostname: "0.0.0.0"
  , port: 3000
  , backlog: Nothing
  }



main :: forall e. Eff (fs :: FS, http :: HTTP, locale :: LOCALE | e) Unit
main = do
  server <- createServer $ staticHandler { root: "./public", maxAge: 60 }
  listen server config $ pure unit

現段階では、設定としては、rootディレクトリの設定とmaxageの設定のみです。

この例だとpublic以下に置いたファイルが配信されるようになります。

さいごに

やったぁPureScriptで静的ファイル配信できるようになったぁ(小並感)

(このパッケージがエッジケースに対応できているかは微妙な感じです。)

追記

昨日作ったこのパッケージにHistory API Fallback機能つけました。これでSPAもオッケーです。

最近仮想通貨買いはじめた

年末に初めて仮想通貨を購入した。 アーリーアダプターではないが、夢のある感じで楽しい。

チャート漁り、情報漁り、草コイン漁りがめっちゃ楽しい。

明らかにバブルという感じで、我々の上の世代はこんな楽しいことを経験していたのかという感覚がある。

通貨を購入した瞬間に金を捨てたと自己暗示しまくって買っていると、短期的な値下がりが気にならないタイプなので楽しめているのかもしれない。

今年は余剰資金が増える予定なので、いろいろ買っていきたい。

PureScriptの末尾再帰最適化

はじめに

年が明けてますが、PureScript Advent Calendar 2017 - Qiitaの19日目の記事にしてしまいます。

PureScriptは末尾再帰最適化ありますが、モナディックな再帰は最適化されません。 よって、何も考えないと稀に、コールスタックに積み上がりすぎてエラーが起こるという状況に遭遇するかもしれません。 というか私は遭遇しました。 そんなときのためのパッケージを紹介します。

紹介

後者はtailrecをラップしたパッケージです。

これらを使って書き直すと、きっとエラーが起こらなくなるはずです。

さいごに

サンプルコード書こうかと思ったけど、まぁREADME読めばわかるしええやろ、という気持ちで手抜きの記事となりました。

purescript-conveyorにBatch operationの機能を足した

追記

本記事のパッケージは現在メンテされておりません。

サーバー向けパッケージは再実装されたものがこちらにあります。

Bucketchain

はじめに

年が明けてますが、PureScript Advent Calendar 2017 - Qiitaの21日目の記事にしてしまいます。

前回、PureScriptでAPIサーバー用のパッケージつくってみた - oreshinyaのブログというようなAPIフレームワークをつくったことを記事にしましたが、 このフレームワークにBatch opearationの機能を足しました。 また、その際に全面的にソースコードを書き直したため、変更点を書きます。

Batch operationってなに

複数のエンドポイントを1回のリクエストで実行する機能です。

複雑なアプリケーションになると、ひとつの画面に必要なデータが非常に多岐に渡ります。 そのような状況下の中、ひとつのAPIで色々な多くのデータを返すか、単機能なAPIを複数回叩くかという選択をすることを最初に考えるかと思います。 このことに対して、何も対策的な仕組みを入れない場合、前者の場合はUIによりすぎたメンテのしにくいAPIによっていくし、後者は何度もリクエスト往復するのでとても時間がかかります。

こういったことに対して、数年前からBFFという概念が公に出るようになりました。 ただ、気合の入ったBFFをしなくても、単純なbatch operationだけでもあると、経験上、わりと快適になるため、自分で使うためのライトユースとして、この機能を足しました。

Servableのメンバの型が変わった

変更コミットたちです。

Batch operationを入れるために、Servableのメンバの型を変えました。 以前の型はserve :: c -> s -> Request -> Response -> String -> Maybe (Eff (http :: HTTP | e) Unit)で、新しい型はserve :: s -> c -> RawData -> Aff e Responderになります。

大きな変更点は、返り値の型となります。

以前の型では、serveの実行文脈の中でレスポンスをクライアントに返すことを期待した型付けでしたが、新しい型では、serveの文脈でレスポンスとなるデータをつくって返すだけになっています。

新しい型にしたことによって、batch operationの実装は、serveを指定された各エンドポイントのパラメータの配列を用いて、traverseして複数のレスポンスをまとめればいいだけになりました。

その他の変更

  • Request bodyのdecodeに関しては、purescript-simple-jsonに依存するように変更して、Readable型クラスを削除した
  • Respondable型クラスの全面的な書き直し
  • Handlerを削除して、普通にAffで動くように変更した
  • 関数の引数の順番とか数とか引数そのものを地味に変えた
  • まぁ、なんか色々書き直した

まだ改善できるところはある

現状は、各エンドポイントを順番に処理してるだけなので、concurrentに実行できるようにするとよりよさそうです。

使い方

ServableインスタンスBatch型で包んで初期化すれば、OKです。 リポジトリのサンプルコードとほぼコピペですが、以下のような感じで初期化します。

runWithContext config 777 $ Batch { contextTest, errorTest, rawDataTest, createBlog }

以下のようなデータをrequestのbodyとして、POST /batchにおくりつけると、

[
  {"path": "errorTest"},
  {"path": "contextTest"},
  {"path": "createBlog", "body": { "title": "hoge", "content": "うんち in batch" }},
  {"path": "rawDataTest", "body": "ろーでーた"}
]

以下のようにかえってきます。

[
    {
        "contentType": "application/json",
        "code": 500,
        "body": {
            "messages": [
                "Internal server error ;)"
            ]
        }
    },
    {
        "contentType": "application/json",
        "code": 200,
        "body": {
            "yours": "777"
        }
    },
    {
        "contentType": "application/json",
        "code": 200,
        "body": {
            "fuck": "title: hoge, content: うんち in batch requested."
        }
    },
    {
        "contentType": "application/json",
        "code": 200,
        "body": {
            "yours": "\"ろーでーた\""
        }
    }
]