Tallman

技術とか読書とかいろいろ

「エンジニアの知的生産術」を読んだ

読んだ理由

エンジニアとして働き始めたこの一年間、どうにも自分が闇雲に進んでいる感覚が拭えなかった。
目標にしている人はたくさんいるのだけれど、果たして自分は効率的に目的に向かっているのか?という疑問を感じるようになってきたので読んだ。

新しいことを学ぶには

情報収集、実践、応用はよく学習本であるやつという感想だったが、具体的に技術書を写経することについて述べられているのが面白かった。写経は効率が低いけれども写経しないと理解できないほどの問題に立ち向かわないとコンフォートゾーンから出れない、と書かれていた。最近自分もRustの技術書をヒィヒィ言いながら写経しているのだけれど、この本で少し勇気づけられた。
写経するときも頭を使って書き換えやコメントを利用しようとも書かれていた。このあたりは自分もよくやるのでこれからもやっていきたい。
逆にRubyの本を一から十まで写経するのも効率が良くなかったなという反省もあった。

やる気を出すには

この本ではタスクを一つに絞り、タスクを小さくするためにポモドーロ・テクニックを活用することが書かれていた。
タスクをマルチにするのは良くないのは自分も感じていて、同期的にタスクをこなすというのは腑に落ちた。 このブログもポモドーロ・テクニックを使って書いている。スマホを見ないと決めるのは大きな効果があった。

記憶を鍛えるには

この章で面白かったのは、一度読んで思い出してからさらにもう一度読むという「思い出し学習」が単に本を四回読んだだけの人より学習効果が高かったというものだ。
「思い出し」というのは記憶の穴を自己認識し危機感を得られる行為らしい。技術書を読んだあとに目次をみて内容を説明できるか?というぐらいなら真似しやすいだろうか。ブログを書くにのは二回目の通読のような気がするので、ブログを書く前に「思い出し」をやるといいかもしれない。

効率的に読むには

読書の目的は大雑把な地図、思考の道具というのが面白かった。自分の場合何しろ基礎体力が足りないと感じているので大雑把な地図がほしい。そのためにいろいろな分野をつまみ食いする。というのが大事なフェーズなんだろうと感じた。 とりあえずブログを書いて人に教えるを重点的に意識したほうが良さそう。

何を学ぶべきか?

戦略をもって学ぶのが良いということが書かれていた。 差別化戦略について語られていて、A要素だけでは人に勝てないならB要素もかけ合わせて卓越することが重要だと書かれていた。よくキャリア戦略なんかでも語られている考え方だと思う。「境界をまたぐ、複数の組織に属する」というのは本やOSSでも達成できることのような気もするけど、実際に体験するためには働いたり留学なり何かしら義務を持ったほうがいいのだろうか。

まとめ

現実の行動で変わったことは

  • ポモドーロ・テクニックを使うようになった
  • 広く片っ端からつまみぐいすることも悪くないと考える様になった。
  • 本を読んだあとに目次をなめる行動をするようになった。

ぐらいだろうか、周りがすごいエンジニアばかりで差別化するというのがなかなか思いつかない。

「食事する哲学者」で学ぶデッドロック

最近 プログラミング言語Rustを読んでるのですが、並行処理のチュートリアルで「食事する哲学者」という問題を知りました。

昔々、裕福な慈善家が、5人の高名な哲学者が宿泊できるカレッジを寄付しました。それぞれの哲学者には思索活動にふさわしい部屋が与えられました; また共用のダイニングルームもあり、そこには丸いテーブルが置かれ、5人それぞれが専用で使うイス5脚で取り囲まれていました。 彼らはテーブルを反時計回りに座ります。哲学者の左側にはそれぞれ金のフォークが配され、 中央には大きなボウルに入ったスパゲッティが常に補充されていました。哲学者は大半の時間を思慮に費やすのですが; 空腹になった時は、ダイニングルームに出向き、自分専用のイスに座り、左側のフォークを取上げ、スパゲッティに突き刺します。 しかし、絡まり合ったスパゲッティを口元まで運ぶには2本目のフォークが必要でした。なので哲学者は自分の右側にあるフォークも使う必要がありました。 食べ終わったら両側のフォークを元に戻し、席から立ちあがって、思索活動を続けます。 もちろん、1本のフォークは同時に1人の哲学者しか使えません。他の哲学者が食事したければ、 フォークが再び戻されるまで待たねばなりません。

