タオルケット体操

サツバツいんたーねっとでゲームとかガジェットのレビューとかをします

Flutter所感

諸事情によりしばらくFlutterでアプリ作って感じたことをいくつか。

良いところ

1. ちゃんと動く

みなさんも今までに出ては消えていくiOS, Android両方で動くアプリ作れるよ系ソリューションで色々なお気持ちを発生させてきたかとおもいますが、Flutterの出来の良さはピカイチ感があります。Flutter Engineすごーい!

大抵のアプリが必要とするような機能(当然全てではない。例えばパスワード管理との連携とかは存在しない)であれば、各プラットフォームネイティブに手を入れることなくちゃんと動く。自前レンダリングと聞いて心配していたパフォーマンスも普通に悪くない。なんて素晴らしいんでしょう。
Flutterの良さはそこに尽きるとおもいます。

続きを読む

TypeScriptで関数型プログラミングしたい勢に推したいPipeline operatorの良さ

前置き

Rubyで一瞬だけ湧いて出てきて消えたPipeline風演算子については忘れてください。あれはメソッド呼び出しの演算子であって今回取り上げるPipeline operatorとは似て非なるものです。

JavaScripterでも、Ramda.jsやRxJSを普段から使っている人には既知の内容だと思うのでこの記事は読まなくて大丈夫です。

ちなみに僕のステータスは関数型にわか勢なので認識に間違いがあったらなんらかの訂正もらえるとうれしいです。

Pipeline operatorとは

古くはML系の言語で定義されてF#やElixirの流行で一般に有名になった演算子こと |> です。

f aa |> f と書けるようになります。
詳しくはこちらで: https://mametter.hatenablog.com/entry/2019/06/15/192311

覚えた方がいい理由

TypeScriptとの相性が良いからです。

なぜTypeScriptとの相性が良いのか

マルチパラダイムな言語であるJavaScriptに柔軟で高機能な型を提供するTypeScriptは様々な書き方が可能となっています。
それによってコミュニティの流派もクラスベースOOPから関数型まで存在するのですが、このPipeline operatorは関数型よりのスタイルを好む人によりフィットする機能になります。

逆にクラスベース好き一派には刺さらないんじゃあないでしょうか。

高階関数の型推論をさせやすくなる

様々な言語で実装されがちな、Result型を操作する関数を例にあげます。

詳細は述べませんが、TypeScriptにはTagged Unionと呼ばれる便利なテクニックがあります。このテクニックによって代数的データ型を模倣できるのですが、Extension Methodsという機能を持つC#と違ってTypeScriptは例えシンタックス上であっても型に実装を追加することができません。
なのでTagged Unionに振る舞いを定義するには別途関数として宣言する必要があります。

export enum ResultStatus {
  Success = 'success',
  Failed = 'failed',
}

type Success<V> = {type: ResultStatus.Success; value: V};
type Failed<E extends Error> = {type: ResultStatus.Failed; value: E};
export type Result<V, E extends Error = Error> = Success<V> | Failed<E>;

ML系でいうFunctorを実装したい場合は以下のような関数が必要になります。
namespaceを使っているのはResultの定義を拡張して Result.map と呼び出せるようにするためです。babelでTSをコンパイルしている人は設定が必要になるので注意。

export namespace Result {
  // ...

  /**
   * Haskell風に書くなら
   *   instance Functor Result where
   *     fmap func (Success a) = Success func a
   *     fmap func (Failed a) = Failed a
   * 
   * ほら、大体同じでしょ?
   */
  export function map<X, Y>(op: (x: X) => Y) {
    return (arg: Result<X>): Result<Y> => {
      switch (arg.type) {
        case ResultStatus.Failed:
          return arg;
        case ResultStatus.Success:
          return {
            type: ResultStatus.Success,
            value: op(arg.value),
          };
      }
    };
  }
}

mapの定義がカリー化されているのは、メソッドチェインの変わりに関数合成できるようにするため(Pipelineの話につなげるための伏線)です。

これで、Resultの結果を操作したいときは

const r: Result<X> = readFileContent('~/Downloads/dog.txt');
const transformer = Result.map(t => 'content: ' + t);
const transformed = transformer(r);

のように書ける……わけではありません。
TypeScriptの型システムではどう書いても const transformer = Result.map(t => 'content: ' + t); の操作対象が Result<string> であることを認識できず、anyに落ちてしまいます。
変数の使われ方から逆算したt推論ができる言語であれば、 content: ' + t の部分でtがstringだと推論できます(ML系とか?)が、TypeScriptの場合は Result.map((t: string) => ...) のように明示しなくてはなりません。

