すごいHaskellたのしく学ぼう を読んだ

ゴールデンウィークに時間ができたので,言わずと知れた名著「すごいHaskellたのしく学ぼう」を読みました. 簡単に振り返ってみます.

どんな本か

Haskellの素晴らしい入門書.全人類読むべき. Haskellのサンプルコードを通して,Haskellの言語仕様と関数型プログラミングの概念について学び,Haskellに備わっている標準関数や型を実装したり,具体的な問題を解いていきます.

冗談を交えながら軽い語り口調で進めていくのですが,実はかなり難しいです. この記事では,この本の書評だけでなく,私が重要だと感じた部分,難しいと感じた部分をちょこっと解説するので,この本に興味がある人や読み始めたけど詰まってしまった人は読んでみてください. また,私自身Haskell初心者なので,間違っているところがあれば教えていただけるとありがたいです.

難しい本ですが,Haskellに興味のある人,関数型プログラミングが気になっている人には自身を持ってオススメできる本です.

内容

第1章 はじめの第一歩

第1章では,関数呼び出しやリスト,そしてリスト内包表記などについて簡単に触れます.

第2章 型を信じろ!

第2章では,型や型クラスについて簡単に触れます.この型クラス初級編の時点で私は少し苦戦しました. 難しければとにかく次に進んで,あとで読み直すとすっきり理解できると思います.

第3章 関数の構文

第3章では,関数の構文,パターンマッチやガードについて見ていきます. もちろんif式で実現できることも多いのですが,これらの構文を使った方が非常にエレガントに書けますし,後の再帰が非常に理解しやすくなります.

第4章 Hello 再帰

関数型プログラミングの真髄,再帰です. この章はこの本でもっとも感動した部分の1つです.私はこの本のおかげで再帰を理解することができました. 再帰に苦手意識のある人は,Haskellに興味がなくてもこの章だけ読んでいってもらいたい(?)

Haskellでは再帰が重要です.なぜなら,Haskellでは命令型言語のように計算をどうやってするかを指定するのではなく, 求めるものが何であるかを宣言して計算を行うからです(p.52).

再帰を使う際の定跡は,まず基底部を見極め,次に,解くべき問題をより小さな部分問題へと分割する方法を考えることです. 基底部と部分問題の詳細を考える必要はありません.部分問題の解が正しいという保証をもとに,より大きな最終問題の解を構築すれば良いだけです(p.59).

声に出して読みたい名文ですね.

それから,あの有名なHaskellによるもっとも単純なクイックソートの実装が現れるのもこの章です.

quicksort :: (Ord a) => [a] -> [a]
quicksort [] = []
quicksort (x:xs) = 
    let smallerOrEqual = [a | a <- xs, a <= x]
        larger = [a | a <- xs, a > x]
    in quicksort smallerOrEqual ++ [x] ++ quicksort larger

美しいですね.特に最終行.最終的に欲しいリストの構造をそのまま定義するだけで実現できています.

第5章 高階関数

第5章では,カリー化関数,ラムダ式,畳み込みなどについて解説しています. この章を読むと,なぜHaskellの関数定義では引数と返り値の区別が付かないような書き方をするのか,その謎が解けます.

第6章 モジュール

この章では,Haskellの言語仕様から離れて,モジュールの作り方,標準で用意されているモジュールの使い方など, 非常に実用的な事柄を説明しています.

第7章 型や型クラスを自分で作ろう

第7章には,独自の型を定義する方法,型引数やレコード構文といった便利な構文,型クラスとそのインスタンスの作り方, そしてファンクタなど,非常に多くの重要な概念が含まれます.だんだん難しくなってきます.

その中でもファンクタは特に重要です.この本によれば,ファンクタを一言で説明すると,「全体を写せる(map over)ものの型クラス」だそうです. そして,ファンクタのインスタンスは型コンストラクタ(具体的な型をとって型を作るもの)で,その型によって生み出された具体的な値を,ファンクタ値といいます.

この説明では少しわかりにくいかもしれませんが,例えばMaybeやリストはファンクタのインスタンスであり,型コンストラクタです. つまり,MaybeはIntをとってMaybe Int型を作りますし,リストはIntをとって[Int]型を作ります. そして,Just 5や[3, 4]などがファンクタ値にあたります.

これらの例でわかる通り,ファンクタ値には,値だけでなく,Just や []など,不思議なもの(値コンストラクタ)が付いています. この不思議なものたちは,「文脈」を値に付加していると考えられます. つまり,Just 5は,5という数字だけでなく,「失敗する可能性のあるMaybe Int型の処理で,うまく成功して5を返したよ」という文脈を持ちます. リストに関しても同様に,[3, 4]はただの3や4ではなく,「3と4の両方だよ」という文脈を持ちます.