並行処理でデッドロックが起こりうる問題です。

f:id:sasa5740:20191127212423p:plain
丸が哲学者で棒がフォークです!!!!
そのまま各哲学者が同時に右のフォークをとるとこうなります

f:id:sasa5740:20191127212547p:plain
丸が哲学者で棒がフォークです!!!!!!!

この状態になってしまうと1が左のフォークを取りたくても5が持っているので取れない、5も早くパスタをとってフォークをおろしたいけど4がフォークを持っているので取れない。…となり全員がお互いのフォークを下ろすのをおみあいしてしまう、いわゆるデッドロック状態になります。

プログラミング言語Rustでは5だけ最初に左のフォークを取らせて確実に誰かが両手にフォークを持てる状況を強制させていました。
この問題は 食事する哲学者の問題 - Wikipediaにも乗ってるけど色々デッドロックを起こさない方法があります。

セマフォって概念ここで初めて知りました。 dirty/cleanなんかはページングで使われてる気がする。

Rustにセマフォあったみたいだけど非推奨になっていた。MutexとCondvarでなんとかするのだろうか。

Punditにコミットした

github.com

結果的には破壊的変更をしてしまったのでPR送った経緯を書いておこうと思う。 もちろんこのままリリースされるかはわからないけど。
ちなみにPunditは以下のように明瞭な形でリソースに対しての行動に制約を設定できるようになるGemだ。

class PostPolicy
  attr_reader :user, :post

  def initialize(user, post)
    @user = user
    @post = post
  end

  def update?
    user.admin? or not post.published?
  end
end

# in controller
def update
  @post = authorize Post.find(params[:id])
  if @post.update(post_params)
    redirect_to @post
  else
    render :edit
  end
end

書きやすく読みやすいコードが書けて良いGemだと思う。

PRを立てた経緯

業務でPunditを利用させてもらっていて、ちょっと面倒なところがあった。

https://github.com/varvet/pundit#policy-namespacing にもあるがこのようなコードだ。

module Admin
  class PostPolicy
  ~~
  end
end

class AdminController < ApplicationController
  def authorize(record, query = nil)
    super([:admin, record], query)
  end
end

class Admin::PostController < AdminController
  def show
    post = Post.find(params[:id])
    authorize(post)
  end
end

「同じリソースに対してpolicy*1名前空間で分けて複数設定する」というのはよく行うのだけれど、Punditは上記のように名前空間を配列で渡すとpolicyを推測して読み込んでくれる。
がしかし、この方法をつかうとせっかくクールなこの書き方が使えなくなってしまう。

def show
  @user = authorize User.find(params[:id])
end

authorizeは渡された引数をそのまま返すので上記のように綺麗にかけるのだが、名前空間を推測させるために配列を渡すとそのまま配列が帰ってきてしまうのである。
仕方がないので業務では

class AdminController < ApplicationController
  def authorize(record, query = nil)
    super([:admin, record], query)
    record
  end
end

としてrecordが帰ってくるようにした。*2

recordが帰ってくるのが望ましいだろう、ということでせっかくのOSSだし修正の提案をしたのがPRを立てるまでの経緯だ。

PR立てた後

マージされるまでメンテナーの方に丁寧なレビューをもらい感謝しかない。こういってはなんだが自分としては無料で自分のコードみてもらって良いことしかなかった。
OSSはやることが多くて大変」という言葉をOSSパッチ会でkoicさんもおっしゃっていたが、本当に尊敬できる方たちだと思う。
Punditという素晴らしいGemをメンテしていただきありがとうございます。
READMEの更新を忘れて後ですごすごPR立ててすいません
最近Elixirが楽しかったがRubyもやっぱり面白い。

*1:Punditではリソースに対しての制約をpolicyと呼ぶ

*2:もちろんこのままじゃないですよ!

