Swiftで利用できるLooseCodableというライブラリを作った
LooseCodableというライブラリを作った。
CarthageとSwiftPackageManagerからインストールして利用することができる。
このライブラリを作ろうと思った動機だが、サーバーサイドのAPIレスポンスが安定していないというのが理由だ。
主にIntegerとString、IntegerとBooleanの型が様々な形式で返ってくるのだが、SwiftのCodable
*1はこれらの型を曖昧に変換することを許さず例外を投げてくる。
例えば、Booleanで扱いたいケースで0
か1
で返ってくるケースとtrue
かfalse
で返ってくるケースが混在しているだとか、10
で返ってくるか"10"
で返ってくるかが混在しているかなど…
Codable
登場以前は自前で書いたマッパーがこのあたりの差異をいい感じに吸収してくれていたのだが、Codable
へ移行したと同時にこのような問題が顕在化してしまった。
Codable
を利用すること自体は間違いのない選択だと考えているが、この気まぐれなAPIとは相性が悪い。
そこで、この気まぐれAPIを快く受け入れるべくLooseCodableを作ったというわけだ。
さて、このライブラリだが期待する型をLooseCodable
という型で包んであげることでいい感じに差異を吸収してくれるものだ。
struct Hito: Codable { // このように定義しておくと let age: LooseCodable<Int> } // こんなデータが let json = """ { "age": "10" } """ // デコードできちゃいます! let hito = try! JSONDecoder().decode(Hito.self, from: json.data(using: .utf8)!) print(hito.age.value) // 10
上記のコードだとLooseCodable
という型が表面に出てしまい、結果の値を得るためにvalue
というプロパティを呼ぶのは、呼び出し側に多くの変更の負担が寄る可能性がありいただけない。
そもそもLooseCodable
はCodableオブジェクトの外に見えている必要は無い。
このため定義するとき、LooseCodable
を指定する型はプライベートにしておき、公開するプロパティをComputedにしたほうが良いだろう。
struct Hito { // LooseCodableなプロパティは非公開にし、 private let _age: LooseCodable<Int> // 利用される値をComputedな公開プロパティにする var age: Int { return self._age.value } // JSONとプロパティのキーマッピングを忘れずに enum CodingKeys: String, CodingKey { case _age = "age" } }
今回、初めてSwiftPackageManagerを利用してライブラリを公開してみたが、何もしなくてもCarthageでのインストールにも対応できていて中々に便利な代物だなと思えた。
ファイル構造を強制するなどとっつき憎い点もあったが、今後のSwiftにおけるライブラリ開発はSwiftPackageManagerで作成することを念頭に置いておきたいところだ。
公式Perlチュートリアル
先日のブログに書いたとおりPerl公式のチュートリアルを学んでいる。
最近WEBフロントエンドのライブラリのチュートリアルをこなすことが多いだが、 これらのチュートリアルは懇切丁寧に記されておりPerlのチュートリアルに対しても同じようなものを想定していた。
しかし、内容は最低限Perlの基礎とかコンピュータ・サイエンスの基礎などの知識が必要とされる内容だった。
先にPerlの基礎を学んでいたおかげで特に問題無く読み進めらることができたが、
このチュートリアルは私のチュートリアルに対する概念は大きく崩すことになる内容だ。
さて、このチュートリアルだがちょっとしたジョークを交えた文章で書かれており読んでいると以外に楽しめる内容になっている。 内容もPerlの細かな挙動について触れられており、こういったところに新たな発見・気付きを得られるのは学ぶ上での喜びだろう。
ここから下はチュートリアルで学んだことの覚書を記していく*1
変数の定義
変数の先頭にプレフィクスをつけることで、データのタイプを分類出来る
# スカラーは`$` $shin_gi = true; $number = 42; $string = "moji moji"; # 配列は`@` @array = ( 1, 2, 3, 4, 5 ); # ハッシュは`%` %hash = ( "ha" => "shun!!" ); %hash = ( "ali", "as" ); # こう書いてもいいが、分かり難いので多分書かない
尚、配列やハッシュに格納出来るのはスカラー値だけなのでネストするデータ構造などが扱えない。
これを解決するために参照というスカラー値を作って格納するらしい。
# 参照を作るには、配列やハッシュの先頭に`\`をつける $aref = \@array; $href = \%hash; # 無名配列・無名ハッシュへの参照を持てる # オリジンを壊されないという意味でこっちのほうが使いやすそうだが… # (メモリ使用量などが問題になるか?) $aref = [ 1, 2, 3, 4, 5 ]; $href = { "no" => "name" }; # 参照へアクセスしたいときは`{}`でくくることでアクセスできるようになる @{$aref} # => @array ${$aref}[0] # => $array[0] %{$href} # => %hash ${$href}{"no"} # => $hash{"no"} # 参照先の要素アクセスにはエイリアスが存在する(こちらを使ったほうが読みやすくて良い) $aref->[0] # ${$aref}[0] $href->{"no"} # ${$href}{"no"}
正規表現
Perlは文字列操作が凄いというだけあってかなりボリュームが高い内容となっていた。
なので基本的な部分にのみ目を通して読み飛ばした。
以下は基本的な部分の覚書である。
# `=~`演算子でマッチの真偽をチェックできる if ($value =~ /kihonn/) { print "matched!"; } # `!~`演算子でアンマッチの真偽をチェックできる if ($value !~ /unmatch/) { print "unmatched..."; } # 正規表現のデリミタは`m`オプションを指定することで変更ができる "Henkou dekiru!" =~ m{dekiru!}; # グループ化して取り出す if ("grouping!!" =~ /(ping).*(!!)$/) { print $1; # "ping" print $2; # "!!" } # マッチした値はリストで返るのでこういうふうに書ける ($ping, $bikkuri) = ("grouping!!" =~ /(ping).*(!!)$/);
スレッド
Perlではスレッドを利用することができるらしい。
Perlのスレッドはスレッドセーフらしいので、スレッドを生成した時点でデータがスレッド内にコピーされる。
(配列やハッシュの参照を渡してみたところ、参照すらも(参照先にある実態も)コピーされました)
※スレッドセーフなのは、スレッド生成時にすべてのデータをコピーするから。
データ以外(chdir
などでプロセスレベルで影響を与えるもの)はすべてのスレッドに影響を及ぼしてしまう。
use threads; # サブルーチンの参照を渡すと、その処理をスレッド内で行ってくれる。 my $thread = threads->create(sub { print "Hello world!!"; }) # `join`メソッドを呼び出すと、スレッドの完了を待機、戻り値がある場合は取得、スレッドの後始末を行ってくれる。 # スレッドの後始末をしないコードはエラーになる。 $thread->join(); # `detach`メソッドはスレッドの後始末を行うだけ。 # スレッドが実行中であっても処理結果が出力される前に`detach`を呼び出せばその結果を得ることは出来ない。 $thread->detach();
上記はスレッドの基本的な利用方法でデータのシェア、キューの存在やセマフォ等も存在しますが、
これらが必要になるケースは稀だろうということで軽く読み進めるだけに留めておいた。*2
オブジェクト
パールのクラスはパッケージ。
package Human;
use Human; # パッケージ名からメソッドを呼び出せる Human->Something;
オブジェクトのコンストラクタは、bless
を内部で呼び出すサブルーチン。
コンストラクタの名前は何でもよい。
※bless
は参照をオブジェクトとみなすために利用するらしい(よくわかっていない)
# 内部でblessを呼び出せばそれがコンストラクタ sub new { bless {} } # newでなくても良い sub init { bless {} }
オブジェクトの継承には@ISA
構文を利用する。
暗黙的にすべてのオブジェクトはUNIVERSAL
クラスを継承する。
オブジェクトからメソッドを呼び出すとき、オブジェクトにメソッドがなければ、@ISA
で定義したオブジェクトの左からメソッドを探していき、
どこにも無い場合にAUTOLOAD
メソッドを呼び出す。
{ package Animal; sub cry {} } { package Dog; # Animalを継承 @Dog::ISA = qw(Animal); } # Dogパッケージにはcryメソッドが無いが、Animalを継承しているので呼び出せる。 Dog->cry;
オブジェクトに関する内容がかなり濃密なので、詳細はまた別途学習することにする。
今更Perl
今更Perlなど学んでどうする?と多くの人が思われるかもしれないが、未だにPerlのお仕事がこの世には存在しているのです。
プログラマ経験も10年近くなると新しいプログラミング言語を学ぶのに殆ど時間など必要ありませんのでサクッと学んでやろうという意気込みです。*1
(シンタックスレベルで学び、適当なライブラリやフレームワークを使ってなにかを作る程度という前提です、言語のコア実装に手を出すという話ではありません。)
さて、学ぶにせよ何の教材が良いのかさっぱり分からりませんでした。 とりあえずWEBで検索してみて日本語で情報がまとまっていそうなこちらを参考にREPLでPerlに触れてみました。
http://tutorial.jp/prog/perl/perlman.pdf
こちらのPDFでは本当に基礎の部分のみに焦点が当てられており、同時にソースコードリーディングしていたtokuhirom/AmonというWebアプリケーションのためのFrameworkを読み解くほどの情報は得られませんでした。
(クラス?関連、パッケージ・外部モジュールの利用方法についての情報は一切書かれていませんし、細々として言語機能もなし)
次に学ぶべきはやはり公式サイトからでしょうか?*2
チュートリアルの内容がしっかりしていて流石公式!といった佇まいですね。
この他にも開発におけるTipsなども満載で、ここに目を通すだけでPerlのお仕事は問題なくこなせそうな充実ぶりです。
これは読み進めるのが楽しみです。