かなり難しくなってきますが,このファンクタ値のもつ「文脈」をどう扱うかがこの本の1つの大きなテーマになっていて,これが以降の章に繋がっていきます. もっと言うと,ファンクタさえ理解できれば,この本を読み進められるので,頑張ってください.

第8章 入出力

この章では,doprintgetLineなど,I/Oの扱い方について見ていきます.

第9章 もっと入力,もっと出力

第8章では,標準入出力だけを扱っていましたが,第9章では,ファイル読み書きがメインのテーマになります. また,ファイル読み書きのパフォーマンスを向上させるBytestring,そして難関である乱数についても見ていきます. 参照透明性をもつHaskellがどのように乱数生成を実現しているのかがわかり,面白いです.

第10章 関数型問題解決法

第10章は,逆ポーランド記法電卓の実装と最短経路探索という2つの問題を解きながら,関数型プログラミングに慣れていきます. Haskellでコンテストに出たくなりますね.

第11章 ファンクターからアプリカティブファンクターへ

7章にも出てきた,ファンクタをより詳しくみていきます. ファンクタは抽象的な概念ですが,ファンクタも型クラスの一種であること,ファンクタのインスタンスはファンクタ値の中身を写す関数fmapをもつものだということがわかれば,読み進めることができると思います. ファンクタという用語をそのまま理解するよりも,fmapで具体的なファンクタのインスタンスを写してみて,イメージをつかむことが大事です.

そして,ファンクタをさらに拡張した概念であるアプリカティブファンクタ(以後アプリカティブと略)についても説明されています. アプリカティブも型クラスで,そのインスタンスにはpure関数と<*>演算子が定義されています. アプリカティブ値は,以下のような操作が可能です.

ghci> Just (5*) <*> Just 3
Just 15

ghci> pure (*) <*> Just 5 <*> Just 3
Just 15

Maybeは実はアプリカティブなので,Justの中身に関数を入れられます.さらに,<*>という演算子を使って,Justの中身の関数を,もう一つのJustの中身に適用したJust値を得ることができます. また,pure関数は,引数をファンクタの持つ文脈に含められることができる関数で,fmap pure (*) Just 5Just (5*)を返します.

解説の中でfmapをいい感じに書けるアプリカティブスタイルという記法も導入されていますが,すでにたくさん記法が導入されているので,慣れるまでfmapで読み替えた方が頭が爆発しなくて済みます.

第12章 モノイド

また抽象的で難しい概念が出てくるのか...と思いきや,モノイドはそんなに難しくありません. モノイドはファンクタと同様型クラスの一つで,結合的な2引数関数とその関数に関する単位元からなる構造です. つまり,ある型同士の2引数関数が結合則を満たし,その関数に関する単位元が存在する場合,その型はモノイドのインスタンスになることができます.

モノイドを導入して何が嬉しいかというと,Haskellの便利な関数である畳み込みを,リスト以外の型にも適用できるようになります.

また,モノイドを扱う上でよく出てくるnewtypeキーワードに関しても解説があります.

第13章 モナドがいっぱい

モナドも型クラスの一種で,ファンクタの拡張ともいえます(結果的にですが,アプリカティブの拡張でもあります). したがって,モナドもまた「文脈」を値に付加するものです.ファンクタと大きく異なるのは,文脈を持つ値をさらに自由に扱うことができるバインド(>>=)という関数を持つ点です.

モナドは,バインドがキモです. バインドは,「文脈付きの値」と,「通常の値をとってモナド値を返す関数」を引数にとり,「通常の値をとってモナド値を返す関数」を,「文脈付きの値」の中身に適用し,文脈をつけて返します. これができるようになることで,文脈付きの値に対してできることの自由度が大幅に大きくなります.

例えば,以下のような操作が可能です.

ghci> Just 5 >>= \x -> Just $ x * 3
Just 15

これは,>>=によって,モナド値の中身に,3をかける関数を作用させて,モナドに包んで返していることを表しています.

そして,複数のモナド操作を繋げられるdo記法がどのようなものか説明されます. これで,IOに出てきた処理がわかるようになりました.

第14章 もうちょっとだけモナド

第14章はモナドの応用編で,WriterモナドやStateモナドモナド値を操作する関数を使ってみたり,自分で新しくモナドを作り出します. その中でもStateモナドに関する説明は非常に面白いです.「参照透明性」と相性が悪いように思われる「状態」を,どのようにHaskellが扱うのか,それをモナドを使って解決していきます.