「コーディングを支える技術」を読んだ

チームの人が読んでいて面白そうだったので、またElixirの諸々でプログラミング言語というものに関心があったので読んだ

感想

ifを始めとする制御構文が何故生まれたのかについてgotoから解説してあったり、関数、例外、コンテナ等いつも書いてるプログラミング言語には当たり前にある機能が何故あるのかだったり、型、オブジェクト指向、並行処理を発端から物語のように書いてある等、単純に読み物として面白い本だった。
配列、連結リストの違いや、辞書におけるハッシュテーブルと木構造は技術的にも知っておきたい認識だと思う。
文字列、文字コードの話ものっていて、文字列がClang以外長さの情報持ってるとか、Unicodeに至るまでの過程とか、当たり前*1(であろう)知識を補完できてありがたかった。
読みやすい、書きやすい、とはなんなのか、それを計算機に効率よく処理させるにはどうしたら良いのか、色んな人がその課題に取り組んでいる。

どんな人におすすめか

何種類かプログラミング言語を触ってみたけど周りが言ってる「文字コード」とか「スレッドセーフ」とか「型推論」とかいまいちついていけないなぁという人におすすめできると思う。
自分が書いてるコードに対して「これってそういう考え、歴史からできていたのか!」というのがふんわりわかってコードを書くのがまた少し楽しくなった。

*1:僕はUTF-8ってのがいっちゃん強いんだろ?くらいの認識しかなかった。

Elixirにコントリビュートしてみよう!

最近ElixirにPRをだしてマージされました。

github.com

Elixir 1.10からEnum.frequencies/1Enum.frequencies_by/2が多分生えます。

      iex> Enum.frequencies(~w{ant buffalo ant ant buffalo dingo})
      %{"ant" => 3, "buffalo" => 2, "dingo" => 1}

      iex> Enum.frequencies_by(~w{aaa aA bbb cc c}, &String.length/1)
      %{3 => 2, 2 => 2, 1 => 1}

着想はRubyのtallyからです。

Elixirは書いていてとても楽しい言語で,リンターやテスト等周辺のエコシステムも整っていて便利です。
みなさんもぜひElixirの魅力にはまってElixirにコントリビュートしてもらいたいということで記事を書きました。 今回はElixirという言語そのものの仕様ではなく、elixir-lang/elixirへの実際にPRした時の流れを書きたいと思います。 後世のElixirを担うそこのあなたに少しでも役にたてば幸いです。

elixir-lang/elixirの特徴

コントリビュートするという視点で見てelixir-lang/elixirの一番大きな特徴は実装がElixirで書かれているということです。Rubyの実装に貢献するにはC言語の知識が必要ですがElixirではElixirが書ければElixirが書けます!

elixir-lang/elixirに貢献したい!どうすれば?

公式のREADMEを読みましょう。 一番最新で正しい情報がのってます。とはいえそれだとこの記事終わってしまうので自分のやったことの流れを書きます。

  1. forkしてローカルにclone
  2. Enjoy Cording!
  3. make compileして変更したexファイルをコンパイル
  4. bin/elixir lib/elixir/test/elixir/変更したファイルのテスト.exsでテストを実行
  5. mix formatでリンターを走らせます
  6. commit & push!

1,2,6はどのOSSでも当然ですね。 2の作業中に実際にREPLを叩きたい場合は、変更したファイルをコンパイルしてbin/iexで変更したコードが反映されたiexを起動することができます。
以下のように変更したファイルだけコンパイルすることもできます。
bin/elixirc lib/elixir/lib/string.ex -o lib/elixir/ebin
6については、例えばrails/railsだと「squashして一つにまとめてね」といった注意点があったりしますがElixirというOSSには今の所ないみたいです。

僕はできなかったけどやるべきだったこと

新機能の追加はまずメーリスで提案しよう!

いきなりREADMEちゃんと読め案件なんですが、新機能のPRを出す前にElixir Core maling listで提案してなぜ役立つのかをコミュニティに説明しましょう。僕はこれを怠りいきなりPRたてて30分でcloseされました。 今回の自分の提案では「tallyの機能って他の言語であったりする?」「名前をこうするのはどうだろう」と様々な意見が出ていました。コミュニティの熱量も感じられますしメーリングリストは怖いところではないですよ!

