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
foldl
やfoldr
と違って、初期値は不要。(foldl1
やfoldr1
と同じ)
fmap
すべての行を関数によって変換する。
fmap :: (a -> b) -> Shell a -> Shell b ls "." & fmap (format fp) & stdout
grep
Pattern
とShell ...
をとり、grepされたShell ...
を作る。
grep :: Pattern a -> Shell Text -> Shell Text select ["Haskell", "Turtle", "Shell"] & grep (plus dot <> "ll") & stdout
has
、prefix
、suffix
が便利。
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
もうちょっと関数の分離を意識すると良いコードが書けるかも。
Haskell :: CSVフォーマッターを書いてみた
勉強がてら試しに書いてみた。
なんというか・・・以外と難しいね!!
ちなみに全角文字に非対応なのでそのうち。
関数プログラミング実践入門 メモ - 第4章 評価戦略
関数プログラミング実践入門の4章「評価戦略」を読んだので簡単にメモ。
遅延評価(lazy evaluation)
- 実際に使うまで計算しないという計算順序の規則
- たらい回し関数(竹内関数)は、積極評価だと実行に時間がかかり、遅延評価だとすぐ完了する関数(の例)
- メジャーなプログラミング言語の殆どは「積極評価」。正確評価(strict evaluation)や先行評価、厳密評価とも。
- 遅延評価では「無限」を定義できる(実際に使われるまでは評価されないので)
- 人間にとって自然な(無限な)「数列」の定義と、その数列から値を取り出す、というのを分離できる。
評価戦略(evaluation strategy)
- 評価(evaluation)を行うときの計算順の決め方
- ラムダ計算では「簡約(reduction)」という変換操作を行うことで評価を進める。
(\x -> 変数xを含むかもしれない式A) 式B
を式A中の変数xをすべて式Bに置き換えた式
にするような変換規則。
- 式中のどの部分も簡約できない式を「正規形(normal form)」と呼ぶ。
- ラムダ計算が基礎となっている関数型言語において、評価戦略とは「簡約を行う順序の決め方」といえる。
積極評価(eagar evaluation)
最左最外簡約(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はランタイム上の処理コストは殆どかからない。
- これは単純なreturn処理としてコンパイラが扱うため。
- 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のセレクタや動的ランタイムが使用できる。
これらの類似は、他の言語のイディオムを採用できることを意味する。
- 適例として、Objective-Cのプロジェクトの機能の殆どはSwiftに移植できる。
- JavaやC#のデザインパターンをSwiftで実装する本はすぐに出版された。
- ニューウェーブとして、Monadのチュートリアル的なブログ記事が生まれた。
しかし、(これらの類似は同様に)フラストレーションももたらした。
- なぜ、Protocol Extensions の関連型は、Javaのインターフェースのように使えないのか。
- なぜ、配列は私達の期待通りに共有されないのか。
- なぜ、Fanctor(ファンクター)が書けないのか。
- 時々ある答えとして、Swiftではまだ実装されていないというものがある。
- (ここから先は自信が無いので後で)
Swiftは多くのプログラミング言語の複合体になっている。
- しかし、上手に複雑さを隠している。
- あなたがSwiftでアプリを開発し始める時に、ジェネリクスやオーバーロード、静的ディスパッチと動的ディスパッチの違いを理解している必要はない。
- たぶんあなたは、Cで書かれたライブラリを呼び出したり、自前のコレクション型を書くことは無いだろう。
- しかし、しばらくして、最終的には以下の様なことを知る必要がでてくる。
- コードのパフォーマンスを改善したり、
- よりエレガントに表現したり、
- 特定の事を実現するために
こうやって日本語に落としてみると、翻訳という作業がいかに大変なのかに気づく。
つづく?