第15章 Zipper

Haskellは副作用を持たないので,データの変更という操作は基本的にできません. もしデータを変更したいなら,元のデータを直接操作するのではなく,変更した後のデータを返す,という処理になります.

このような制約を持ちながら,木構造やリストなど,順番に辿る操作を効率的に実現する機能がZipperです. この章では,二分木やリスト,ファイルシステムといった木構造に対してパンくずリスト,つまり訪問履歴を付け加える方法について説明しています.

終わりに

Haskellの言語仕様と関数型プログラミングの考え方が学べる非常に素晴らしい本でした.

現在,多くの言語が高階関数ラムダ式,map,filterなどの関数をサポートしていることから, 関数型プログラミングはコードを書く人間にとって必須の教養であることがわかりますね.

Spacemacsを導入した

Emacsを始めようと思い立ち,「Emacs実践入門」を読んで設定ファイルを書いていたところ,Spacemacsなるものを見つけたので導入してみました. 今回はそのメモです.

インストール

GitHubのページにインストール方法が丁寧に書いてあります.

インストールとはいっても,結局Spacemacsも.emacs.dなので,もしすでにemacsをインストールしていれば,.emacs.dにクローンしてくるだけです.

git clone https://github.com/syl20bnr/spacemacs ~/.emacs.d

ただし,上のコマンドは既存の.emacs.dを置き換えてしまうので注意してください.

GitHubのページには,emacs-plusのインストールに関する記述がありますが,ターミナルで使うのであればこちらはインストールしてもしなくてもどちらでも構いません. ただし,もともとemacsをインストールしている方は,brew install中に次のようなエラーが出るかもしれません.

Error: The `brew link` step did not complete successfully

困った時はbrew doctorします.見ると,emacsとlinkが衝突しているみたいなので,emacs-plusを使いたい人は,インストールの前にemacsのリンクを外してあげるといいと思います.

brew unlink emacs

GitHubに書かれているbrew linkapps emacs-plusは動かないので,インストール時はこうします.

brew install emacs-plus
ln -s /usr/local/opt/emacs-plus/Emacs.app /Applications

リンク元はインストール先に応じて変えてあげましょう.

ターミナルで動くemacsを使う場合には,上記のステップは必要なく,git cloneした後,いつものようにemacsを起動すれば,少しかっこいい見た目のemacsが起動します.

最後に

Spacemacsは新しいエディタではなく,Emacsの設定を書くことでパワーアップしたものです.

Emacsフルスクラッチでカスタマイズするのは辛そうだなあ,どこかに良さげな設定ファイル落ちないかなあと思っている人はSpacemacsから始めてみてもいいと思います.

私もまだまだ始めたばかりなので,さらに設定ファイルをカスタマイズして使いやすくしていきたいと思います.

マスタリングTCP/IPを読んだ

テストが終わったので,エンジニアの教養の1つであるTCP/IPを学ぼうと思い,「マスタリングTCP/IP 入門編」を読みました.

マスタリングTCP/IP 入門編 第5版

マスタリングTCP/IP 入門編 第5版

勉強ログとして,参考になった部分や感想を書いていきます.

どんな本か

言わずと知れたTCP/IPの日本語書籍の名著ですが,ゆるふわ非常に平易な文章で書かれています. わかりやすい例えと図のおかげで,初学者にも優しい構成となっています.日本語で書かれているのもありがたいですね.

ただ,TCP/IP自体がかなり幅広い概念なので,また,入門編ということもあって, 詳しく説明しているのは基本的な部分だけで,多くのプロトコルは紹介するだけとなっています. また,スループットを求める数式などもほとんど出てきません.

TCP/IP,インターネットの仕組みに興味を持った人が,初めの一冊として取り掛かるには最適です.

TCP/IP とは

TCP/IPとは,インターネットを支える通信技術の総称です. 決して,TCPやIPなどの単体のプロトコルを意味するわけではありません.

データリンクとは

ネットワークを構成する最小単位(OSI参照モデルデータリンク層を意味する場合もあります). 代表例はイーサネットネットワークで,これは端末とスイッチをケーブルで繋いだネットワークや, 端末を接続したスイッチ同士をケーブルで繋いだネットワークを指します.

ネットワーク層データリンク層の関係

ネットワーク層のデバイス(例えばルーター)は駅員さん, データリンクは,乗り換えなしで行ける駅同士の1区間に例えることができます.

駅員さんに,JR京橋駅から阪急北千里駅に行きたいと伝えると,どの切符を買って,どの電車に乗れば良いか教えてくれます. そして,教えてもらった通りに電車に乗れば,確実に目的地に到着することが保証されるわけですね.

