My Favorite Things - Coding or die.

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

try! Swift 2017 Tokyo に参加してきた - 1.全体を通して振り返る

はじめに

参加してきました。
https://www.tryswift.co/tokyo/jp

家に帰ってから記事を書くまでがカンファレンスです、という名言を最初に聞いたのはXP祭りだったでしょうか? そんなわけで今回も個人的な感想を書いていきたいと思います。

おそらく長くなるので記事は以下のように分割したいと思います。

  1. 全体を通して振り返る(この記事)
  2. 1日目
  3. 2日目
  4. 3日目(ハッカソン

要約

とてもハイレベルなイベントで、参加してすごく良かったと思えるイベントでした。

正直なところ早割で$250、通常だと$350する参加費は高いと感じました。 (もちろん開催にあたってかなりの経費がかかることはわかりますが)

しかしながら他ではなかなか聞くことが出来ない"リアル"な話が聞けて、 個人的には参加費として$350払う価値は十分にあったかと思います。

ちなみに外国人の登壇者が多かったですが、同じく外国人の参加者も多かったです。 日本で開催されるIT系のイベントでここまで外国人率が多いイベントも少ないように感じます。

開催場所

1日目と2日目が新宿、3日目が神田で開催されました。

1日目と2日目(新宿)

新宿の会場へはJR新宿駅から会場に向かったのですが、いやはや思ったより遠かったです(笑)

会場は広く、前半分くらいはテーブルがあって、後ろ半分はテーブルなしでした。 幸いにもテーブルがある席が確保できてメモが捗りました。

ちなみに会場の電源は使用することが出来ず、Macのバッテリーはギリギリでした。

3日目(神田)

神田駅からわりと近い位置でした。

1日目と2日目が平日、しかも早い時間帯ということもあって、満員電車で揺られた後、そこそこの距離を歩いて会場に到着、という感じだったのでありがたかったです。

同時通訳について

同時通訳があるイベントに参加したのは恐らく初めてだったのですが、 技術ワードについてもきちんと通訳できており、非常にレベルの高い通訳だったと思います。

わたしはかろうじて英語が少し読める程度の力なので、今回のカンファレンスでの同時通訳は無くてはならないものでした。 通訳してくださった皆様方には大変感謝です。

発表について

発表内容はそれぞれ違うのですが、以下のような共通項があったように感じます。

  • 銀の弾丸は無い
  • 可読性の重要さ
  • Protocol Oriented Programming(プロトコル指向を使ったテクニック)
  • 型を意識したコード

銀の弾丸は無い

銀の弾丸は無い」というのは(多くの発表者も言われてましたが)古くからあるもので新しいものではありません。 しかし、これほど何度も繰り返し言われるということは、それだけ人がその存在を信じやすい、ということでしょう。

最近について言えば「リアクティブプログラミング」がその良い例だったように感じます。あの騒ぎは何だったのでしょうか?

もちろん優れた技術ですし、私自身も勉強会を開催するくらいには気に入った技術です。 しかし、それを「銀の弾丸」だと信じて採用した開発チームの末路はどこも一緒なのではないかとも思います。

可読性の重要さ

これも多くの発表に共通しているものだったように感じます。

プログラミング言語というのは半分は技術の進歩で、もう半分は(数学のような)記法の進化である、という言葉はハッカーと画家に書いてありましたが、つまりそういったことではないでしょうか?

可読性、言い換えるとコード上でのコミュニケーションの重要さを軽視すべきではない、ということかと思います。

Protocol Oriented Programming(プロトコル指向を使ったテクニック)

これもSwiftを使った実践的なテクニックの面では多用されていました。 プロトコル指向の原型はHaskellの型クラスにあると思うのですが、やはり関数型のテクニックはSwiftのコードにおいても有用、ということかと思います。

個人的にProtocolを大げさに感じていた面もあった(これはJavaのインターフェースの時代からですが)のですが、これを気に考え直したいと思いました。

型を意識したコード

Interface Builder が必ずしも最適解ではないという理由に、Swiftの良さである型安全が保たれない、という説明がありました。 他にも型を重要と(明示的あるいは暗黙的に)捉えた発表が多くあったように感じます。

発表テーマ

1日目

2日目に比べると「Swiftにかぎらず」「詳細よりもコンセプトを」という発表が多かった気がします。 もちろんそれだけに限っているわけではありませんが。

個人的には「Orta Therox」さんの「独自のツールを構築する」が面白かったです。

2日目

1日目に比べて「具体的なSwiftコード」という側面での発表が多かったです。 まさにSwiftカンファレンスという感じで、個人的には2日目の方が楽しめました。

個人的には「Jon Reid」さんの「モックオブジェクトをより便利にする」が面白かったです。 今までの人生で聞いた発表の中で一番かもしれません。

3日目

わたしは友人と一緒に2人チームでハッカソンに参加しました。

合計48組の中で、審査員の選考を通過した10組が壇上でプレゼンし、うち上位3組が商品をもらえるという形式でした。

残念ながら入賞はできなかったのですが、10組には選出されて壇上でプレゼンすることができました。

swift-heredoc
https://devpost.com/software/swift-heredoc

朝食・昼食・パーティーについて

朝食

タイムテーブルにある”朝食”という文言で、ホテルの朝食のようなビュッフェを想像してしまったのですが、実際には菓子パン1つでした(笑)

これは勝手に期待してしまった私が悪かったのですが。 ただ3日間を通じてコーヒーが自由に飲めたのは大変ありがたかったです。

昼食

これ系のイベントにしてはわりと豪華なお弁当だったように感じます。 ただし人気のあるメニューはわりとすぐになくなったような気がします。

パーティー

IT系のイベントに限らず、これほど豪華なパーティーに参加したのはわたしは初めてでした。 美味しい料理を開発者たちで飲み食いできるのは楽しかったです。

わたしはシャイなので自分からは話しかけられないタイプなのですが、話しかけてくださった方には大変感謝です。ありがとう。

まとめ

この一言で片付いてしまうのですが、本当に参加してよかったと思えるイベントでした。

もしSwiftに興味がある技術者は、多少無理をしてでも来年参加されてみることをおすすめします。

業務が忙しい人も多いと思いますが、何倍ものリターンが得られるかと思いますし、その知見を日々の業務にフィードバックすることで、より生産的な仕事が出来る様になると思います。

Swift3 例外メモ(try、try!、try?、do-catch)

try?の仕組みとか曖昧になってしまったのでメモ。

例外のスロー

  • 例外をスローする関数はthrowsを宣言(書く位置は引数宣言の直後)
  • スローする例外はErrorプロトコルを実装している必要がある
enum MyError: Error {
    case notFound
    case fail(String)
}

func fatalError() throws -> String {
    throw MyError.fail("fatal error.")
}

呼び出し元

すべての例外の補足

  • do { try throws宣言された関数呼び出し } catch { ... }といった構文で例外をキャッチ
  • throws宣言された関数を呼び出すときには、頭にtrytry!try?のいずれかをつける
  • catch { ... }またはcatch let error { ... }ですべての例外をキャッチ(switchdefaultみたいな)
  • enumのようにエラーの種類から網羅性はチェックされないので、網羅するにはすべての例外を補足するcatchが必須
func catchAllErrors() {
    do {
        try fatalError() // `throws`宣言された関数を呼ぶには`try`が必要
    } catch MyError.notFound { // エラーの種類を記述
        // ...
    } catch MyError.fail(let message) { // enumの関連値はパターンマッチで取得できる
        // ...
    } catch let error { // enumと違って網羅するにはこれが必須
        // ...
    }
}

部分的に例外を補足

  • すべての例外を補足しない場合は、関数の宣言にthrowsを記述する必要がある
func catchSomeErrors() throws { // 例外を網羅できていないので`throws`宣言が必須
    do {
        try fatalError()
    } catch MyError.notFound {
        // ...
    }
}

例外を強制的に無視する(try!)

  • try!を使うと例外を無視できる(do-catchが不要)
  • ただし例外が発生したときにはクラッシュする(強制アンラップと同じ挙動)
func forceCall() {
    try! notError() // 例外を無視する(が、発生したときはクラッシュする)
}

例外を安全的に無視する(try?)

  • try?を使うと、try!と同じように例外を安全に無視できる
  • try!と違い、例外が発生した場合でも単純に無視される
  • 呼び出した関数に戻り値がある場合は、戻り値がnilとなる(失敗した、というニュアンスか)
func safeCall() {
    try? fatalError() // 戻り値がない場合はセーフな呼び出し
    let s1 = try? fatalError() // => nil
    let s2 = try? someString() // => some string
}

まとめ

個人的にこんな感じで考えたらよいのかなという指針。

  • 発生した例外に興味がなければ、try?を使って例外を無視する
  • 発生した例外に興味がある場合は、do { try xxx() } catch { ... }を使って補足
  • 入力値が完全に固定など、確実に例外が発生しないと断言できる場合はtry!を例外的に使う(かも)

個人的にはEitherあるいはResultのほうが好きなのだけれど、シンタックスシュガーがよく出来ているので場合によっては例外の使用を検討しても良いのかも。

Vagrantfile: Ubuntu14.04 で Swift製Webフレームワーク「Vapor」を使える状態にする

Swift製のWebフレームワークである「Vapor」をUbuntuに入れてみた。
github.com

せっかくなのでVagrantfileを書いてみた。(たぶんこれが一番楽だと思います)

ちなみにVagrantについてはドットインストールの動画が分かりやすいです。
http://dotinstall.com/lessons/basic_vagrant

環境

手順

作業ディレクトリ作成

$ mkdir ubuntu-server # 適当なディレクトリを作成
$ cd ubuntu-server
$ curl -O https://gist.githubusercontent.com/YusukeHosonuma/54a2f4f294e87cf0dbc87b7420b5ae1c/raw/c181f41b05f2a7dddbe452dd5119b5dcbd857a96/Vagrantfile

VM起動+ssh接続

$ vagrant up
$ vagrant ssh

Vaporプロジェクト作成・ビルド

$ vapor new hello
$ cd hello/
$ vapor build

ビルドがわりと長い・・・依存関係の解決に時間かかってる?

実行

$ vapor run serve
Running hello...
No preparations.
Server 'default' starting at 0.0.0.0:8080

Tomcatと同じ8080ポートで起動されている模様。

動作確認

ホストマシン(Macとか)で以下のURLをブラウザで表示。 http://192.168.33.19:8080

真っ白な画面の中央に以下のロゴが表示されればOK。 f:id:yu_dotnet2004:20170102150015p:plain

簡単でしたね。

Vagrant + CentOS7 で、RustのWebフレームワークRocketを発射する

Linuxとか環境面詳しくないのでメモ。

Rocketは最近出たRustのWebフレームワーク。
https://rocket.rs

環境

VirtualBox: 5.0.30
Vagrant: 1.9.1
CentOS: 7.1
Rust: 1.16.0-nightly
Rocket: 0.1.2

ちなみにこれを書いている時点で最新のCentOSは7.3だったけれど、vagrant upで立ち上げたら途中で止まってしまったので断念。

VagrantでCentOS7を立ち上げる

まずは発射台を準備。

$ mkdir launch-pad
$ cd launch-pad

Boxイメージは、Chef社がOSSとして公開してる「Bento」を使う。
(弁当箱とは誰がうまいことを)
https://atlas.hashicorp.com/bento

$ vagrant init bento/centos-7.1
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.

Vagrantfileは以下のようにしてプライベートネットワークとして接続できるようにしておく。

Vagrant.configure("2") do |config|
  config.vm.box = "bento/centos-7.1"
  config.vm.network "private_network", ip: "192.168.33.11", auto_config: false
end

原因はよく分かっていないのだけれど、auto_config: falseを設定しておかないと、立ち上げたVM上で正しくIPアドレスが振り当てられなかった。

VMイメージを起動。(Boxの取得から始まるので時間かかるよ!)

$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
...

SSHで接続。

$ vagrant ssh
-bash: warning: setlocale: LC_CTYPE: cannot change locale (UTF-8): No such file or directory
[vagrant@localhost ~]$

基本設定

ファイアウォールを切ってしまう。(仮想環境なので)

$ sudo systemctl stop firewalld
$ sudo systemctl disable firewalld

SELinuxが無効か確認。今回は無効になっているのでそのまま。

$ getenforce
Permissive

Rustのインストール

Rocketでは構文拡張を利用するため、NigthlyなRustが必須。(2016/12/30時点)

Rust1.14.0で公式ツールとなったrustupをインストールする。 https://rustup.rs

$ curl https://sh.rustup.rs -sSf | sh
info: downloading installer
...

選択肢が表示されるが、とりあえずデフォルトの1)を選択。