ちゃんと議論が収束するまで待ちましょう

これが一番の反省点なんですが、メーリングリストに提案を出してすこし肯定意見が出始めたあたりで勇んでPRを再度立ててすぐにcloseされるということがありました。ちゃんと議論の収束やコミッターからの指示を待ちましょう。

パフォーマンスを意識したコードを書こう

最初に僕が出した実装はこれです。

  def tally(enumerable, func \\ fn x -> x end) do
    group_by(enumerable, func)
    |> map(fn {key, val} -> {key, count(val)} end)
    |> Map.new()
  end

関数名とか可読性は別としてこの実装だとgroup_byを使っているので全ての要素に二回アクセスしていることになります。
PRを出して提案されたコードは以下

  def frequencies_by(enumerable, key_fun \\ fn x -> x end) when is_function(key_fun) do
    Enum.reduce(enumerable, %{}, fn entry, acc ->
      key = key_fun.(entry)
      Map.update(acc, key, 1, &(&1 + 1))
    end)

reduceは折りたたみ演算です。この実装なら各要素へのアクセスは一回ですみます。 全てのプログラミングにおいて当然のことですが、特にプログラミング言語や大きいOSSではパフォーマンスが重要だと思います。意識して書くと良いですね。

自分一人でPRをつくるわけではない

特に既にコミュニティがあるOSSに言えますが、PRは自分だけで完成するものではないです。 最終的に自分が出したPRも色々変更されています。 というか最終的な実装は僕が書いたとは言えない
最初から文句なしマージというのは他のPR見ていてもなかなかないと思います。(typo修正とかは別)
きちんとOSSとコミッターに敬意をはらったPRをだして反応に真摯に対応しましょう。

おわりに

後半はElixir関係なくなっちゃいましたが、ElixirにPR出すときの簡単な標識にでもなっていれば幸いです。
なんだか偉そうなこと書いたけどメンテナー側の人間ではないのでもしかしたら間違ってるかもしれない。とりあえずROMして雰囲気を掴むのが一番いいと思います。
もしElixirに興味を持たれた方は(日本語版は1.2時点のもので少々情報が古いですが) プログラミングElixirという本がおすすめです。というか神本なのでぜひ買ってください。他言語からElixirを学ぶ人向けに書かれていて、ライブラリを作るハンズオンや非同期処理やメタプログラミング等盛りだくさんの内容です。
また今回PRを出すに当たり#tokyoexで学んだことにとても助けられました。大変勉強になる会なのでみなさんもぜひ足を運んでみてはいかがしょうか。

tokyo.ex#13 elixir本体ソースコードもくもくリード会に参加してEnum.tally書いてみた

「tokyo.ex #13 elixir本体ソースコードもくもくリード会」に参加してきました。

beam-lang.connpass.com

Elixirのソースコードを事前にcloneしておいてMakefileなんかを見ながらエリクサーのコンパイルの流れを解説していただきました。

speakerdeck.com

speakerdeck.com

Makefile is 何?から解説してくださっている発表もありElixir以前の段階で勉強になりました。

ふんわり理解をつぶやいたら登壇者の@hayabusa333氏に補足してもらえました。ありがとうございます!

 もくもく会

実際のもくもく会の内容は「Elixirのバージョンを書き換えよう」と「標準モジュールの追加」の2つがありました。

Elixirのバージョンを書き換えよう

ElixirのversionはrootディレクトリのVERSIONというファイルに記載されているのでこれを書き換えてmakeすればok

実際にmakeしてみると

~~~~~~
Recompile: src/elixir_aliases
Recompile: src/elixir
Generated elixir.app
==> bootstrap (compile)
Compiled lib/elixir/lib/kernel.ex
Compiled lib/elixir/lib/macro/env.ex
~~~~~~

スライドどおりelixir.appが作成されてbootsrapという関数が実行されているのがわかりますね。

標準モジュールの追加

