書いて覚えるSwift入門

第41回 Swiftyなデータ処理

この記事を読むのに必要な時間:およそ 3 分

swift-sion

前回予告どおり,今回はswift-sionを題材にSwiftyなデータ処理とは何かを考察していきます。

to type or not to type, that's the question.

前回記事より,SIONの実装はさらに増えて合計5種5言語になりました。

ここで言う「実装」とは,SIONをシリアライズ(serialize)⁠デシリアライズ(deserialize)できるという意味ですが,どのようにそうしているのかというのは,それぞれの言語ごとにかなり異なります。一番異なるのは,言語に組込みの型をそのまま使うか,そのための型を別途用意するかでしょう。js-sionは前者,swift-sionは後者です。双方とも筆者が作者なのですが,おかげで責任を持ってなぜそうしたのかを説明できます。

何のためにシリアライズするのか

たとえばSIONのDictionary。js-sionでは,String:SIONの場合はObjectそれ以外の場合はMapにデシリアライズされます。どちらもECMAScript 6には標準装備されている型です。それに対し,swift-sionではこの場合も含め,SIONで表現されたバイト列はすべてSIONという,標準装備されていない専用の型にデシリアライズされます。

一見,データ交換用のシリアライゼーションのために専用の型を用意するというのはいささか本末転倒の感じもします。なんのためにデシリアライズするかといえば,その言語の中でそのデータを使うためであり,だとしたらその言語に自然に備わった型にそのままデシリアライズするのが最も使いやすいはずです。事実,JSONはECMAScriptのリテラルそのまま(literally literal!)であり,黎明期においてデシリアライズはeval()でなされました。今ではJSON.parse()という専用関数が組み込まれましたが,その結果はArrayなりObjectなり,ECMAScriptの標準型であることに変わりありません。

しかし,言語をまたがってデータ交換がなされるようになると,それ以外の言語で型の違いが問題になってきます。たとえばCやPerlにはBoolに相当する型が不在で,数値が0ならfalseそれ以外はtrueという扱いです。これがPerlになると0 but trueなどという場合が出てきてさらにややこしい。こうした問題をどう解決するか。

必要は発明の母

「なければ作ればいい」⁠これは型にも当てはまります。Perlに標準装備のJSON::PPでは,JSON::PP::Booleanという型―Perlにおいてはクラス―を用意してこの問題を解決しています。ただし用意されているのはあくまでもJSONを表現するのに足りないデータ型だけで,すべてのJSONを過不足なく受け入れるJSON型というのは用意されていません。これはある意味当然で,Perlでは,

use JSON::PP;
my $array_ref = json_decode('[0,1]');
my $hash_ref  = json_decode('{"one":1}')

$array_refは普通の配列リファレンス,$hash_refは普通のハッシュ(辞書)リファレンスであるのがデータ交換フォーマットとして期待される振る舞いで,オレクラスを押し付けられてもユーザは困ってしまいます。JSON::PP::Booleanはある意味必要悪であり,実際数値や文字列としても評価可能で,"" . decode_json("true")"1"となります。

シリアライズする2つの理由

それでは,なぜswift-sionではSION表現が過不足なくデシリアライズできるSION型を用意したのでしょう。JSON同様,ある言語のリテラルをほぼそのまま転用したのであればなおのこと。

理由は2つ。1つはSwiftにeval()が存在しないこと。同様のことは不可能ではありませんが,そのためにはswiftcが必要になります。これはSwiftが根底で「スクリプト言語」ではない傍証ともみなせます。⁠スクリプト言語」というのはあいまいな言葉ですが,利用にあたってランタイム(runtime)という名のコンパイラが必ず備わっているという定義を筆者は採用しています。Swiftはスクリプト言語のように使うこともできますが,SwiftでコンパイルされたアプリケーションはSwiftの実行環境がなくても実行できるというのが大前提になります。である以上,SIONをシリアライズ/デシリアライズするのに過不足ない型を用意するのがもっとも自然かつ楽だということになります。

もう1つは,Swiftはユーザ定義の型をあたかも標準装備の型のように扱えるようにするのが容易な言語だから。演算子(operators)に添字(subscript)にコンピューテッド・プロパティ,そしてExpressiveBy*Literalプロトコル……,これらの条件がそろって初めてSION型が成立するのです。

import SION

御託はこれくらいにして,swift-sionを使ってみましょう。GitHubのREADME.mdどおりに環境を整えたら,

