Dart2を3時間以内にそこそこ書けるようになっておきたい。
Flutterのおかげで、汎用クライアント側言語になりそうな芽がでてきたDart。
学習コストが低いという噂ゆえ、3時間でそこそこ学べると思われる。
前提:DartのJava的な側面+αの解説を30分以内に流し読み
「KotlinとJavaができる人向けDart速習」が抵抗なく読めるならば,おそらく2~3時間でdartのコードの基本的な読み書きはできるようになるはず。dartな先達たちによるコメントが理解できるならはば、なお良い(私は読みこなす時間がなかった)。時間が余ったら、Effective Dartをちら見。
方針: Dartのコードを、なるべくスクリプト言語的に書いていく。
上速習を読み、クライアント側のコードを、IDEの支援の下、概ね型安全に記せるスクリプト言語としてdart2.xを捉えて、入門するのが良いと判断。ということで、略すな危険、というところ以外は略記してみる(Javaに似ているとされるdartを、groovy/scala的に捉えていくといったところ)。
ということで、IDE(Intellij)の支援を前提に、初めてDartを書いていく。なお、Dart のコーディング規約はEffective Dartの冒頭に書いているあたりを守れば、自由に決めていいものと思っている(Goのようにコンパイルの際のLinterが定められているというわけではない、という意味)。実質的にも、Dart1から言語仕様に改変があったわけだし、推奨される書き方は今後も変わりうるものだろうし。Effective Dartは触りしか見ていないので、ツッコミどころはいろいろある書き方かもしれないことに留意されたし。よりベターな書き方のツッコミ大歓迎。
#ハローワールドは、非同期処理を含むコードから。
dartを書こうとする人のほとんどは、FlutterやAngularDartなどのUI周りで非同期処理を扱うことになる。いわば非同期処理は、dartのハローワールド。そのため、巷にdartで非同期処理のコードはけっこう転がってくれている。ただ,dart1.x時代の例が多いためか、簡潔さにやや欠けるコードが多い気がする。
ということで、ひとまず省略できるものはなるべく省略してみたコードを書いてみる。アプローチとして間違っているところがあるのかもしれないが、短時間でも「俺ならこう書く」的な意識で取り組むと構文とかが記憶に残りやすいと思う。
ということで、以下のように書いてみて、dart2.1のコンパイルを通した。
class Fmt {
Fmt(this.s); //コンストラクタ
final String s;
str() => "Hello ${s} at ${new DateTime.now().toString()}.";
}
futureTest(String s,int i) => new Future.delayed(
Duration(seconds: i), () => //i秒待った後に処理が行われる。
Fmt("$s, again. Around $i seconds later").str()
);
/// 注、await を使う場合、 async で囲まなければコンパイルエラーとなる。
main() async {
const target = '世界';
print(Fmt(target).str());
final ret = await futureTest(target, 3);
print(ret);
}
実行結果
Hello 世界 at 2018-12-22 21:49:19.684302.
Hello 世界, again. Around 3 seconds later at 2018-12-22 21:49:22.695433.
mainでクラスFmtを生成し、はじめに同期的に呼び出した結果をprint。
次いで、futreTestメソッドを介し、非同期的にFmtを生成し呼び出した結果をprintしている。
パット見、Android Javaよりは明らかに簡潔だと思う。最近のJavaの方はほとんど知らないのだけれど。
書き方の方針
書くにあたり、以下の方針を採用。
1) void/const/newは、省略
voidを書くと一気にjava臭くなるので、略。あと、constもメソッドについては略。classのnewも略。IDEを使うこと前提ならば、過度な省略は即座に赤線が引かれるので、Dart2が用意してくれいてる略記法は使うが吉と思った。なお、アクセス修飾子なしのメソッドは、publicだ(scala/groovyあたりと同様)。
2) アロー演算子 => を積極活用
dart において、アロー演算子(...でいいんだよね?)は、
=> ...; x;
という表現は、
{
...
return x;
}
のシンタックスシュガーとのこと。
アロー演算子を使うことで、中括弧を使う言語にありがちな
...
}
}
}
}
といった表現の回避できる。同様に、中括弧を略せるScalaなんかではなるべく中括弧を略すなという意見も多かったりするのだけれども、Flutterでwidget記す際の例題には、アロー演算子を積極活用されている模様。
ちなみに、Classはこう書きたかったが、エラーになった。
class Fmt(String s) { //エラーになった
str() => "Hello ${s} at ${new DateTime.now().toString()}.";
}
自分の理解が足りないのだろうか。いずれにせよ、Scalaでいうcase class(Kotlinでいうdata class)が欲しいところ。
3) import 'dart:async';は、必要な場合のみ書く。
Dart2.1から、FutureとStreamがdart:coreから利用可能なため、明示的なインポートは不要に。
4) セミコロンを忘れずに。
ここだけはJavaっぽい。よく忘れるけれども、IDEが怒ってくれるのであまり困りはしない。
非同期的な書き方は継続的に学ぶべし。
どの言語を使う場合でも、ある程度以上に複雑なアプリにおいては、非同期処理は鬼門になりうる。
見通しを良くする書き方は、継続的に学んでいくべきだろう。
例えば、以下を参考とさせていただく。
List.forEachでasync, awaitしたいならFuture.forEachを使おう
書いてみた:
class Fmt {
Fmt(this.s); //コンストラクタ
final String s;
str() => "Hello ${s} at ${new DateTime.now().toString()}.";
}
futureTest(String s,int i) => new Future.delayed(
Duration(seconds: i), () => //i秒待った後に処理が行われる。
Fmt("$s, again. Around $i seconds later").str()
);
main() {
const nums =[1,2,3,5];
const target = '世界';
// 注、Future.forEachを使うことで、 目的の処理でのみasyncをつけれぱ良くなる。
Future.forEach(nums, (num) async {
final ret = await futureTest(target, num);
print(ret);
});
}
Hello 世界, again. Around 1 seconds later at 2018-12-23 00:56:03.653855.
Hello 世界, again. Around 2 seconds later at 2018-12-23 00:56:05.667420.
Hello 世界, again. Around 3 seconds later at 2018-12-23 00:56:08.669242.
Hello 世界, again. Around 5 seconds later at 2018-12-23 00:56:13.670478.
mainがクリーンになったね。
頭の中にとどめて置くべき事柄。
1)三項演算子は、見通しが良くできる場合に使用
Dartでは、python的に、以下のようには書けない。
ret = X if (...) else Y
変わりに
var ret1 = (...) ? X : Y;
var ret2 = (...) ? _fn1() : _fn2();
といった風に書く。
ちなみに、_fn()という書き方はプライベートメソッドだそうだ。
大文字/小文字でpulic/privateを区別するGoと似ているわけだね。
書き方を練習しておこう。
例1:
/// private methods
_printPositive(int i) => print("0以上の値 : $i");
_printNegative(int i)=> print("負の値 : $i");
//三項演算子「(...) ? _fn1() : _fn2()」のパターン
PRINT(int i) => i >= 0 ? _printPositive(i) : _printNegative(i);
main() {
PRINT(3);
PRINT(0);
PRINT(-2);
}
例2:
/// private methods
_fnPositive(int i) => "0以上の値 : $i";
_fnNegative(int i)=> "負の値 : $i";
PRINT(int i) => print(
//三項演算子「(...) ? _fn1() : _fn2()」のパターン
i >= 0 ? _fnPositive(i) : _fnNegative(i)
);
main() {
PRINT(3);
PRINT(0);
PRINT(-2);
}
0以上の値 : 3
0以上の値 : 0
負の値 : -2
2)今後のためにmixinを知っておく。
DartのMixinについての解説 (Dart 2.1対応)を参考。
(引用) Dartは言語レベルでMixinを採用しており、Collectionライブラリ、Flutter framework, AngularDartをはじめ、あらゆるライブラリ、フレームワークで利用されている。
Dartでは、すべてのclassは暗黙的に自身のmixinを宣言しており、mixinとして振る舞うことができる。ただし、このシンプルな仕様には残念ながらいくつかの問題があるとみなされ、Dart 2.1において、mixin宣言専用の構文を用意するなどして仕様が改訂された。
ということで、今後のdart学習では、初歩のうちからmixinを学んでおいたほうが良さそうだ。
上のブログ上のコードを動かしてみた:
mixin M1 {
int aM1Field = 42;
String aM1Method() => "M1 method";
}
mixin M2 {
int aM2Field = 43;
String aM2Method() => "M2 method";
}
mixin M3 {
int aM3Field = 44;
String aM3Method() => "M3 method";
}
abstract class S {
int aSField = 45;
String aSMethod() => "S method";
}
class C extends S with M1, M2, M3 {
int aCField = 46;
String aCMethod() => "C method";
}
main(){
final c = C();
// is関係
assert(c is M1, true);
assert(c is M2, true);
assert(c is M3, true);
assert(c is S, true);
assert(c is C, true);
print("${c.aM1Field} & ${c.aM1Method()}");
print("${c.aM2Field} & ${c.aM2Method()}");
print("${c.aM3Field} & ${c.aM3Method()}");
print("${c.aSField} & ${c.aSMethod()}");
print("${c.aCField} & ${c.aCMethod()}");
/* 以下はコンパイルエラーになった。
assert(c.aM2Field, 43);
assert(c.aM3Field, 44);
assert(c.aSField, 45);
assert(c.aCField, 46);
assert(c.aM1Method(), "M1 method");
assert(c.aM2Method(), "M2 method");
assert(c.aM3Method(), "M3 method");
assert(c.aSMethod(), "S method");
assert(c.aCMethod(), "C method");
*/
}
実行結果:
42 & M1 method
43 & M2 method
44 & M3 method
45 & S method
46 & C method
見ての通りの振る舞い。馴染みのない方は,kotlin/scalaのtraitを参照。
感想
他言語の習得者にとって、Dart2の学習コストは低い。
理想的には、半日くらいかけてeffective dart含めて学んでおくと良いと思う。
1.0に達したFlutterに注目が集まったことで、Flutter本も何冊が出てきているようなので、今後は日本語だけでも入門できるようになっていくのだろう。
python使いに親しみやすいシンタックスシュガーが導入されるとDartが普及するのでは。
サーバ側をpythonという人にとって、手に馴染みやすいクライアント言語は少ない気がしている。pythonの内包表記などのシンタックスシュガーのいくつかがdartに導入されると、dart使おうという人が増える気がした。
終わりに : ScalaかGroovyをbetter Javaとして使ってる人に向けて。
最後に、サーバ側で、Javaより高い生産性を期待しScalaかGroovyを使ってる人(含、自分)向けに。
サーバ側でKotlin使っている人は、クライアント側もKotlinが吉と思うので略。個人的にはDartに一番なじむサーバ側言語はGrrovyだと思っている。けれども、Future/async/await/Streamあたりの非同期処理の記法は、Scalaと丸かぶりなので、クライアント側の汎用言語としてdart、サーバ側のAPIをscalaで切るという生き方は、(特に自社開発サービスの場合)今後はありと思いはじめている。あと、dart2.1から、scalaのtrait相当のmixinが導入されたので、よりscalaと馴染みがよくなったかも。IDEもIntellij使えるしね。
ということで、今後に備え、自分は、ひました時に、Flutterのコードを読んでいく。Flutterという充実したプロダクションのコードを読めることがdartの良い点だと思う。