標準モジュールの追加ですが、自分はモジュールの追加ではなく組み込みモジュールのEnumに関数を一つ追加しました。Rubyで新しく実装されたtallyです。単にtallyが好きで書きたかっただけ

ソースコードはこんな感じです。

  def tally(enumerable, func \\ fn x -> x end) do
    group_by(enumerable, func)
    |> map(fn {key, val} -> {key, count(val)} end)
    |> Map.new
  end

ちょっと例外までは考えきれてないんですが現状のElixirの知見で書いてみました。*1

せっかくなのでテストもElixir本体に書いてみました。

  test "tally/2" do
    assert Enum.tally(~w{a a a b c c}) == %{"a" => 3, "b" => 1, "c" => 2}
    assert Enum.tally(~w{aa aA bb cc}, fn x -> String.downcase(x) end) == %{"aa" => 2, "bb" => 1, "cc" => 1}
  end

Elixir本体のテストはコンパイルしたら直接ファイル指定して実行できます。*2

 > bin/elixir lib/elixir/test/elixir/enum_test.exs

Excluding tags: [windows: true]

......................................................................................................................................................................................................................................................................................................................................................

Finished in 2.0 seconds (1.9s on load, 0.05s on tests)
180 doctests, 162 tests, 0 failures

Randomized with seed 313260

けっこう便利なメソッドだしドキュメントちゃんと書いてElixirにPR出してみようかなとか思ってます。 Elixirがこういうメソッドどう考えてるのかはわかりませんが。

Elixirコミュニティについて感じたこと

福岡を中心に西日本で盛り上がってるみたいですね。Rubyも福岡は元気なイメージだし福岡熱い。
Rubyの勉強会にいくとほとんどの方がwebエンジニアという印象ですが、Elixirでは組み込みやゲーム領域のエンジニアの方もいて新鮮でした。ゲームとか組み込みとかとの親和性もElixirの魅力ですね。

感想

とても楽しい勉強会でした。主催者の方、参加者の方、会場を提供していただいた株式会社ドリコム様ありがとうございました!

*1:もっといい書き方あるで!という方ぜひ教えてください:pray:

*2:https://github.com/elixir-lang/elixir#contributing

Elixirについて社内LTで発表したよ

最近Elixirに興味をもってプログラミングElixirという本で勉強してます
社内LTでそのことについて発表してみました https://speakerdeck.com/qwyng/elixirtopatanmatuti

再代入の説明に@cedretaber氏のこの記事 qiita.com を参考にしたところがあります。

speakerdeck.com

以下Elixirについて思ったこととか

再帰の考え方を学べる

再帰の考え方が学べる言語かと思います。パターンマッチでを使うと引数で処理を分けたりができるのでifを使うことがあまりなさそう

JSON解析が強そう

プログラミングElixir読むとわかるのですがパターンマッチが根幹をなしてる言語なのでJSONの解析がCoolにできます。

関数型楽しい

Rubyだとオブジェクトが主役でメソッドがオブジェクトのAPIぐらいの考え方だったんですけど、Elixirでは関数が主役でオブジェクトじゃなくて「値」って感じがします。 脳に新たな刺激が入ってくる感じで楽しいです。
破壊的変更がないって読みやすくなりますね。

プログラミングElixirが神本

これが一番言いたかったことなんですけど

プログラミングElixir

プログラミングElixir

この本すごいです。他の言語からElixirを学ぶためにこの本を読むと「これこれこの難易度感で語って欲しいねん〜」が頻発します。 また、練習問題が散りばめられており、いい感じに手を動かしながら読み進めることができます。(練習問題は答えがのってないので間違ってるかもしれない)

github.com

GitHubAPIからisuueとってきてきれいにターミナルに表示するというライブラリ作成ハンズオンまであります。 私的にメタプログラミングRubyレベルでいい本かと思ってます。

おまけ

ruby-jpでこの本の内容について質問して「訳とか文脈の問題ですかね〜」とかのたまったら訳者である笹田耕一さんに「訳大丈夫ですか」と聞かれて震えました。 (翻訳ありがとうございます!、翻訳のおかげで自分もこの本の内容を少しづつ読み進めることができます:bow:)