でもいちいち型を書くとかそんなダルいことしたくない……全部推論してほしい!
そこでPipelineです。

しかしPipelineはいまだstage 1で仕様策定に揉めている機能なのでTypeScriptで使うことはできません。なのでminimalな仕様をエミュレートする物を入れ(作り)ます。

class Pipeable<Arg> {
  constructor(private readonly x: Arg) {}

  public pipe<R>(op: Operator<Arg, R>): R;
  public pipe<X, R>(op: Operator<Arg, X>, op2: Operator<X, R>): R;
  public pipe<X, X2, R>(op: Operator<Arg, X>, op2: Operator<X, X2>, op3: Operator<X2, R>): R;
  public pipe<X, X2, X3, R>(op: Operator<Arg, X>, op2: Operator<X, X2>, op3: Operator<X2, X3>, op4: Operator<X3, R>): R;
  public pipe<X, X2, X3, X4, R>(
    op: Operator<Arg, R>,
    op2: Operator<X, R>,
    op3: Operator<X2, R>,
    op4: Operator<X3, R>,
    op5: Operator<X4, R>,
  ): R;
  public pipe<R>(...ops: ReadonlyArray<Operator<any, R>>) {
    const op = (arg: Arg) => ops.reduce((prev, op) => op(prev) as any, arg);
    return op(this.x);
  }
}

export function functional<X>(x: X): Pipeable<X> {
  return new Pipeable(x);
}

これによって、仮にResultへの操作がチェインされても以下のように書けるようになりました。

const r = readFileContent('~/Downloads/dog.txt');
functional(r)
  .pipe(
    Result.map(t => t.split('\n')),
    Result.map(numOfLines => `file has ${numOfLines} of lines`),
    Result.map(msg => `!!!! ${msg}!!!!`),
  );

// Pipeline operaorがきたらこう書ける
r
  |> Result.map(t => t.split('\n'))
  |> Result.map(numOfLines => `file has ${numOfLines} of lines`)
  |> Result.map(msg => `!!!! ${msg}!!!!`)

型注釈なしでちゃんと動作していますね?
TypeScriptの型システムは前提条件からの推論しかできないのですが、 r: Result<Text> という前提条件(この場合は引数)が先に出現するPipeline operatorは、このようなTypeScriptの弱点を補って動作させることができるようになるため、ある意味本家のML系言語よりも導入の恩恵が大きいかもしれません(要出典)。

OCamlなどでpipeがうまく働いているのは、operator単体の素晴らしさもそうですが上手く統一されている標準ライブラリのコーディングルールなどの環境もその一翼を担っているのだとおもいます。
ECMAScriptのpipeline規格はまだ前提となる方針のところから議論されている段階なので、TypeScriptで本格的に導入されるのは当分先になるでしょうし、導入されても歴史的カオスにまみれたジャバScriptワールドでどの程度ワークしてくれるのかは未知数です。
しかし似非関数型一派を自称する僕としては、この演算子が入ってくれると非常に愉快なことになるなぁとか、そんなところです。

以上。

補足

世の中にあるモナドなろう系TypeScript記事はclassの継承で再現する派が圧倒的に多いようです。
なのでそもそも今回前提としたResultの実装に疑問を抱いた人が多いかもしれません。というか↑の方式で記事書いてる人みたことない。

念のため継承で再現するとこんな感じですかね。

class Success<T> {
  constructor(private x: T) {}
  map<U>(func: (x: T) => U): Success<U> {
    return new Success(func(this.x));
  }
}
class Failed {
  constructor(private x: Error) {}
  map<U>(_func: (x: any) => U): Failed {
    return this;
  }
}

type Result<T> = Success<T> | Failed;

// こんな感じになる?
r
  .map(t => t.split('\n')),
  .map(numOfLines => `file has ${numOfLines} of lines`),
  .map(msg => `!!!! ${msg}!!!!`),

上の書き方の優位性は何もなしで普通にメソッドチェインで簡単に書けること、 interface Functor<T> のような型クラス(的なもの)を提供できることでしょうか。

しかしTagged Unionでは、Switch caseの条件式にかけたときにTypeScriptコンパイラが網羅性をチェックしてくれるという優位性があります。
Result型程度だと恩恵はないのですが、例えばElmでよく使われるRemoteData型などをTypeScriptでエミュレートしようとする場合などはSwitch caseの網羅性チェックの恩恵を嬉しく感じることが多いでしょう。

どちらも良し悪しだと思うのですが、擬似Pipeを導入することでそれなりに書き味もよくできるので今回は後者の方式を採用しました。

独断と偏見による ベルリン旅行に持って行って良かったもの3選

