Mediumに引っ越しています
こちらで記事を書いています。 medium.com
このはてなブログは当分は更新予定がありません。
視聴メモ : Why JavaScript Programmers Hate You: an ode to dynamic languages, Jan Vitek
2013年のプレゼン。ここでのYouは、このプレゼンの聴衆である言語研究コミュニティを指している。以下視聴メモ。
Why JavaScript Programmers Hate You: an ode to dynamic languages, Jan Vitek
- 型は生産性を上げるいう伝統的な信念がある。静的型付けは、より良いものだと。目標は正しさ。
- その伝統的信念に反して、実世界では、動的型付け言語がデザインされ続けてきた。VB, Python, R, Lua, Java (= static + dynamic), JavaScript, PHP, Ruby, Clojure。
- Adobe Lightroomは、大半のコードがLuaで書かれている。簡単に書けるし組み合わせることができるから。スウェーデンで500万人の顧客が利用するペンションの管理システムは32万行のPerlで書かれている。正しさに関しては、彼らは独自のcontract systemを発明した。
- 実世界で使われている言語は動的型付け言語ということが、統計で示されている。
JavaScriptプログラマーがあなたを嫌うのは、だれも使わない言語を設計するからだ。
- コンピューティングを必要とする人々の中で、あなたたちはほんの一握りのニッチな集団に過ぎない。あなたの駆使するML, Haskell, Scala, C++は、プログラミングの専門家が大規模なシステムを開発するための、ドメイン特化言語だ。大多数の人々は、それらを使っていない。
- そのようなDSLおよびその厳格な規律を、他の人々(たとえば、生物学者、統計学者など)に押しつけてはいけない。人々にとって、プログラミング言語は、計算論的思考のための導入剤であるべきだ。
- 静的型付けのベネフィットとコスト。静的型付けは、実行前にエラーを抑止でき、効率的なコードを生成でき、機械的に検査可能なドキュメントとなる。
- なぜ静的型付けが悪いアイデア?静的型付けは、自明なエラーを潰すことに関してだけは役立つ。しかし、実世界での、Smalltalkという反例がある。静的型付けがなくとも、デバッガーによる高速なイテレーションで自明なエラーを簡単に潰せるので、静的型付けが提供するベネフィットは不要とされる。
- 静的型付けがより多くのエラーをキャッチできるようになるために、型システムはますます複雑になっていく。それはコンピューター科学者以外のユーザーにとって、大きな参入障壁になってしまう。
- 静的型付けは、そもそも本質的にモジュール間にまたがったグローバルなシステム。局地的な変更の実験をしたいだけなのに、システム全体で型チェッカーを満足させなければ実行できない。それはコードを硬化させて融通をきかなくさせ、進化の邪魔をして減速させてしまう。
- 静的型付けが動的型付けと比べて生産性を上げるというデータは乏しい。ある研究では、仮説に反して、両者の生産性に差は見られなかった。
- 動的型付け言語は手軽に初めることができる。動的型付け言語は静的型付けを強制しないので、プログラミングを初める敷居がとても低い。未完成のコードを実行できる。それが間違ったコードであれ、プログラミングを初めることができる。ほとんどのPHPプログラマーは、言語のマニュアルを一度も読んだことがない。
JavaScriptプログラマーがあなたを嫌うのは、あなたが実世界を無視しているからだ。
- プログラミング言語のデザインは、実世界での使われ方からの実証に基づくべきだ。
- JavaScriptのSunspiderベンチマークが想定するコードと、実世界でのコードの傾向はかなり異なる。
- リフレクションは静的解析を破壊するが、JavaScriptでは常用される。トップ100サイトの82%でevalが使われている。
JavaScriptプログラマーがあなたを嫌うのは、あなたが見当違いの問題を解いているからだ。
- Java向けのエイリアス解析について言及した13000もの論文PDFがあるが、結局はどのようなJVMにも導入されていない。実世界の、リフレクションかネイティブコードという要素を無視しているからだ。Ownership Types, Information Flow......
- プログラマーの生産性を第一に考えよう。大事な問題は、どうすればプログラマーが問題解決にかける時間を減らし、正しいコードを生み出せるかだ。
- 動的言語に異論は多い。動的言語は、プログラムをできる限り実行し続ける。未完成のプログラムを実行し、データタイプを自動変換し、エラーを飲み込む。JavaScriptではエラーはできるだけ遅延される。JavaScriptで実行されるWebサイトは、一部のコードが壊れても、UIの一部が動かなくなるだろうが、サイト自体は動き続ける。もしこれがJava製ならば、サイトがクラッシュするだろう。
- やりかたが分かるから定理を証明するのではなく、やる必要がある定理を証明しよう。
- 漸進的型付け、たとえば動的型付け言語であるThron言語では、型注釈を書くことによってプログラムを遅くすることはないし、よくテストされた実行中のプログラムを破壊しない。PHPやClojureでの型システムと同じ原理。
- Rは、もっとも広く使われているlazy functionalな言語。(その事実はだれも知らないだろうが)
結論としては、実際にプログラミング言語の技術は必要とされているが、ユーザーのベネフィットに沿うように、我々の価値体系を変化させる必要がある。言語研究は、実世界での使用方法に基づき、動機づけられなければならない。ダイナミズムを受け入れなさい。そして、静的な解析技術を(JavaScriptやPHPなどの)ランタイムに適用しよう。そうして彼らを手助けすれば、我々は彼らに大きな影響を与えることができる。
2013年のプレゼン。JavaScriptの人気については、参入障壁が低い動的言語であることは正解としても、単にWebでのランタイムの選択肢がそれしかないという不幸な環境要因であることが一番大きいだろう。最近はTypeScriptやDartがようやく盛り上がってきているけど。
仮に新たなWebのようなプラットフォームをデザインするとしたら、その言語のデザインについては、やはりシンプルでクリーンな動的型付け言語にして、その上であくまでオプションとしてユースケースに応じての型システム等のツールを提供できるようにして大規模開発にも適用可能なものにするのが良さそうだ。
DartからJavaScriptライブラリのAPIを呼び出す方法についてのメモ
文体をですます調に変えます
DartからJavaScriptライブラリのAPIを呼び出す処理を書くことがまれにあります。まれなのですが、そのたびに書き方を忘れたり罠にはまったりするので、ここにメモを残して思い出しやすくしておきます。
dartdevcでは動作するコードが、dart2jsでは実行時エラーになる問題
https://github.com/dart-lang/sdk/issues/33134
いきなり深刻な問題を取り上げますが、そもそもこの文章を書く気になったのはこの問題に気づいて解決するまでに数時間も費やしてしまったからです。
DartのJavaScriptへのコンパイル手法は大別して2つあります。開発時にはdartdevc
コンパイラーでデバッグのしやすさを優先したかたちのJavaScriptにコンパイルし、デプロイ時にはdart2js
コンパイラーで実行効率を優先したかたちのJavaScriptにコンパイルします。特に、dart2js
ではさまざまなコンパイラーオプションが用意されています。
JavaScriptの関数は引数の数が合わなくともそのまま受け入れて実行してしまうので、結果としてArity関係のエラーが起きないのですが、dart2jsでは、そのようなJavaScriptの関数呼び出しは実行時エラーにします。一方、dartdevcではそのようなエラーは(おそらく偶発的に)発生しないので、dartdevcとdart2jsではJavaScript API呼び出しのセマンティクスが異なるという問題がおきています。
上記のIssueではこの問題に優先度高で対応することになっているので、そのうち直ると思いますが、開発時に発生せずにデプロイ用コンパイル後に発生する実行時エラーに遭遇すると、かなり精神的なダメージが高いです。
そもそもpackage:js
でちゃんとJavaScript APIの実装どおりのシグニチャを書いて型で守ったら、このようなエラーには悩まされないというのはもっともですが、世の中には実装とAPI Docの記述が乖離しているJavaScriptライブラリがあります。
私が実際にハマってしまった例を挙げると、CライブラリをEcmascriptenしたオープンソースのJavaScriptライブラリに対して、そのAPI Docを参照しながらpackage:js
を使用して呼び出し用のDartコードを書いていったのですが、ローカル環境でのdartdevc
では問題なく動作していたので安心していたら、ステージングサーバーで実行時エラーに気づきました。いろいろな実験用コードを書いて問題を絞り込んでいく過程で上記のissueを発見し、JavaScriptコードを読んでいくと、API Docと実際のAPIのシグニチャがだいぶ異なるものが複数ある状態に気づきました。
JavaScriptは上記の通りarity mismatchをエラーにせずに、足りない引数にはundefined
を割り当てたり余計な引数は無視して実行するので、一部の引数をAPI Docに載せずに実装と乖離していても、API Docの記載どおりに呼び出せば動作してしまいます。とても怖いですね。
そのJavaScriptコードは、Ecmascriptenしたものなのでかなり読みにくく、とくにコールバック関数のシグニチャを把握するのが困難でした。結局、Ecmascripten元のC APIまで読んでそのコールバック関数のシグニチャを把握し、Ecmascriptenしたほうのシグニチャをそこから推測して、あとはエラーが出なくなるまでシグニチャを書き直して実行を繰り返して解決しました。
そんなこんなで数時間費やしてかなり萎えたのですが、上記Issueを読むと、dart2js
のセマンティクスのほうをJavaScriptのほうに合わせて制限を緩和する方向のようで、たしかに大雑把なJavaScriptの世界に合わせてdart2js
のJavaScript関数呼び出し時の型チェックを緩めたら、今回のような苦労をしなくてすむようになりそうです。
それまでは、JavaScript関数呼び出し部分に関しては、dart2js
でコンパイルしてテストするなり動作確認をすればデプロイ前に気づくことができます。webdev
コマンドでは、以下のように-r
または--release
オプションをつければ、dartdevc
でなくdart2js
で開発用サーバーが立ち上がります。
webdev serve --auto=restart --launch-in-chrome --debug --release
dart2js
にオプションを渡すには、build.yaml
をプロジェクト直下に置いて、たとえば以下のように書けばできます。
targets: $default: builders: build_web_compilers|entrypoint: # These are globs for the entrypoints you want to compile. generate_for: - test/**.browser_test.dart - web/**.dart options: compiler: dart2js # List any dart2js specific args here, or omit it. dart2js_args: - -O1 --verbose --show-package-warnings --dump-info
dart2js
のオプションに関するドキュメントは、https://dart.dev/tools/dart2js です。
https://github.com/dart-lang/sdk/issues/37105 のような関連Issueもあります。Flutter Webのためにも、優先度は高くなっています。
TypeScriptの型定義ファイルからDartのJavaScript呼び出しコードを自動生成する
https://github.com/dart-lang/js_facade_gen を使えば、一応d.ts
からpackage:js
用のDartコードを自動生成できるようです。しかし、更新が3年前で止まっているのであまり期待はしないほうが良いかもしれません。
なお、今回の問題のライブラリでは、d.ts
ファイルは残念ながら見つかりませんでした。
コールバック関数はallowInterop()
でくるむ必要がある
JavaScriptにDartのクロージャ(つまりメソッドや関数)をコールバック関数として渡すばあいは、そのDartのClosureをallowInterop()
でくるむ必要があります。allowInterop()
により、それがJavaScriptのClosureに変換され、JavaScript APIのコールバック関数として呼び出し可能になります。私はこの指定をよく忘れてしまいますのでここに記載しました。
正直、これはpackage:js
内部で解決してallowInterop()
を書かなくてもすむようにしてほしいです。関連Issueを読むとそのような計画はあるようです。Flutter Webの開発の進捗に合わせて、DartとJavaScriptの通信方法についても改善される機運が感じられます。
dart:typed_dataのJavaScriptとの関係
dart:js
のドキュメントによれば、以下の型に関しては、Proxyを噛まさずにJavaScriptと直接の相互利用が可能です。
- Basic types:
null
,bool
,num
,String
,DateTime
TypedData
, including its subclasses likeInt32List
, but notByteBuffer
- When compiling for the web, also:
Blob
,Event
,ImageData
,KeyRange
,Node
, andWindow
.
https://api.dartlang.org/stable/2.3.1/dart-js/dart-js-library.html
低レベルなJavaScript API呼び出し用ライブラリであるdart:js
のラッパーライブラリとしてpackage:js
があるという関係性です。dart:js
を使うことは稀で、通常はpackage:js
で事足りるはずです。
dart:typed_data
に、バイナリを扱うための型が定義されています。たとえばUint8List
やFloat32List
です。これらはそのままJavaScriptでのUint8Array
やFloat32Array
になりますので、そのままClosureの引数として渡して問題ありません(ただし、ByteBuffer
を除く)。
JavaScript PromiseとDart Futureの相互変換
Issueに上がっています。https://github.com/dart-lang/sdk/issues/27315
FirebaseのJSラッパー (package:firebase-dart) は独自で変換するコードを書いているように見えます。それぞれのパッケージで独自でやっているようです。詳しくは調べていません。
その他
Optional parameterとoptional named parameterをpackage:js
において使用する手法など、その他のトピックがいろいろある気がしますが、長くなってきたのでまたつまづいたときに思い出して記事にしようと思います。
Flutter Webの現状調査
Web特有の事情はどう解決するのかに興味があって内部構造などを調べていた。 開発が進むにつれて実装はどんどん進化して問題解決されていくだろうし、現段階のこの情報の正確性も保証しない。個人のメモを公開しているだけなので鵜呑みにはしないようにしてほしい。
あと、Preview版が公開された後にFAQが追加されているので読んでおくほうがいい。
https://github.com/flutter/flutter_web/blob/master/docs/faq.md
ニュース
まとめ
https://medium.com/flutter/a-roundup-of-flutter-news-at-google-i-o-453bb3249981
Flutter Webの開発体験とPreview版段階の技術的制約についての解説
https://medium.com/flutter-nyc/under-the-hood-with-flutter-for-web-bc0d5ce1c11e
Tech Preview版
現段階の一時的な措置として、Web用に名前空間を分けている。
flutter_web/packages/flutter_web
は、flutter/packages/flutter/
からコードをコピーされている。flutter_web/lib/io.dart
のように、dart.io
環境を前提としたコードをwebで動かすための一時対応コードもある。Flutter向けpackageは、たとえネイティブAPIとの通信をしていなくとも、FlutterとFlutter Webのpackage名が分かれているので、Flutter Webではまだ使えない。たとえばpackage:providerはFlutterに依存があるためpackageの依存解決ができない。
現段階では、さまざまな制約が存在する。上記のNYTimesのゲームアプリの作成を通じたFlutter Webの解説記事から引用。
- Web-specific code that requires the html package. Keeping separate web/mobile/desktop branches is tricky.
- No native debugger. While Chrome DevTools is great, working with the IDE debugger is a superior experience.
- Offline persistent storage that works across web & mobile.
- Dart isolates are not supported in Flutter for web. Javascript is single-threaded.
- Text input does not yet match platform conventions.
- Selecting/copying of text is not yet supported.
- The browser back button works just like the Android back button, but the forward button is not supported yet.
- Making Flutter for web more SEO-friendly.
- Printing with a @media stylesheet isn’t yet supported.
エンジン
Under the hood, Flutter for web draws most elements to the canvas directly except text, which is rendered by the browser
https://medium.com/flutter-nyc/under-the-hood-with-flutter-for-web-bc0d5ce1c11e
すべてのUIをFlutter Widgetで書き、Web用にcompileする。
https://css-tricks.com/the-css-paint-api/
flutter_web_ui/lib/src/ui/
<->pkg/sky_engine/lib/ui/
flutter_web/packages/flutter_web_ui
が、Webプラットフォームに依存したモジュール。ui/
にdart:ui
のコードがコピーされて、一部にWeb対応のための改造がされている。engine/
が、Web版Flutter Engine。ネイティブ版はC++とDartで書かれているもの。Web版はもちろんJavaScriptではなくDartですべて書かれている。
flutter_web_ui/libsrc/engine/dom_renderer.dart DOM操作に使用するサブセットを定義している?
- flutter/engineへのflutter web用エンジンの統合作業が始まっている。
https://github.com/flutter/engine/pull/8891
レンダリング
テキスト以外はほとんどCanvasでレンダリングしている。Custom ElementとCanvasのカタマリ。位置はabsolute positionやtransformなどで調整。
Chromeのdev toolでelementsタブを見ても構造の把握はほぼできない。代わりに、Dart Dev Toolsを開いてネイティブ開発と同様にFlutter専用の開発ツールを使う。
CSS Paint APIでレンダリングするオプションもあるが、ブラウザーのサポート率がまだ低いので、まだ先の話だろう。
UIに関しては完全にFlutter frameworkによってDOM APIが隠蔽されている。AngularのDOM APIを拡張する思想とは対照的。
ジェスチャー
JavaScriptのGestureライブラリを使っているわけではなく、あくまでFlutter frameworkのGestureコード(flutter/lib/gestures.dart)をWeb用にコンパイルするようだ(実装は追っていない)。
Widgets
(プラットフォーム依存コードが含まれていなければ)既存のWidgetをそのまま再利用できる。HTML, CSS, JavaScriptのDOM APIを意識することはなく、完全に隠蔽されている。
ルーティング
Flutter WebはSPAとして動作する。History API操作を抽象化するモジュールを内部で使用している。現在は、AngularDartのルーティング関連モジュールをコピーしている。Flutterのnamed routingでコードを書くと、WebではUrlにpathがつく。paramとquery paramをハンドリングする方法は分からなかった。自分のFlutter力が足りないだけの可能性もある。
最悪、Flutter Web起動時の処理としてdart:htmlを使ってparamとquery paramを取得してFlutter Webに渡せばいけるだろうが、その前にquery paramsがあるとhomeにredirectされてしまう動作を観測している。
History push時にquery paramsを指定したい。
もしparamsとquery paramsをハンドリングする仕組みがまだないならば、さすがに現段階で実用的なWebアプリを作るには制約が強すぎる。
404ページにする方法もまだ調べていない。
当然、
<a></a>
tagを書くことはできない。外部リンクを開く方法は調べていない。url_launcher
のような仕組みを使うのだろうか。その他、Matrix Paramsのような慣習的な記法をFlutter Webはサポートするのだろうか。
アクセシビリティ
src/engine/semantics/
でFlutterのSemantics
をARIAにコンパイルしている。まだ発展途上のようだ。
ペイロードサイズ
- Flutter Web Hello World サンプルアプリで146KB zip。これから開発が進むにつれて削減されていくだろう。
- アプリが大規模になれば、AngularDartのDeferred Loadingのような起動時のペイロードサイズを削減する仕組みがWebアプリでは必要になるが、いまのところそういったものはないようだ。
- AngularDartと同様、普通のアプリならば全体で300KBから500KBくらいになるはず。
アニメーション
まだカクカクする。改善作業中とのこと。もちろん、CSSやWeb AnimationといったWeb APIを直接操作することはない。
JavaScriptライブラリの利用
- プラグインシステムはまだ設計段階。
- 現段階では、
dart:html
やpackage:js
を使って直接操作する。
未実装API
一部のAPIは未実装のようだ。たとえばGradient APIが未実装で、実行時にUnimplemented Error
を観測した。
SEO
- SSRも技術的には可能そうだが、アプリケーションコードをSSR対応にするために条件分岐したりstateの引き継ぎをしたりするのはとてもダルいので、可能でもやりたくないなという感想。
- DartをJSにコンパイルすると結局EcmaScript 5になるし、Google Search Botは最新版Chromeになることが発表されたので、レンダリングに問題は起きないだろう。とはいえ、レンダリング方法がかなりアグレッシブなので、Google Botからどう見えているのか、実際にGoogle Search Consoleなどで動作テストしてみないと怖い。
- OGPなどの
html head
に配置するメタデータは当然サーバーでレンダリングしておく必要がある(SSRではなく。ややこしい)。 - CDNでレンダリングなど、状況は変わりそう。
構造化データ
HTMLを書けないので、Schema.orgやMicrodataをどうやって指定するのか。HTML埋め込み機能が必要になるのだろうか。
所感
歴史的事情が積み重なって複雑で扱いづらいHTML, CSS, JavaScriptを、Flutter Frameworkが隠蔽して古層にしてくれる。
iOS, AndroidのネイティブアプリをFlutterで開発する際でも、プラットフォームの事情を知らなければハマることもあるので、Webでも同じようにプラットフォームの知識は必要になるが、これまでのようにWeb APIの進化に精通する必要性はだいぶ薄れていくはず。Extensible Webの思想的には喜ばしいことでは。
Flutter Frameworkが隠蔽してくれる代償として、Frameworkの抽象から漏れたWebの機能を利用しづらくなりそう。(たとえば、いま話題のPortal
とか。)Web Standardへの対応は進んでいくだろうけど、それまでは生のHTMLやAngularDartなどの従来型のソリューションとの併用が必要になるはず。Flutterがすべてのユースケースに対応するかについてはまだ懐疑的だけど、長期的にはなんとかしてくれそうという期待感はある。
AngularDartは、DOMを拡張する思想のフレームワークなので、Webに特有の要求仕様に対応した詳細な制御が可能。DOMを完全に隠蔽するFlutterとは対照的。詳細な制御ができるが、HTMLやCSSを扱う必要があるので、Flutterに慣れるとそれがとても面倒に感じる。UI以外はAngularDartとFlutterで共有可能なので、AngularDartでWebアプリを書きつつ、Flutter Webの適用可能範囲が広がってくればFlutterでUIの置き換えを検討する戦略。
WebにはAndroidやiOSにあるような統一されたデザインガイドラインがあるわけではない。マテリアルデザインやiOSデザインの採用を拒否したり理解度が低いプロジェクトではFlutterの高い生産性を発揮しにくそう。現段階では、ペライチ用途やWebサイト用途には向いていない。まずはSPA Webアプリ向け。
#AngularDart and #FlutterWeb are fundamentally different. There are likely apps where one will work much better than the other.
— Kevin Moore (@kevmoo) May 16, 2019
Google I/O 19 Dart関連セッションの視聴メモ その2: Pragmatic State Management in Flutter (Google I/O'19)
Pragmatic State Management in Flutter (Google I/O'19)
Flutterで状態管理をする方法についてのセッション。去年も同じペアで同じテーマのセッションがあった。
https://www.youtube.com/watch?v=d_m5csmrf7I&list=PLjxrf2q8roU2no7yROrcQSVtwbYyxAGZV&index=4&t=0s
ひとつのWidget Treeしかないなどの単純なアプリの場合をのぞき、アプリの状態管理は重要な問題となる。
Agenda
- 状態管理の重要性
- さまざまな取り組みの推移
- Flutterでの実践的な状態管理方法
論点
- 理解しやすく読みやすく保守しやすいこと
- テストしやすいこと
- 実行性能(パフォーマンス)が高いこと
残念ながら万能の解決策はないので、あなたのユースケースのなかでエッジケースを考慮するべき。
案1: グローバル変数で状態共有
動くが、ユニットテストも保守も難しい。Widgetが強結合してしまっている。本当に単純なアプリ以外ではするべきではない。
UI = f(state)
- UIの状態は、アプリケーションの状態を反映したもの
- UIは他のUIの状態を直接管理しない。
- なんらかのNotifierで状態をUIに通知する
案2: package:scoped_model
ScopedModel
WidgetをWidget Treeの先祖(ancestor)に配置し、その子孫(descendant)達に通知する。(現在は、後述のpackage:providerで代替できる。)
案3: BLoC
複雑なアプリ向け。Streamを活用。かなり複雑なアプローチ。
案4: package:provider
package:provideでなくpackage:provider。
package:provideは、ScopedModel version 2とでもいうべきもので、Googleからオープンソース化されたが、同時期にCommunityにより開発されたpackage:providerのほうが優れた選択肢という結論になり、package:providerが公式に推奨された。package:provideは非推奨に。
複雑なアプリを書くためには、
- Dispose callbackでリソースの後始末をする
- ChangeNotifierProvider、StreamProviderなどの機能を活用してレンダリングコストを抑える
- MultiProviderで複数の依存を管理する
Single State Objectを避ける
ただ一つの巨大なState Objectは作りたくない。通常は、精緻な状態管理のためには種別ごとにComponent, Classを分ける。 Providerによってそうすることができる。 (筆者注: これはReduxの一枚岩stateを批判をする文脈ではなく、package:providerで状態を分割管理して更新する文脈)
Performance, Measure
- Widget Treeの更新範囲をできるだけ小さくする
- Flutter Driverでパフォーマンス測定してコードの最適化のための根拠にする。(測定なしに当てずっぽうでコードの最適化をしない)
https://medium.com/flutter-io/performance-testing-of-flutter-apps-df7669bb7df7
package:providerが万能、というわけではない
以下のような場合は単純にStateful WidgetのsetState()
で状態管理すれば十分で、package:providerは過剰設計。
Testing
Flutterにはheadless test frameworkが同梱されている。ひとつのテストを数ミリ秒で実行できる。
まとめ
状態管理においてScoped Modelはそんなに精巧な手法ではないが、申し分なく良い手法。(筆者注: ここでいうScoped Modelは、Scoped Model version 2であるpackage:provide、そしてそれを非推奨にして代わりに推奨しているpackage:providerを指すと解釈した)
詳細な状態管理が必要になりStreamとRxDartを好む場合は、BLoCパターンが素晴らしい手法。
Reduxのパターンに慣れている人には、Flutter向けReduxライブラリが申し分ない手法。しかし、Flutterをこれから始めるならば、package:providerで状態管理を始めることが本当に信頼できる良い選択肢である。
所感
- 詳細な状態の制御が必要ならばBLoCが最適
- FlutterでのReduxの採用は、Reduxに慣れていてこだわりがある人以外にはあまり推奨しない
- いまからFlutterを始めるならば、Providerで状態管理を始めてみてBLoCパターンにも挑戦すると良い
結局はProvider + BLoCに落ち着きそう。最初からProvider + BLoCの決め打ちで状態管理をするのも十分アリだと思う。BLoCの難点は初学者にとってはStreamの学習コストが重いことだと思う。
ProviderはAngularでいう階層型Injectorと解釈できる。昔はDagger2ベースのユニバーサルDIパッケージの開発の構想があったが、実現はせずに代わりにFlutterではProviderを、AngularではAngularのDIの仕組みを使い続けることになるようだ。
UIとModelの通信には、ProviderかBLoCを状況に応じて選択という結論。Model自体の内部の設計についてはこのセッションではなにも語られていないが、主にJavaで培われてよく知られているオブジェクト指向のデザインパターンおよびクリーンアーキテクチャ的な設計パターンを適用していけばよく、BLoCパターンについてもデザインパターンへの理解があるならばあとはStreamの操作方法を理解すれば習得できる。
FlutterとMVC
ここで述べられているUI=f(state)
は、結局、MVCアーキテクチャを関数スタイルで言い換えたもの。Controllerを通じて状態変更のためのメッセージをModelに送り、状態変更したModelの状態をViewに反映する。PDSしてModel Driven View。Widget
はView + Controller
。
Google I/O 19 Dart関連セッションの視聴メモ : Dart: Productive, Fast, Multi-Platform - Pick 3 (Google I/O'19)
Google I/O 19には数種類Dart関連セッションがあり、Youtubeに公開されている。その視聴メモ。まずはひとつめ。
Dart: Productive, Fast, Multi-Platform - Pick 3 (Google I/O'19)
https://www.youtube.com/watch?v=J5DQRPRBiFI&list=PLjxrf2q8roU2no7yROrcQSVtwbYyxAGZV&index=6&t=0s
Dart入門者向けののSessionだった。
Dart 1時代から一貫して、総合的な生産性にフォーカスして言語とライブラリ、エコシステムに投資
3つの側面
- Productive
- Fast
- Multi Platform
Productive
- Dartはマルチパラダイムプログラミング言語。
- KotlinやSwiftと同じC系構文。
- Dartはオブジェクト指向言語。オブジェクト指向言語にポジティブネガティブ両方の評価があるが、UIの構築という分野ではほんとうに優れている。
- Dartは関数型の特徴も併せ持つ。OOFP。
- 静的型システムサポート。Reified Generics。型推論。
Dart - ライトウェイトなOOP言語で、関数スタイルと静的型付けをサポートした言語。
Dart 2.3。UI-as-code。ソースコードで視覚的にUIの構造を把握しやすくするための進化。Listのなかでif
for
を使用可能に。Spread operator。UIをより宣言的に記述可能に。
DartVM
CFE (Common Frond End)
Backend
- JIT Compiler
- RunTime
- Debug Service
Hot reloadはDartVM前提
Hot reloadはRuntimeのStateを維持したままコードの変更をRunTimeに反映する。
Flutter CLI -> FE Server (on CFE) -> DartVM (JIT Compiler, Runtime, DebugService)
AoT compile
開発中は生産性向上のためにDartVMで動くが、デプロイ時はコードサイズの最小化、パフォーマンスの最大化のために、AoTコンパイルを行う。ネイティブコードで動作。
Dart for Web
DartはFlutter以前からWeb向けに投資を続けてきた。Google Adsなどの多くのWebアプリがDartで動作している。Google AdsはGoogleの最も重要なビジネスのひとつ。何百万行ものDartコードで動いている。
Web向けHot reloadは安定化にむけ作業中。
Flutter Web
DartVMのサポートが手厚いので、Flutter WebはWeb Browser向けに一部の基盤を差し替えるだけで動作。
Dartの他の様々な動作環境
- Chrome OS
- Google Home Hub
- Raspberry Pi
- CLI
- Cloud
Non-nullable Types (NNBD)
- 長い間ペンディングになっていた仕様。
- Null safety。Runtime ErrorになっていたNull関連エラーをコンパイルエラーにする。
- この破壊的変更の痛みを最小限にするための移行施策として自動migrationなどを計画中。
- コンパイル時にnull checkが要らなくなるので、パフォーマンスとコードサイズにも大きな効果がある。