My Favorite Things - Coding or die.

とある技術者の経験記録、的な。

iOSオールスターズ2に参加してきた感想

帰ってブログ書くまでがイベントって言われたので書くよ! eventdots.jp

ブログなので当然ですけれど、私が思ったことをそのまま書いてます。 (とりあえずファーストインプレッションをダンプしてきます)

あ、登壇者の皆様、本当に貴重な情報をありがとうございました!

RxSwift in Practice

  • 状態をストリームとして表現するのは良いと思った。
  • しかし、やっぱ殆どのケースでは大げさ=オーバースペック感は否めない。
  • インジケータ非表示にするの忘れる、くらいであればテストで十分感あり(発表でもあったけど)。
  • やはり状態がたくさんある状態で、最新のViewに更新される必要がある、みたいなケースが活きるのではないか?
  • コードレビューをランダムに回すのは良いアイディアだと思った。
  • 学習コストが高い、っていうのは素直に認めなきゃと思う。

VC「もしかして...」Model「私たち...」「「入れ替わってるー!?」」を前前前世から防ぐ方法

  • ギャグ満載。
  • Fat VC=「肥満体質な」VCって表現は分かりやすかった。(コードの臭い匂い的な意味とも通じる)
  • クリーンアーキテクチャは大げさ過ぎる気がしたけど、ViewController/Presenterまわりの実装の仕方はありだと思った。
  • 「君の名は」を見ておくべきだった?

Type-safe URL Routing in Swift

  • サーバサイドの話かと思ったけれど、URLスキーム起動とかでiOSも該当する。なるほど。
  • ApplicativeFunctorでパース処理を書けるのは良いと思った。(JSONパーサとかでも活用されてるけど)
  • 途中から全く分からん状態!
  • 関数型もっと勉強しよう・・・

Using PDF in iOS

  • 濃い・・・。
  • でも実際にPDFを扱う必要が出た場合に、間違いなく参考になる貴重な情報。

Xcode8で開発はどうかわったのか

  • 変わってない。
  • Visual DebuggingでAutoLayoutの制約が確認できるようになったのは良いと思った。
  • FPS Performance Gauge、いずれ使ってみたい。
  • プロファイラも進化したなぁ、と思う。

これから始めるProtocol Buffers導入

  • Googleが考えた、シリアライズフォーマット。
  • バイナリで高速(サイズがJSONの半分以下に)
  • .protoファイルからSwiftコードを生成できる
  • ありだとは思うけれど、本当にパフォーマンスが重要な場面以外では導入は控えて、JSONにすべきだと思った

DIを前提にしたiOSアプリの設計

  • DIコンテナを使うべきか、というとそこまでは判断できないけど、DI設計はありだと思った。
  • 依存性注入を意識したコードスタイルになっていれば、DIコンテナを導入しなくても、テストなどは相当楽になるはず。
  • Storyboardからインスタンス生成されるとプロパティインジェクションになっちゃうのは確かに。(個人的にもコンストラクタインジェクション派)

Swiftらしい表現を目指そう

  • すごく分かりやすかった。
  • イニシャライザは文章っぽく書かなくてもいいってのは知らなかった。
  • プロトコルHaskellで言えば型クラス)を意識したコードを書くのは、もうちょっと日頃意識したいと思った。

おわり

  • 楽しかった!
  • 途中でピザの匂いで食欲そそられた。
  • イベント会場キレイだった。
  • 関数型はもっと勉強していくよ!

Qiita記事「Haskell チュートリアル (Haskell Day 2016)」を読んでメモ

以下を読んでメモ。

Haskell チュートリアル (Haskell Day 2016) から学んだこと http://qiita.com/hiratara/items/169b5cb83b0adbfda764

Shell以降は新しいことばっかりだったのと、元記事の完成度が高すぎて途中からは殆ど写経みたいになってしまった。

基本