Current installation options:

   default host triple: x86_64-unknown-linux-gnu
     default toolchain: stable
  modify PATH variable: yes

1) Proceed with installation (default)
2) Customize installation
3) Cancel installation

Rust is installed now. Great!

To get started you need Cargo's bin directory in your PATH environment variable. Next time you log in this will be done automatically.

To configure your current shell run source $HOME/.cargo/env

パスを設定してくださいと言われるので、.bash_profile に記述しておく。

# Rust
source $HOME/.cargo/env

再読込して現在のシェルに反映させておく。これでRustが使えるようになった。

$ source ~/.bash_profile
$ rustc --version
rustc 1.14.0 (e8a012324 2016-12-16)

そしてRustを最新版(Nightly)に変更。

$ rustup default nightly
$ rustc --version
rustc 1.16.0-nightly (4ecc85beb 2016-12-28)

Hello, world

Rocket用のプロジェクトを作成して、そこに移動。

$ cargo new hello-rocket --bin
     Created binary (application) `hello-rocket` project
$ cd hello-rocket

Cargo.toml の依存ライブラリに以下を追加。

[dependencies]
rocket = "0.1.2"
rocket_codegen = "0.1.2"

src/main.rs を以下のように編集。このあたりは公式ドキュメントどおり。

#![feature(plugin)]
#![plugin(rocket_codegen)]