import SION

とするだけです。これだけで,

var sion:SION = [
    "nil":      nil,
    "bool":     true,
    "int":      -42,
    "double":   42.195,
    "string":   "漢字,カタカナ,ひらがなの入った
string",
    "array":    [nil, true, 1, 1.0, "one", [1],
["one":1.0]],
    "dictionary":   [
        "nil":nil, "bool":false, "int":0, "double":0.0, "string":"","array":[], "object":[:]
    ],
    "url":"https://github.com/dankogai/"
]

という具合にふつうにSIONをリテラルとして受け付けます。試しに:SIONを取ってみてください。どうなりましたか?

もちろん,文字列をはじめ,ほかのデータソースからの初期化,つまりデシリアライゼーションもサポートしています。

SION(string:sionStr)
SION(json:jsonStr)
SION(jsonUrlString:"https://api.github.com")
SION(propertyList:plistXML.data(using:.utf8)!, format:.xml)
SION(msgPack:msgData)

デシリアライゼーションはさらに簡単で,SION型の文字列表現そのものがSIONフォーマットです。sion.descriptionでも"\(sion)"でもいいですし,そのままprint(sion)してもいいでしょう。

直感的なのは,シリアライズ/デシリアライズだけではありません。データへのアクセスもそうなっています。たとえば空のSION配列は,

var sa = SION([])

で,

sa[0] = nil
sa[1] = true
sa[2] = 1

という具合にあたかも組込みの配列のように値を代入できますし,辞書も同様にSwiftで,

var sd = SION([:])
sd["nil"]  = nil
sd["bool"] = false
sd["int"]  = 0

とできます。1点「Swiftらしくない」のは,添字が不在の場合。Swiftの標準のArrayでは範囲外へのアクセスはfatalErrorになりますし,Dictionaryで存在しないキーはnilを返しますが,swift-sionでは.Error()という特別な値を用意し,以降のアクセスはそれが伝播(propagate)するような設計にしてあります。この点はSwiftyJSONswift-sionの元ともなったswift-jsonに合わせました。

もう1点注意を要するのは,添字でアクセスされた要素もまたSION型である,というよりSION型に包まれていること。上記の例では,

sa[1] = true // SION.Bool(true)
sa[2] = 1    // SION.Int(1)

であり,アンラップ(unwrap)するには,

sa[1].bool
sa[2].int

とします。なおこれらのコンピューテッド・プロパティはgetterであるだけではなくsetterでもあるので,Swiftでは次のような操作も受け付けます。

sion[2].int! += 1              // now 2
sion[3].double! *= 0.5         // now 0.5
sion[4].string!.removeLast()   // now "on"
sion[5].array!.append(2)       // now [1, 2]
sion[6].dictionary!["two"] = 2 // now
["one":1,"two":2]

次回予告

swift-sionやSwiftyJSONのような「組込み型のようなユーザ定義型」が簡潔に書けるのは,筆者にとってSwiftの最大の美点なのですが,ここまで書いたところで誌面が尽きたようです。次回は具体的にどのようにすればそうできるのかをみていくことにします。

Software Design

本誌最新号をチェック!
Software Design 2018年11月号

2018年10月18日発売
B5判/184ページ
定価(本体1,220円+税)

  • 第1特集
    その知識古くなってない? 新サービスを適材適所で使えてる?
    あなたの知識をアップデート AWS再入門
  • 第2特集
    よい機器はエンジニアを鍛える
    ヤマハルーターをお勧めする理由
  • 一般記事
    [特別企画]量子コンピュータ〈超〉入門
    【後編】量子プログラミングと量子チップ開発の実際

著者プロフィール

小飼弾(こがいだん)

1969年生まれ,東京都出身。元ライブドア取締役の肩書きよりも,最近はPokemon GOのガチトレーナーのほうが有名になりつつある……かもしれない永遠のエンジニアオヤジ。

活躍の場はIT業界だけでなく,サブカルからアカデミックまで多方面にわたり,ネットからの情報発信は気の向くまま毎日毎秒! https://twitter.com/dankogai,ニコニコチャンネルは,http://ch.nicovideo.jp/dankogai,blogはhttp://blog.livedoor.jp/dankogai/

当社刊行書籍は『小飼弾のアルファギークに逢ってきた』『小飼弾のコードなエッセイ』など。他にも著書多数。

コメント

コメントの記入