しかし,乗り換えが複雑になってくると,全てを一度に駅員さんに教えてもらうとかえって混乱します. そこで,駅員さんははじめに乗るべき電車だけを教え,「あとは次の駅の駅員に聞いてください」と伝えます. これがルーターにおけるパケットの中継に相当します.

もう少し掘り下げて説明すると,JR環状線京橋駅という名前はIPアドレス,つまりネットワーク層のアドレスに, 駅に割り当てられた番号はMACアドレス,つまりデータリンク層のアドレスに相当します.

駅を識別するだけなら駅ナンバーだけでよいのですが,それは駅員や乗客には非直感的でわかりにくいので, JR環状線京橋駅などのように,構造を含めた呼び名を使います.

TCPのウィンドウ制御

本書を読んで最も感動した部分です.

TCPではデータが届いたらACKを返すのですが,1セグメントずつACKが返ってきたかどうかを判定していたら日が暮れてしまいます. そこでウィンドウ制御です.ウィンドウ制御とは,1セグメントずつACKを待つのではなく,もっと大きな単位でACKに対処する方法です.

面白いのはここからです.次に,ウィンドウ制御を採用した場合の再送制御について見てみます. 図を見ていただいた方がわかりやすいので引用します. f:id:txtbokwrm:20190220220242j:plain [マスタリングTCP/IP Ch6.4.6 P.238]

1 ~ 1000番目のデータを受け取った後,1001番目から2000番目のデータだけが届かなかったケースです. この場合,2001番目以降のデータは無事に届いていますが,1001番目から2000番目のデータは届いていないので 受信ホストはその部分のデータをリクエストし続けます.

ここで,受信ホストから3つの重複応答があった時に再送すると決めておきます.すると, ウィンドウ制御によってデータをやり取りする時間間隔が短くなっているため,タイムアウトによる再送制御よりも ずっと早くデータロスに気づくことができます.

さらに,2001番目から7000番目までのデータが無事に届いているので,1001 ~ 2000番目の再送要求が受け入れられた後に リクエストすべきデータは7001番目以降のデータとなり,受け取ったデータを一切無駄にしていないことがわかります.

このようにウィンドウ制御は,元々は非常にシンプルな発想であるにも関わらず,平常時にスループットが高いだけでなく データが失われた時の対応も高速であるという非常に素晴らしい性質を持っています.

電子メールとPOPとIMAP

何気なく使っていたメールですが,その裏では多くの技術が使われていることがわかりました.

基本的に,電源が入っていないホストとは通信できません.しかし,スマートフォンの電源を切っていても, その間に送られたメールはスマートフォンの電源を入れた後に受信されます.

この仕組みを実現するために,電源を切らないメールの受信サーバーを置き,端末がそのサーバーにメールをリクエストすることで 端末宛のメールを受信できるというプロトコルが考案されました(POP).

さらにIMAPというプロトコルによって,メールのデータそのものだけでなく,メールの管理もサーバー側で行います. これによって,メールの未読・既読情報やメールボックスの管理などを同期することができ,複数の端末でメールを読む環境で便利です.

セキュリティと暗号化

通信においては,これでもかというほど暗号化やセキュアなアプリケーションが使われています.例えば,

厳重すぎますね... これもネットワークのレイヤー化の恩恵の1つです.

IEEE802.1X

IEEE802.1Xは,認められた機器のみがネットワークにアクセスできるように認証できる仕組みです. これは簡単に言えば,スタバの無線LANを実現する仕組みです.

スタバの無線LANでインターネットを利用するためには2つの手順が必要です.

  1. スタバの無線LANに接続
  2. ブラウザを使ってログイン

この裏で何が行われているかというと,手順1によって接続確認用のVLANに繋がります. このVLANでは,認証サーバーなど限られたホストのみと通信可能です.

この状態で,手順2によって認証を行うことでインターネットを利用可能な状態になります.

おわりに

以上が,私がなるほどなあと思ったことや覚えておきたいなあと思った部分です. この本は本当に入門者向けなので,もしネットワークやインフラ関係の仕事をするようになったら, タネンバウムの「コンピュータネットワーク」やスティーブンスの「UNIXネットワークプログラミング」を読みたいですね.

リクルートのデータサイエンティストインターンシップに参加してきました

2/25から3/22まで、リクルートのデータサイエンティストのインターンに参加させていただきました(実はデータエンジニアとしてインターンに参加したのですが、案件としてはデータサイエンティストのものでした)。

そこで今回はその記録です。

選考