extern crate rocket;

#[get("/")]
fn index() -> &'static str {
    "Hello, world!"
}

fn main() {
    rocket::ignite().mount("/", routes![index]).launch();
}

そしてビルド・実行・・・するがエラーになる。

$ cargo run
...

error: aborting due to previous error

error: Could not compile `unicase`.

To learn more, run the command again with --verbose.

どうやらgccが入っていないのが原因らしいのでインストール。

$ sudo yum -y install gcc
...

今度こそロケットを打ち上げられた。

$ cargo run

Finished debug [unoptimized + debuginfo] target(s) in 79.19 secs
 Running `target/debug/hello-rocket`
🔧  Configured for development.
=> listening: localhost:8000
=> logging: Normal
🛰  Mounting '/':
=> GET /
🚀  Rocket has launched from http://localhost:8000...

VM内でcurlを叩くとちゃんと動いていることが分かる。

$ curl localhost:8000
Hello, world!

80ポートで起動する

ここからはあんまり正攻法じゃない。 ので、とりあえずこの方法でうまくいった程度の話。

Rocketのドキュメントを見ると、Productionで実行するには以下のようにすればよいと書いてある。

$ sudo ROCKET_ENV=staging cargo run
sudo: cargo: command not found

が、エラーとなる。

これは環境変数PATHがsudo時に引き継がれないのが問題らしい。

