「とりあえずDelegateやKVOを代替する目的でRxSwift使ってみるか〜。」と思ってたら痛い目を見た系iOSエンジニアです。
前述の理由から、Rx周辺の色々なことについて調べたのでまとめました。
用語
FP = Functional programming(関数型言語)
RP = Reactive programming
OOP = Object-oriented programming(オブジェクト指向言語)
FRP = Functional Reactive programming
FP系
関数とは
入出力の「変換」を行うもの
純粋関数とは
- 入力のみで出力が決まる
- 入出力変換以外の副作用を起こさない
FPとは
入出力の変換の連鎖によってアプリケーションを記述する。
ElmはFPスタートで部分的にOOPを取り入れた言語。
scalaはOOPスタートで、FPを取り入れた言語。
OOPとFPの考え方
流動性に関しての捉え方
OOP → 流動性をカプセル化することによって、コードをわかりやすくする。
FP → 流動性を最小限にすることで、コードをわかりやすくする。
OOPのメリット
内部の状態を隠蔽してインターフェースを記述すればいいので、要求を簡潔に表現できる。
データとロジックをまとめて記述する。
OOPのデメリット
状態が複雑になって、把握しきるのが難しい。
(個人的感覚)抽象化って本質的に難しいもの。しかも現実世界に存在しない概念を抽象化する。基本的にオブジェクト思考は難しい。
FPのメリット
入出力の連鎖なので、副作用がない。
データとロジックを分離して記述する。
FPのデメリット
全てを入出力変換の連鎖で書くため、記述が冗長になる。
モナド
関数に対して、エラーとかのおまけをくっつけたいとかログを出したい時があるが、関数型ではこれらを内部で色々やるのは副作用になってしまうので、おまけと本来の出力を便利に扱うためにモナドを利用している。
ぶっちゃけイメージがまだついてないけど関数型プログラマのための Rx 入門(前編によると
『Observable は単なる非同期データストリームにおけるモナドのインスタンスだよ。何か問題でも?』
らしい。
リアクティブとの関連性
リアクティブシステム
「イベント駆動を用いて、伸縮性・耐障害性を実現し、高レスポンスという価値を届ける。」
RP
データの流れとデータの値の変化の伝搬にフォーカスしたプログラミングパダライム。宣言的に書く。
例えば、命令的に書いて以下のようにしても、出力は2。
var a = 1
let b = a + 1
a = 2
print("b = \(b)")
しかし宣言的に書いた時は (a + 1) という計算の宣言が正しければ良いので、aの入力によってはbの出力が3になるのが宣言的プログラミング
let a = BehaviorSubject(value: 1) // a = 1
let b = a.map { $0 + 1 } // b = a + 1
b.subscribeNext { print("b = \($0)") }
a.onNext(2) // a = 2
※コードはRxを使った設計をビジュアル化するからお借りしました。
FRP
FPとRPの合成パラダイム。時間の流れと値の変化をシーケンスとして取り扱い、入出力変換を連鎖させて宣言的に書く。
Rxとは
観測可能(observable)なコレクションとLINQスタイルのクエリ演算子を使用して、非同期なイベントベースのプログラムを合成するライブラリ。(Rx = Observable + LINQ + Scheduler)
非同期データストリームをObservableで表現
非同期データストリームに対するクエリにLINQ演算子を使用
非同期ストリームの並行性をSchedulerでパラメータ化
非同期データストリームをObservableで表現 = 時間の流れと値の変化をシーケンスとして取り扱う
map, filter などで副作用を考えずに要素に対して入出力変換が書ける = 関数言語的
これで
「FRPのプログラミングパラダイム」を実現出来る?
(個人的見解)
面白かった話
アンチ細かい分割
小さい単位でモジュール分割とかって、副作用をコントロール可能な単位に分割するための話で、このために階層構造や分割単位がめちゃくちゃ増えて、理解が容易ではないレベルまで膨らんでしまう。これに対して、そもそも副作用がない関数言語では別の設計ができるかも。というやつ
アンチレイヤードアーキテクチャ
テスタビリティ(試験性)のためだけにレイヤーを利用しても、不必要な複雑性を増やすだけなのでは。っていう話
[TDD再考 (6) – テスト容易性 ≠ 良いデザイン] (https://ubiteku.oinker.me/2015/11/17/tdd再考-6-テスト容易性-≠-良いデザイン/)
DIの必要性
DIして取り替え可能にしているけど、本当に複数の実装が必要なケースって頻繁にあるの?って話
Elixir から Elm の流れで、いよいよオブジェクト指向に対する懐疑心が無視できないレベルに達した2017年冬。
間接層のご利用は計画的に
間接層はもろ刃の剣であることに注意しなければなりません。1つのものを2つに分割するということは、それだけ管理しなければならない部分が増えるということなのです。また、オブジェクトが、他のオブジェクトに委譲を行って、その先もさらに委譲を繰り返すような場合、プログラムが読みにくくなるのも事実です。つまり間接層は、最小限に絞り込むべきなのです。
[TDD再考 (6) – テスト容易性 ≠ 良いデザイン] (https://ubiteku.oinker.me/2015/11/17/tdd再考-6-テスト容易性-≠-良いデザイン/)
感想
RxSwiftを導入するかどうかの基準に対して、「リアクティブさを求めるかどうか」が自分の中での観点だったが、加えて「関数言語的に書きたいかどうか」も加えていいと思う。
Rxを利用する時は、オブジェクト思考で考えるのとは別の基盤として「FRP(もしくはRP、FP)で考える」っていうのが必要っぽいし便利ぽいのでとりあえずやってみる。
というかFRPがSwiftで書けることにめっちゃテンション上がってきた🤗
参考一覧
Elixir から Elm の流れで、いよいよオブジェクト指向に対する懐疑心が無視できないレベルに達した2017年冬。
関数型つまみ食い: 関数型とはプログラミング言語ではなく、プログラムデザインの問題であることに気づく