暗黙的に副作用を起こす式がなく、明示的に副作用を起こす式(IO型)がある。
言い換えると、IO型が登場しない関数は副作用がないことを保証できる。

Hindley/Milner型推論アルゴリズムによる推論で、型は1つも書かなくても大丈夫。(readなどは例外)

Shellプログラミング

副作用の塊なので相性が悪いと思われるが(私はそう思ってた)、そうでもない。

1行目にシェバング、2行目にstack runghcコマンド、を書いておくことで直接実行できる。

#!/usr/bin/env stack
-- stack --resolver lts-6.15 --install-ghc runghc --package turtle
...

REPLでは:lでソースを読み込んで、:mainで実行できる。
その後、:r:main とすると効率的に開発できる。

turtle

Haskellでshell相当の関数が実装されたもの。

以下の2行のおまじないで使える。

#!/usr/bin/env stack
-- stack --resolver lts-7.0 --install-ghc runghc --package turtle

{-# LANGUAGE OverloadedStrings #-}
import Turtle

REPLで試す場合、Stringのオーバーロード設定を忘れずに。

Prelude> :set -XOverloadedStrings

MonadoIO

MonadoIOは型クラスで、IOはそのインスタンス

MonadoIO io => Text -> io ()Text -> IO () と読みかえて問題ない。

<-=の違い

=Haskellの言語仕様で、let式、where句で使える。

<-バインドの糖衣構文で、do専用。

main = do
    let title = "now: "
    now <- date
    putStrLn (title <> show now)

<>は文字列(StringまたはText)の連結。(Monoid)

main関数はmain :: IO ()という型を持つ。

printfとformat

Turtle.Formatモジュールに定義されている。

*Main> printf ("My name is "%s%". "%d%" years old.\n") "shu1" 0
My name is shu1. 0 years old.

Turtleの関数

-- 引数取得
arguments :: MonadIO io => io [Text]

-- ファイルパスに変換
fromText :: Text -> Turtle.FilePath

-- 最終更新日付を取得
datefile :: MonadIO io => Turtle.FilePath -> io UTCTime

-- Textへ変換
repr :: Show a => a -> Text

mapM IOアクション リストという形式で繰り返し処理が出来る。 (「通常の引数をとってモナドに包まれた値を返す」関数をリストにmapするときに使うものっぽい)

mapM print [1,2,3]
mapM echoModified args

IOアクションはモナドから値を取り出した時に、はじめて実行される。

nestedIO = do
    putStr "Hello, "
    return (putStrLn "I/O!")

main = do
    r1 <- nestedIO
    r2 <- r1 -- I/O! はここでの評価によって出力される

ストリーム処理

UNIXのパイプの実現に、IO ...では役者不足。
Shell ...というTurtleが提供する型を利用するのが良い。

IO ...:すべての結果を一度に返す `Shell ...``:複数行の結果を1行ずつ返す

入力の関数

入力はShell ...という型で表現される。

empty :: Shell a
stdin :: Shell Text
input :: FilePath -> Shell Text
select :: [a] -> Shell a
"INPUT" :: Shell Text

出力の関数

出力はShell ... -> IO ...という型で表現される。
最終的にIO ...になるので、doブロックで書ける。

sh :: Shell a -> IO ()   -- 出力を捨てる
view :: Shell a -> IO () -- 出力を表示
stdout, stderr :: Shell Text -> IO
output :: FilePath -> Shell Text -> IO ()  -- ファイルに出力
shell :: Text -> Shell Text -> IO ExitCode -- 外部コマンドに流し込む

パイプの関数

パイプ(||の間)はShell ... -> Shell ...という型で表現される。

id :: Shell a -> Shell a
limit :: Int -> Shell a -> Shell a
inshell :: Text -> Shell Text -> Shell Text -- 外部コマンドを通す

組み合わせ

-- 通常の関数適用
stdout (limit 10 (input "sample.txt"))

-- $によるカッコの省略
stdout $ limit 10 $ input "sample.txt"

-- 関数合成
(stdout . limit 10 . input) "sample.txt"

-- `&`による関数適用(UNIXライク)
input "sample.txt" & limit 10 & stdout

fold

Control.Foldlに定義されたFoldを使って、Stream ...を回収できる。

import qualified Control.Foldl as Fold

fold :: Shell a -> Fold a r -> IO r
foldIO :: Shell a -> FoldM IO a r -> IO r

foldlfoldrと違って、初期値は不要。(foldl1foldr1と同じ)

fmap

すべての行を関数によって変換する。

fmap :: (a -> b) -> Shell a -> Shell b

ls "." & fmap (format fp) & stdout

grep

PatternShell ...をとり、grepされたShell ...を作る。

grep :: Pattern a -> Shell Text -> Shell Text
select ["Haskell", "Turtle", "Shell"] & grep (plus dot <> "ll") & stdout

hasprefixsuffixが便利。

select ["Haskell", "Turtle", "Shell"] & grep (suffix "ll") & stdout

パターンについてはドキュメントを参照。

do記法

IO ...と同様に、Shell ...do記法が使える。(モナドだから)

doブロックの戻り値はShell ...となる。

戻り値はループ処理となる。

lsPrintf = do -- Shell ... の do ブロック
    file <- ls "."
    -- 全ファイル分、ループ処理される
    printf (fp%"\n") file

main = do     -- IO    ... の do ブロック
    lsPrintf & sh

Applicative

[...]Maybe ...IO ...Shell ...のようなコンテナ型を指す。

通常の関数を使った演算が可能なコンテナのこと。

(<*>) :: f (a -> b) -> f a -> f b
(<$>) :: (a -> b) -> f a -> f b

(+) <$> [1, 2] <*> [3, 4] -- => [4,5,5,6]

fがn引数の関数のとき、f <$> x1 <*> x2 <*> x3 <*> x4 <*> ... <*> xn

(,)(タプル)やコンストラクタに適用すると、一度に複数の方法で畳み込める。

Main Fold Turtle> select [1..6] & (`fold` ((,) <$> Fold.minimum <*> Fold.maximum)) & view
(Just 1,Just 6)

パーサ

Applicativeはパーサで使われることが多い。

パーサの本質は「文字列の消費」と「結果の生成」からなる。

「結果の生成」をApplicativeで演算。

match :: Pattern a -> Text -> [a]

*Main Fold Turtle> match ((,) <$> "a" <> star dot <*> "d" <> star dot) "abcdefg"
[("abc","defg")]

戻り値の差し替え

(*>) :: f a -> f b -> f b -- 右のパーサの結果のみを使う
(<*) :: f a -> f b -> f a -- 左のパーサの結果のみを使う
sed :: Pattern Text -> Shell Text -> Shell Text
pure -- パースしない(結果のみを返す)

-- 文字列が消費されていって、左か右のどちらの結果を扱うか決めている
*Main Fold Turtle> "abcdefg" & sed ("abc" *> pure "xyz") & stdout
xyzdefg
*Main Fold Turtle> "abcdefg" & sed ("abc" <* "def") & stdout
abcg

コマンドライン引数のパーサ

Turtle.Optionsコマンドライン引数をパースできる。

parser :: Parser (Maybe Text, Bool)
parser = (,) <$> optional (optText "dir" 'd' "Target directory")
             <*> switch  "show" 's' "Show module names."

(mDir, isShow) <- options "Count import." parser

演習問題の答え合わせ

letは一つの宣言で複数書ける。

let fileTxt = head args
    file = fromText fileTxt

Haskellでは短い変数名をつけることが多い?(Maybe型の頭にmをつけるのは分かりやすいと思った)

dt <- datefile file

最後がIOアクションで終わる場合はreturnは不要。(でも書いておけば、とりあえずコンパイラは黙らせられる)

<-バインドしなくても、最終行に記述すればIOアクションは処理される。

nestedIO = do
    putStr "Hello, "
    return (putStrLn "I/O!")

-- 解答
main = do
    printIO <- nestedIO
    printIO

-- 私の答え
main = do
    r1 <- nestedIO
    r2 <- r1
    return ()

Turtleの(疑似)パイプ処理は、Shellコマンド実行にしても、関数にしても、&で繋げられる。

find (suffix ".hs") path
    & grepImport
    & (`fold` Fold.length)
    & view

もうちょっと関数の分離を意識すると良いコードが書けるかも。

AtCoderに挑戦 - ACR001、ACR002 :: Haskell

関数プログラミング実践入門の巻末に載っていたAtCoderHaskellで挑戦してみた。

001は、sortとgroupを活用すればすぐ出来ることが分かった。

002は、すごいH本に載っていたチェス盤のナイトのコードを参考にして、リストモナドを使って解いてみた。

実際にコードを書いてみると、以外とスラスラ書けないという思う反面、Haskellは強力だとあらためて感じた。

関数プログラミング実践入門 メモ - 第4章 評価戦略

関数プログラミング実践入門の4章「評価戦略」を読んだので簡単にメモ。

遅延評価(lazy evaluation)

  • 実際に使うまで計算しないという計算順序の規則
  • たらい回し関数(竹内関数)は、積極評価だと実行に時間がかかり、遅延評価だとすぐ完了する関数(の例)
  • メジャーなプログラミング言語の殆どは「積極評価」。正確評価(strict evaluation)や先行評価、厳密評価とも。
  • 遅延評価では「無限」を定義できる(実際に使われるまでは評価されないので)
  • 人間にとって自然な(無限な)「数列」の定義と、その数列から値を取り出す、というのを分離できる

評価戦略(evaluation strategy)

  • 評価(evaluation)を行うときの計算順の決め方
  • ラムダ計算では「簡約(reduction)」という変換操作を行うことで評価を進める。
    • (\x -> 変数xを含むかもしれない式A) 式B
    • 式A中の変数xをすべて式Bに置き換えた式にするような変換規則。
  • 式中のどの部分も簡約できない式を「正規形(normal form)」と呼ぶ。
  • ラムダ計算が基礎となっている関数型言語において、評価戦略とは「簡約を行う順序の決め方」といえる。

積極評価(eagar evaluation)

  • 関数型言語でも積極評価を採用している言語は多い。
    • Haskell使いでも”Haskellが積極評価だったら良かったのに”という意見を述べる人も。

最左最外簡約(leftmost-outermost reduction)

  • 外側にあるものから
  • 左側にあるものから
    • を、優先的に簡約していく順序。
  • 評価戦略としては「遅延評価」になる
  • 最左最外簡約で停止しないラムダ式であれば、他のどのような簡約順を選んでも停止しない。(正規順序)

弱冠頭正規形(WHNF) - weak head normal form

  • これ以上適用する値がない関数
  • 式の先頭にコンストラクタが出た状態の値
  • というところまで評価する、Haskellでの評価戦略。
  • isJust (Just (1 + 2))という式では、(1 + 2)の計算は実行されずにTrueが得られる。(中身の値は関係ないので)

サンク(thunk)

  • 「評価が行われないまま放置されている計算予定」オブジェクト
  • 他の言語では「ラムダ式(関数)」を利用することで、同じようなことを実現できる。(関数を呼び出した時に、はじめて計算)
  • サンクに予定されていた計算を発生させて値を得ることを「サンクを潰す」と表現する。
  • Haskellでは「グラフ簡約(graph reduction)という仕組みで、同じ式が複数回登場する場合に最初の1回だけ評価される。

積極評価 vs 遅延評価

  • 「積極評価」の方が、現在の計算機アーキテクチャと相性が良い。
  • 「遅延評価」では、
    • 必要のない計算を本当に行わないことで計算量を低減できる。(が、実際には稀)
    • 「計算の定義」と「その実行」を区別できるため、モジュラリティが高い。(数列定義と取り出し操作など)

評価の制御

  • seq(seq :: a -> b -> b)という関数を利用することで、サンクを潰せる。
  • 1つ目の値を「WHNFまで評価してから」2つめの値になる関数
  • let xs = map (+1) [0, 1, 2] in xs seq xs ++ xs では、++する段階でxsが「WHNF」まで簡約されている

パフォーマンスチューニング

他の言語とチューニング方法は一緒。

  • パフォーマンスを計測する
  • 問題箇所を潰す
  • 積極評価の言語でも、ラムダ式などを利用することで「関数呼び出しを行うことで評価される」仕組みを作れる。

Advanced Swift メモ - 8. Error Handling

8. Error Handling

  • Swiftにはいくつものエラーハンドリングの仕組みがある。
    • Optional、はシンプルだがエラー情報は返せない。
    • Assertion、はバグを早期発見するために利用できる。
    • 例外、はOptionalと違い詳細なエラー情報を持てる。
  • CollectionTypeのfirst/lastなどはOptionalだが、失敗の理由が「配列が空」しかないので適切。
  • ネットワークエラーなどは、失敗の理由を知りたいこともあるはずなのでErrorTypeが良い。

The Result Type - P.248

enum Result<A> {
    case Failure(ErrorType)
    case Success(A)
}
  • Swiftのエラーハンドリングは、殆どResultTypeと同じように実装されている。
  • throwsで宣言された関数を呼び出した時は、キャッチするか伝播させる必要がある。
  • try-catchは他の言語と似ているが、Swiftはランタイム上の処理コストは殆どかからない。
  • ResultTypeとの大きな違いは、エラーが型として定義されていないこと。
    • 厳格な型情報が欲しければResultTypeを使うと良い。
    • でも標準のtry-catchを使ったほうがシンプルではあるかも。

Errors and Objective-C Interface

  • NSErrorポインタを受けるようなObjective-Cの関数はthrowsに変換される。
    • エラーポインタの引数は除外され
    • 戻り値で成否を返すようなものはVoidになる。
- (NSString *)contentsOfFile(NSString*)fileName error:(NSError **)error;
func contentsOfFile(fileName: String) throws

Errors and Function Parameters - P.253

  • すべての要素が条件を満たすか判定するメソッドallは以下のように実装できる。
extension SequenceType {
    func all(@noescape check: Generator.Element -> Bool) -> Bool {
        for el in self {
            guard check(el) else { return false }
        }
        return true
    }
}
  • しかし上記のバージョンではthrows宣言された関数を渡すことが出来ない。
func isEven1(x: Int) -> Bool {
    return x % 2 == 0
}
func isEven2(x: Int) throws -> Bool {
    return x % 2 == 0
}

(1..<10).all(isEven1) // OK
(1..<10).all(isEven2) // Compile error
  • そこでmapなどの標準メソッドは以下のようにrethrowsで宣言されている。
extension SequenceType {
    func all(@noescape check: Generator.Element throws -> Bool) rethrows -> Bool {
        for el in self {
            guard try check(el) else { return false }
        }
        return true
    }
}

try! (1..<10).all(isEven2) // OK

Cleaning Up Using defer - P.255

guard let database = openDatabase(...) else { return }
defer { closeDatabase(database) }
guard let connection = openConnection(database) else { return }
defer { closeConnection(connection) }
guard let result = runQuery(connection, ...) else { return }
  • deferは他の言語のfinallyと似ているが少し異なる点もある。
  • try/doブロックに繋げる必要はなく、どこでも書くことが出来る。
  • deferを同一ブロックで複数宣言した場合、逆順に実行される(スタックのように)
  • セグメンテーションフォルトあるいはfatal errorなどの場合は実行されない(プログラムはクラッシュする)

Errors and Optionals - P.256

if let contents = try? parseFile("Hello.md") {
    print(contents)
}
  • エラーがthrowされなかった場合だけ処理したいときはtry?が使える。
  • Optionalがnilだった場合に、代わりの例外をthrowするような関数は以下のように書ける。
func optional<A>(value: A?, onError e: ErrorType) throws -> A {
    guard let x = value else { throw e }
    return x
}
let int = try optional(Int("42"), onError: ReadIntError.CouldNotRead)
  • try?キーワードは、エラーを無視するべきではないというSwiftの哲学とは矛盾しているようにも見える。
  • しかし、try?は明示的に書く必要がある。
  • あなたがエラーの詳細な内容について興味がないときには、これは便利である。

Chaining Errors - P.257

  • 例外がthrowされる可能性のあるメソッド呼び出しがチェーンしても、ifなどのようにネストしたりしない。
  • 例外が発生した場合は、catchにコントロールが移動して処理される。
func checkFilesAndFetchProcessID(filenames: [String]) -> Int {
    do {
        try filenames.all(checkFile)
        let contents = try contentsOfFile("Pidfile")
        return try optional(Int(contents),
            onError: ReadIntError.CouldNotRead)
    } catch {
        return 42
    }
}
  • これをResultで実装することも簡単に出来る。
  • optionalのように成功の場合は中身を取り出して適用し、失敗の場合は何もしない。
  • そうしたコードはとてもエレガントになる。
func checkFilesAndFetchProcessID(filenames: [String]) -> Int {
    return filenames
        .all(checkFile)
        .flatMap { _ in contentsOfFile("Pidfile") }
        .flatMap { contents in
            Int(contents).map(Result.Success) ?? .Failer(ReadIntError.CouldNotRead)
        }
}

Higher-Order Functions and Errors - P.258

  • この本を執筆している段階では、Swiftのエラーはコールバック関数に向いていない。
  • 単純なケースでは、コールバックの引数をoptionalにすることで解決できる。
    • nilが入っていた場合は失敗をみなす、というように。
func compute(callback: Int -> ()) // 結果をIntで返す
func compute(callback: Int? -> ()) // 失敗した場合はnil
  • 詳細なエラー情報を返すために、最初は以下のようにしてthrows宣言しようと思うかもしれない。
func compute(callback: Int throws -> ())
  • しかし、この宣言は完全な誤りで、型ではなく関数に修飾するthrowsの使い方としてはNG。
  • この宣言をResultで書きなおした場合どうなるかやってみると、間違いであることに気づく。
func compute(callback: Int -> Result<()>)
  • 我々が欲しいのは、以下のようにコールバック関数の引数がResultであるものだ。
func compute(callback: Result<Int> -> ())
  • throwsを使った、現在ではあまり分かりやすくない書き方もある。
func compute(f: (() throws -> Int) -> ())
  • これを利用する側はさらに複雑になる。
compute { (theResult: () throws -> Int) in
    do {
        let result = try theResult()
        print(result)
    } catch {
        print("An error happend: \(error)")
    }
}
  • なのでResultは非同期エラーハンドリングの選択としては良い。
  • しかし、同期関数でthrowsをすでに使用している場合、Resultを使用している箇所との食い違いで、APIが使いづらく鳴るかもしれない。
  • あなたが多くの非同期関数を書いている場合、トレードオフとしてResultを使う価値はある。
  • しかし、一つのコールバックしかないようであれば、先ほどのネストした関数を利用するのも良い選択肢だ。

Conclusion - P.260

  • AppleがSwift 2.0にエラーハンドリングを導入した時、多くのイケてないことが発生した。(訳間違ってるかも)
  • Swift 1.xでは、殆どResult型がエラー処理に使われていた。
  • 実際、型付けされていないthrowsは、嬉しいような、そうでもないような気持ちがあった。(意訳)
  • throwsの利点として、型シグネチャの宣言がシンプルであるという点がある。
  • 例えば、複数のエラーが発生する可能性のある関数の場合、以下のようになっていたかもしれない。
func checkFilesAndFetchProcessID(filenames: [String])
    throws ReadFileError, CheckFileError, MiscellaneousError -> Int // この書き方はしんどい
  • しかし、これは大きな欠点も残した。
    • どのエラーが発生したか明示できず、余分なボイラープレートコードを必要とした。
    • さらに、throwsが関数のみに作用するため、不必要な複雑さを生むことになった。(非同期コールバックのように)
  • Swiftが80%に最適化された、実用的な言語であると考えると、この欠点はシンプルな振る舞いから外れる。

    • 組み込みのエラーハンドリングを利用することで、関数を結果に包んだり、不必要な複雑さをもたらす。
    • そしてあなたが思っているように、我々は曖昧なエッジケースについてここでは言及していない。(非同期コールバックのみ)
  • もし特別なエラー情報が欲しい場合、Result型(エラーをジェネリック型で表現したもの)を使用することが出来る。

  • しかし、これは他の複雑さをあなたのコードに持ち込むことにもなる。
  • あなたが作っているものによっては、この複雑さを持ち込む価値はあるかもしれない。

  • このようにコード中の期待しない動作をハンドリングする仕組みは多くある。

    • 継続できないのであればfatalErrorまたはAssertion
    • エラーの種類に興味がないか、1種類に限定できるのであればOptional
    • エラーの種類が必要か、追加情報が欲しい場合、Swift標準の例外か、Result
    • 関数を引数に受け取る関数を宣言するときは、rethrowsを使うことで、throws宣言された関数も受け取れるようになる
    • そしてdeferは標準のエラーを扱うときにとても便利である
    • deferを使ったコードはスコープを抜けるときにクリーンアップとして必ず実行される(throwsあるいはreturnなど)

Advanced Swift メモ - 2. Introduction (1)

Advanced Swift を読んでのメモ。 www.objc.io

英語レベルは低いので間違っている箇所は普通にあると思います。

Swiftはあなたが選択したプログラミング言語と似ているように見える。

  • 低レベルなビット操作(とパフォーマンスチューニングができる点)は、C言語に似ている。
    ただし、たくさんの未定義動作の落とし穴は除外されている。
  • mapやfilterなどの軽量な接尾クロージャは、Rubyプログラマにお馴染みのもの。
  • SwiftのジェネリクスC++のテンプレートに似ている。
    ただし、型制約は使用時ではなく宣言時に正しいことが保証される。
  • 柔軟な高階関数演算子オーバーロードは、HaskellやF#のようなコードを書けることを意味する。
  • @objcキーワードを使用すれば、Objective-Cセレクタや動的ランタイムが使用できる。

これらの類似は、他の言語のイディオムを採用できることを意味する。

しかし、(これらの類似は同様に)フラストレーションももたらした。

  • なぜ、Protocol Extensions の関連型は、Javaのインターフェースのように使えないのか。
  • なぜ、配列は私達の期待通りに共有されないのか。
  • なぜ、Fanctor(ファンクター)が書けないのか。
  • 時々ある答えとして、Swiftではまだ実装されていないというものがある。
  • (ここから先は自信が無いので後で)

Swiftは多くのプログラミング言語の複合体になっている。

  • しかし、上手に複雑さを隠している。
  • あなたがSwiftでアプリを開発し始める時に、ジェネリクスオーバーロード、静的ディスパッチと動的ディスパッチの違いを理解している必要はない。
  • たぶんあなたは、Cで書かれたライブラリを呼び出したり、自前のコレクション型を書くことは無いだろう。
  • しかし、しばらくして、最終的には以下の様なことを知る必要がでてくる。
    • コードのパフォーマンスを改善したり、
    • よりエレガントに表現したり、
    • 特定の事を実現するために

こうやって日本語に落としてみると、翻訳という作業がいかに大変なのかに気づく。

つづく?