$ env | grep PATH
PATH=/home/vagrant/.cargo/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/vagrant/.local/bin:/home/vagrant/bin
$ sudo env | grep PATH
PATH=/sbin:/bin:/usr/sbin:/usr/bin

で、調べるといろいろと情報が出てくる。

CentOS で sudo 時に実行ユーザーのPATHを引き継ぐ http://qiita.com/ogwmtnr/items/3ec2fab50d069a3cf335

sudoで環境変数を引き継ぎたい http://mikio.github.io/article/2012/03/10_sudo.html

が、何を試しても一向にcargoのパスが引き継がれない。

仕方ないのでルートユーザーでもrustupを入れてしまう。(同様なので方法は割愛)

$ su -
# rustupのインストールと設定

ちなみにパスワードは「vagrant」。

そしてそのままルートユーザーで以下を実行すると、80ポートで起動する。

# ROCKET_ENV=staging cargo run

🔧  Configured for staging.
    => listening: 0.0.0.0:80
    => logging: Normal
🛰  Mounting '/':
    => GET /
🚀  Rocket has launched from http://0.0.0.0:80...

ホストマシンからブラウザでアクセスしてみるとちゃんと動いている。 http://192.168.33.13

GET /:
    => Matched: GET /
    => Outcome: Succcess
    => Response succeeded.
GET /favicon.ico:
    => Error: No matching routes for GET /favicon.ico.
    => Warning: Responding with 404 Not Found catcher.
    => Response succeeded.

ちなみにデフォルトの8000ポートだと以下のように指定してもアクセスできなかった。
http://192.168.33.13:8000/

ファイアウォールを切ったので問題ないと思ったのだが、何か設定がマズイのかもしれない。

終わり

Linuxとか環境構築とかあんまり経験ないので思ったよりもずっとしんどかった。 まぁしかしVagrantとか楽でいいなと思った。

とりあえず以下は解決したい課題。

  • ルートユーザで実行しちゃってる。(本番環境なら大問題)
  • 8000ポートでホストマシンから接続できるように。
  • Linuxとか詳しくなる。

Swift3: バージョン番号を楽に比較する

なんか面倒だったので書いた。

これがSwift使いの戦い方。たぶん。

Swiftコンパイラに詳しくないけど、こういった構造体でラップするだけのコードって最適化後はゼロコストになるのかしら?
Haskellnewtypeはゼロコストだったはずだけど)

SwiftでApplicative Style

書いたことなかったので書いてみた。

我流なのでいい感じに出来てるかは微妙だけどApplicative Styleがパーサ周りで有効活用できるという確かな実感は得られた。 Swift 4で(?)ジェネリクスが強化されれば、Haskellの型クラスのようにProtocolでもジェネリクスが使えるようになるのかしら?