選考は、エントリー→コーディングテスト→1次面接→2次面接→結果発表という流れでした。

コーディングテストの難易度は、エンジニアインターンとしては比較的簡単。 1次面接は人事の方と、2次面接はデータサイエンティストのマネージャーの方とおしゃべりしました。

実は私は夏に一回リクルートのエンジニアインターンに応募したのですが落ちてしまって、今回再チャレンジして合格できたので、一回落ちてしまった人も落ち込まずに、実力を付けてもう一度チャレンジしてみるといいと思います。

インターン

1週目

私はリクルート住まいカンパニーに配属されました。SUUMOの会社です。 当初やりたいこととは違ったのですが、以前から興味のあったデータサイエンティストの業務が経験できそうでわくわくしてました。

案件としては、テーマを与えられるのではなく、データを分析し、課題を自分で考える形式でした。

とりあえずBigQueryを使ってデータ解析。SQLはWebアプリ開発などで基本は押さえていたつもりだったのですが、業務で使うデータ解析をやろうと思うと、全然スキルが足りないことに気づきました。

2週目

この週に中間発表があり、それに向けて分析したり取り組むタスクを決定する。。。はずだったのですが、1週間で、ドメイン知識もない状態で、データを解析してどこがボトルネックかを分析して課題を発見するのはとっても難しかったです。 分析の面でも、ビジネスの面でもバチバチに詰められてとっても辛かったですが、その分学びも多かったです。

3週目

3週目の半ばにようやくアイデア決定。1日でデータとモデルを作ることができたのは間違いなくkaggleのおかげ。みんなkaggleやろう。手が動くようになります。

4週目

この週はほとんど最終発表の準備に充てました。さらに厳しいフィードバックを受けた時期。心を強く持って自分の考えた課題に根拠を与える重要性を感じました。

普段からビジネス面での指摘に慣れていたおかげで、最終発表は堂々とできましたし、質疑応答もきちんと対応できたと思います。メンターも褒めてくれました。

学んだことと気づいたこと

SQL

1週目はメンターが書いてくれた400行弱のクエリを見て怯えていましたが、3週目には、気づけば自分の書いていたクエリが400行を超えていて、ウインドウ関数も使いこなせるようになって成長を感じました笑 SQLクエリも、他の言語で書かれたプロダクトコードと同じように、読みやすい書きかたがあることに気づきました。メンターの方が書いたクエリの意図がわかるようになり、自分も可読性の高いクエリを書くことを心がけていました。

ランキング学習

レコメンデーションには興味があったので、この機会にランキング学習を学ぶことができ、業務でゴリゴリにレコメンデーションを書いているメンターの方にフィードバックをもらえて非常に楽しかったです。 今まで回帰問題と分類問題しか扱ったことがなかったので、ランキング学習の考え方は新鮮でした。

データサイエンティストの重要性

今回データサイエンティストの業務を体験させていただいて、その仕事の重要性に気づきました。SUUMOのアプリに、これだけ多くの優秀なデータサイエンティストがユーザーの行動ログを解析し、データ駆動でビジネスを展開しているのを見ると、データサイエンティストとビジネスサイドの人が協力して進めていけるような企業が今後強いんだろうなあと思いました。 データサイエンティストは、エンジニアと同様今後も需要の高い職業になると思います。

そのほかインターンに参加してよかったこと

お給料がもらえる

新卒と同じか少し上くらいの額がもらえます。お給料が高いと会社に貢献するモチベーションが高くなります。

お昼ご飯が豪華

もはやリクルートインターンの名物となっていますが、高めのランチを食べられるだけでなく,メンターとも仲良くなれるので、本当にいい制度だなと思いました。 ただ、ランチのグレードは配属先のオフィスの場所に大きく左右されます。住まいカンパニーのある田町はあんまり高いランチが無くて残念><

技術書購入補助

インターン修了の副賞として、*円分の技術書購入補助が頂けました。めちゃめちゃ嬉しい(額は公表して良いのかわからないので伏せてますm( )m)。

LT会

エンジニアインターンでは毎週LT会が開催され,インターンに参加している学生と社員の方が持ちネタを話してくれました.新しい技術を試してみた話や趣味の話など,面白いLTがとっても多かったです.また,LT会を通じて他のカンパニーのインターン生や社員の方とお話できるので,本当に良いイベントでした.

最後に

データサイエンティストの業務を体験する中で、自分はデータサイエンスとエンジニアリング両方やりたいなと思いました。今後もがんばっていきます💪

今回指導していただいたメンターの方々、インターンを企画していただいた事務局の方々、本当にありがとうございました。