はい。
ベルリンに旅行に行ってきました。
実は出張じゃない、普通の観光目的の海外旅行ははじめてだったのでいろいろと新鮮でした。
ついでに持っていってよかったお役立ちの品々を忘備録的に書き溜めようとおもいます。

ちなみに変圧器なんかのアイテムは良かった/良くないのレベルではなくただの必需品なのでリストインさせません。

続きを読む

退職エントリーに対抗して在職エントリー

退職エントリーに関する一方的なお気持ちを語りたいんですけど、端的に言って我々に必要なのは在職エントリーじゃないでしょうか。

ちなみに僕は退職エントリーは書きません。
どうしても面白おかしく書いてしまい社会的カルマが溜まりそうだからです。

人は必ずいつか退職します。つまり退職に理由は必要ない、逆に在職し続けるには理由が必要です。
というのは建前ですが、惰性で在職していたり、あるいはほかに行き場所がないから在職しているようではロクな仕事はできないでしょう。逆に働き続ける理由を定期的に見つめなおすことでモチベーションが再発見できるわけです。

人間は変化を嫌うので「退職」という変化には理由を求めます。ストーリー性のない退職エントリーを書くと「いやあの会社のことだからほんとはもっと色々あったんでしょ」というような邪推をされるのはこれが原因です。

退職の理由は、辞めるに足る事案の発生か在職理由の消滅のどちらかあるいはその両方です。辞めるに足る事案がどういうものかというと、例えば社長の自己顕示欲を満たすために毎週月曜日に朝早く全社員を集めてASAKAIと称しバカ話をぶつなどがありますが、ぶっちゃけていうと「辞めるに足る事案」が理由で退職するのは本人の先見性のなさを改めて露呈してるだけで、他人が読むと面白いですが書いている貴方への益は何もありません。
在職理由の消滅、これは非常に神秘的な案件であり、その理由はドラマティックです。何故なら我々の仕事ぶりそのもの、そして会社の方向性が時間軸に沿ってどう変わっていったかというのがそこに出てくるからです。バンドマンは音楽の方向性の違いで解散しますが、社会マンも事業の方向性の違いなどで退職します。 "駅前で歌を歌う"

ちなみに僕が在職のモチベーションというのを非常に重要視するのは、僕が働くのが嫌いだからです。そしてTwitterで確認する限りみなさんも労働がお嫌いなようなのでたぶん僕と同じで在職理由が大変重要になってくるはずです。
働くのが嫌いな人間がなんとか働こうという気持ちになるには仕事が楽しくなければなりません。仕事が楽しいからこそクソきったねえ電車で毎日出勤してクソ箱に詰める気力を出せるわけです。
楽しくないことをしてるとクオリティも上がりませんし、クオリティが低い仕事をしていると自然とスキルも損なわれていき、自尊心は失われ、最後には雑魚死……。

まとめると、退職エントリーを書くのは悪いことじゃないとおもいます。
なんだかんだ建前はありますが、我々の立場は会社に比べて弱いですし、転職先を探すときに参考にする情報は少ないので入ってから気が付くヤベー制度とかも沢山あります。例えば「半年間で5日以上休んだらボーナス全額カットというすごい制度」とかのリーク(他人の日記帳を信じ込むのは馬鹿がやることなのであくまで参考程度にしておきましょうね)があることでそういう地雷を避けやすくなります。
あるいは日本の多くの組織で「誰かが辞める」というのはネガティブな側面のある事象なので、きれいな退職エントリーを書くことで前の職場でかかわった人々を安心させたりできるんじゃないでしょうか。わからんけど。

*1

ただ、自分のための日記帳として使うのであれば「在職エントリー」的なものを非公開かなんかで時たま書いておくのはすげー有意義なんじゃないかなと、そういうことを思った次第であります。

ちなみに私は無職です。書くべき在職エントリーはありません。

*1:ちなみに念のために書いておくと闇属性の退職エントリーに関しては「それを書いてたやつがただのヤベー奴だった」事例や、「それを書いてたやつがただのヤベー奴だったというエクスキューズが界隈では流れてるけど実際に環境がヤバかった」事例などがあり完全な情報戦です

レインボーシックスシージにおける壁補強と開通の考え方

R6S歴二日(二年前に一瞬だけやったことあるけどそれはカウントしない)の男の忘備録。

R6S初心者が定石についてて調べていて最初に躓くであろう場所。
それがどの壁を補強してどの壁を補強するべきではないのか。

ネットの記事やYoutubeを色々と探したけど、みんな「このマップはここ補強しましょうね~ここは開けましょうね~」というだけで理由を教えてくれない(そういう動画もあるのかもしれないけどみつけられなかった)ので考察したことをここに書いておく。

続きを読む