Uploaded avatar of xavdid

#12in23を完走した人による振り返り

@xavdid
2年以上前

この記事はもともとDavidのウェブサイトに掲載されたもので、許可を得てここに再掲載しています

昨年1月、Exercismは12in23という新しいプログラムを発表しました。これは、参加者に2023年に12の新しいプログラミング言語を試すことを促すものでした。毎月テーマ(「Analytical April」や「Object Oriented October」など)が設けられ、試すべき特定の言語が紹介されました。私は新しいことを学ぶのが大好きで、少し(プログラミング)言語オタクになっていたので、試してみることにしました。12の言語、12か月!

年がほぼ終わろうとしている今、このプロジェクトがうまくいったことをとても嬉しく思っています。12の新しい言語を無事に試し、Exercismコミュニティで素晴らしい人たちと出会い、その過程でいくつかクールなオープンソースへの貢献も形にしました! この記事では、それらすべてを順に振り返り、それぞれから何を得たかを話します。

言語の選び方

この経験を最大限に活かすため、その年のためにいくつかの指針を立てました。

  1. 言語は、私にとってまったく新しいか、少なくともたくさん学んでいると感じられるほどには馴染みのないものであること。
  2. 選んだ言語は、将来さらに学ぶのに(潜在的に)実用的であること。このプロジェクトはただの遊びでしたが、私は(少なくとも部分的に)役に立つことを学ぶ時間に使いたいのです。
  3. 使う言語ごとに、ローカルのツール一式とVSCodeプラグインをすべてインストールすること。できるだけ多くの型ヒントとインテリセンスを効かせて、ほぼ同じ条件で言語を比べたかったのです。大学時代、私はプログラミングの宿題をすべてSublime Textで、リンティングもオートコンプリートもなしにやっていました。そういうツールを使いながらプログラミングを覚えると、それらに頼りすぎて良いプログラマーになれないのではないかと心配していました。ところが、実際は逆でした。思考をツールに任せられれば任せるほど、目の前の本当の問題について考えられるようになります。物事を覚えるのではなく、それを見つける方法を覚えましょう。

さっそく見ていきましょう!

1月(テーマなし)

1月が始まった時点で、Exercismチームはまだ毎月のテーマを選んでいる最中だったので、その月の言語は自由選択でした。方向性がないまま、私はGoでこの年を始めました。2022年の半ばにこの言語の短期集中講座をやっていましたが、それ以降ほとんど使っておらず、まったく習熟している感じはありませんでした。

Goは面白い言語です。厳格なコンパイラーは、書いたプログラムが必ず正しいものになることを意味しますし、コンパイラーが安全だと判断するまでは一歩も先に進めません。1 エラー処理に対する冗長なアプローチのおかげで、驚かされることはありません(その代償として、if err != nil { return err }を 何度も何度も 書くことになります)。難しいこと(たとえばチャネルを使った並行処理)を簡単にするのは得意ですが、簡単なこと(文字列操作)を難しくもします。強力な標準ライブラリを備えているので、ほとんどの作業はサードパーティのモジュールなしで済ませられます。エコシステムの多く(フォーマット、インストール、ビルドなど)がファーストパーティ製で、goコマンドに組み込まれているところが気に入っています。この言語には批判者もいますが、正確さと保守性という目標はおおむね達成できていると思います。

第一選択として手を伸ばすほど使っていて楽しいわけではありませんが、パフォーマンスが重要なプログラム、たとえばシェルプロンプトにネストしたパスを表示するような場面では、道具箱に入れておくと便利な優れたツールです。

Functional February

2月はいきなり深いところから始まり、関数型言語が取り上げられました。関数型言語は、より一般的な命令型プログラミング言語から派生した、数学寄りの系統です。関数型言語は、(副作用のない)「純粋な」関数で知られています。私はElixirを選びました。主な理由は、友人のCalebがAdvent of Codeで使っていて絶賛していたからです。

Elixirはとても楽しく使えました。Rubyに影響を受けており(それは当然で、作者のJosé ValimはRailsの主要コントリビューターでした)。メソッドチェーンなどの関数型の概念を表現するのが簡単でした。それを簡単にしてくれるシンタックスシュガー、たとえばパイプ演算子(|>)が気に入りました。

foo(bar(baz(new_function(other_function()))))
# becomes
other_function() |> new_function() |> baz() |> bar() |> foo()

マクロ、つまりコードを書くコードを扱うのも初めてでした。Elixirのプログラムは、それ自体が有効なElixirコードであるASTとして表現できるので、別の有効なコードを生成するコードを書くのは簡単です。これはとてもクールな概念で、Elixirはそれを簡単にしてくれました。関数が引数の形に対してパターンマッチでき、関数呼び出しを適切な実装に振り分けられるのも気に入りました。

defmodule TuplePrinter do
  def print({a}) do
	IO.puts("single")
	IO.puts(a)
  end

  def print({a, b}) do
	IO.puts("double")
	IO.puts(a)
	IO.puts(b)
  end
end

TuplePrinter.print({1})
TuplePrinter.print({2, 2})

# single
# 1
# double
# 2
# 2

こういう機能は、素晴らしいか、コードをスパゲッティ状態にするかのどちらかになりがちです。いずれにせよ、クールな概念でした!

ElixirはErlangのBEAM仮想マシン上で動作するという利点もあり、やり取りできる大きなエコシステムを持っています。並行処理が得意で、愛されているPhoenixウェブフレームワークの中核でもあります。

すぐにElixirを使う必要があるわけではありませんが、扱っていてとても楽しく、また戻ってきてもいいと思えるものでした。さらに、見慣れた問題を慣れない方法で(つまり再帰的に)解くのは、面白い挑戦でした。

Mechanical March

3月は、マシンコードにコンパイルされる「システム」言語に焦点を当てていました。

選択肢の中で、私が興味を持ったのはGoだけでした。2 さて、鋭い読者はお気づきでしょうが、私はすでに1か月Goをやっていたので、繰り返しても12には数えられません。1月にGoを選んだ時点では、テーマ制がまだ発表されていなかったので、自分で自分の逃げ道を塞いでいることに気づいていませんでした。

Bunが登場すると知っていたら、おそらくZigを試していたでしょうが、あいにく(まだ)未来は見えませんでした。そこで、これ以上魅力的な選択肢もないので、年内のどこかで1つ分を埋め合わせる必要があると承知のうえで、Goをもう1か月やることにしました。

Analytical April

4月は、データサイエンスで人気の言語がテーマでした。Pythonはよく知りすぎていて、Rは大学の統計の授業でやっていて(好きではありませんでした)、というわけでJuliaに決まりです!

使っていて楽しかったのですが、それは主にPythonにとても似ていると感じられたからです。少し落ち着かない感じでした。カナダにいるアメリカ人のような気分です。すべてがとても見慣れているのに、言葉にしづらい形でほんの_少し_ずれています。突然、誰かが2ドル硬貨(あるいは行列計算に_とても_適した関数)を差し出してきて、もうカンザスにはいないのだと気づくわけです。

私が最も印象に残ったのは、Juliaの型システムです。Pythonの型システムのように(任意で)注釈を付けられますが、引数が宣言された型に合っているかを確認する実行時チェックがありました。Pythonのシステムは、ツールと連携しつつ邪魔をしないという点でちょうど良いバランスだと思いますが、型の合わない関数に対するJuliaの実行時エラーも役に立ったのは認めます。

結局のところJuliaは素敵でしたが、将来必要になるとは思っていません。

Mindshifting May

5月は「何か新しいことを試す」をさらに推し進め、とても変わったことをする言語を取り上げました。この機会に、大人気のRustを試してみました。言わせてもらえば、その熱狂がよくわかります。

悪名高い借用チェッカーに慣れるのには確かに時間がかかりますが、それによって自分のプログラムをより注意深く考えるようになる点が気に入りました。コンパイラーは確かに厳格でしたが、エラーメッセージは問題を修正するのにこれ以上ないほど助けてくれました。最初の1週間で特に生産的だったとは言いませんが、少なくとも学習曲線の頂上は見えた気がします。

Rustのパッケージマネージャーであるcargoにも特筆すべき点があります。サードパーティのパッケージは一切インストールしませんでしたが、ビルド、テスト、フォーマットの機能は素晴らしいものでした。Rustのような静的型付け言語に期待する便利機能をすべて備えたVSCode拡張も同様です。エンジニア体験の良さは、やはりすべてを左右します。

実装レベルでは大きく異なりますが、私が使う目的、つまりプログラムをとても速く動かすという点では、RustはGoと似ていると感じました。私が普段使っている言語の多くのツールが、そのパフォーマンス特性のためにRustへ移行し始めているので、今後もっと見かけるようになると思います(私自身がRustを書くことはなくても)。

Summer of Sexps(6月)

6月はS式の月でした。S式はlispで一般的な構文形式です。私はJVM上で動作する関数型言語、Clojureを選びました。

何年も前にClojureを少し書いたことがありました。学校を出たばかりで、最初の仕事で業務上重要な日次スクリプトの唯一の保守担当になりました。言うまでもなく、それは大変な時期でした。年を重ねて賢くなった今なら、もう少し親しみやすいものになっているか知りたく思いました。

嬉しいことに、そのとおりでした! 2月の関数型の経験が再帰的に考える助けになり、実際に取り組んでみると構文もそれほど悪くありませんでした。より大きなプロジェクトで使うなら、JVMとの相互運用性も役に立つでしょう。

代替手段があるのにClojureを何かに使う自分は想像できませんが、まったく不快な経験だったわけではありません。

サイドクエスト:Universal Test Runner!

何年もの間、カレントディレクトリでユニットテストを実行するために小さなbash関数を使っていました。こうした新しい言語で作業するうちに、便利さのためにこの関数に行を追加するようになっていました。tを実行すると覚えておくほうが、言語ごとのテストコマンドを何度も覚え直すよりずっと簡単だったのです。

必要とされるロジックが私のbashの扱いやすさの範囲を超えてきたので、6月に少し時間を取って、このプロジェクトを独立したものにしました。それがUniversal Test Runnerです。

Exercismのフォーラムで共有したところ、良い反応をもらいました。とても気に入ってもらえたので、Exercism CLI自体に同様の機能を組み込むことになりました(CLIはGoで書かれており、ちょうど復習したばかりの話題でした)。そのため、年の後半にはexercism testを実行してその月の言語のテストスイートを走らせることができました(このコマンドはUniversal Test Runnerでネイティブにサポートされています)。

この過程についてもっと知りたい方は、ローンチ時にはるかに詳しく書いています。

とにかく、次に進みましょう!

Jurassic July

7月は古い言語を紹介しました。実用性という点では、今月はかなり選択肢が乏しいものでした。多くの重要なインフラが今もCOBOLで動いていると聞いたので、由緒あるCOBOLから始めました。しかし、8月初旬の結婚式が迫っており、私にとってこれほど大きく違う言語をじっくり学ぶ余裕がありませんでした。そこで代わりに、最もマシに見える選択肢としてVisual Basicに切り替えました。

ここで言うことはあまりありません。この言語は少し冗長に思えましたが、使うのは十分簡単でした。私の理解では、これはWindowsでのUI開発のために設計されたものなので、小さな演習をやっていると全体像をつかみにくくなります。

Appy August

8月は、アプリを作る言語であふれていました。驚くことではありませんが、今月は選択肢がたくさんありました。私はSwiftにしました。Apple製品をたくさん使う者として、同社が独自に作った言語はとても身近に感じられます。まったくの初心者ではありませんでした。2016年にSwiftだけで書いた1つのiOSアプリを公開したことがあります。しかしそれ以降この言語には触れておらず、大きく進化しているので、まだカウントされるだろうと考えました。

扱いやすさに嬉しい驚きを覚えました。ここで取り上げた他の多くの言語とは対照的に、Swiftはかなり新しいです。2014年に最初にリリースされ、現代の言語設計の教訓から明らかに恩恵を受けています。ファーストパーティのパッケージマネージャー、オプショナルチェーン、第一級関数、そしてまともな文字列補間を備えています。Xcodeを使わなくても、読み書きしやすいと感じられました。

とはいえ、Swiftは主にAppleプラットフォーム向けアプリの文脈で役立つもので、私は現在そうしたものは書いていません。演習にはよく合っていましたが、すぐにまた戻ってくることはないと思います。とはいえ、iPadで書けるのは気に入っています!

Slimline September

9月は、とても簡潔な言語や小さな言語を探求しました。私はjqにしました。何年も使って愛用しているツールです。

ただ、私はずっとこれをJSONを扱うための単なるツールだと思っていて、汎用プログラミング言語だとは考えていませんでした。関数、変数、ループなど、通常の装備がすべて揃っているのを見て嬉しい驚きを覚えました。おかげでかなり複雑なプログラムも書けます。

# input: { "series": "1", "sliceLength": 1 }
. as {series: $series, sliceLength: $sliceLength} |
if
  $series == "" then
	"series cannot be empty" | halt_error
  elif $sliceLength > ($series | length) then
	"slice length cannot be greater than series length" | halt_error
  elif $sliceLength == 0 then
	"slice length cannot be zero" | halt_error
  elif $sliceLength < 0 then
	"slice length cannot be negative" | halt_error
  else
	.
end
| [range(0; $series | length)]
| map($series[. : . + $sliceLength])
| map(select(. | length == $sliceLength))

単純なデータ変換では必要になったことのないjqの機能をすべて試すのは楽しかったです。ここでのツール類はやや乏しいものでしたが(エディター連携がないなど)、jqの機能の幅をより深く理解できたのは価値がありました。

追記: MastodonのDJ Adamsがjq-lspプロジェクトとそれに対応するVSCodeプラグインを教えてくれました。今回は乗り遅れてしまいましたが、今後チェックしてみます。

Object Oriented October

10月はオブジェクト指向言語を掘り下げました。私はオブジェクト指向設計に思い入れがあります。それは私が頭の中でプログラムを思い描く方法をよく反映しているからです。私はRubyにしました。奇妙な選択に聞こえるかもしれません。

私は世界最大のRubyコードベースがあるStripeで働いています。まさか「馴染みのない」言語には数えられない、なんてことはありませんよね? それはすべて事実ですが、社内のRubyモノリスは「標準的な」Rubyとはかけ離れているように感じられます。すべてがSorbetで型チェックされ、コード生成が_大量に_あり、すべてを連携させてスケールさせるために多くの魔法を使っています。Stripeの内外のRubyは結局同じ言語ですが、これほど異なる規模で作業すると体験は大きく変わります。外の世界がどんなものか(Rubyをヘビーに使っていた頃から数年経った今)知りたかったのです。

おおむね、良かったです! Ruby自体は素晴らしく、「プログラマーの幸福」を主要な目標に掲げているのが私には響きました。使ったことのない標準ライブラリの関数の名前を、よく当てられるのが気に入っています。関数型のコードを組み立てるのが簡単で、構文が使いやすく表現豊かなのが気に入っています。

とはいえ、エンジニア向けのツールがPythonにこれほど後れを取っているのには驚きました。自分が甘やかされてきただけかもしれませんが、エディター内の型ヒントや、非常に高速なリンティングとフォーマットが、思っていた以上に私にとって重要なのだと気づきました。ピーク時にあれほど人気だった言語にしては、その点でかなり後れを取っていると感じたのは意外でした。3 関数呼び出しで括弧が任意なのにも、最後までしっくりきませんでした。そのせいで、関数を引数として渡すのがやや分かりにくくなっていました。

Rubyは今も素晴らしい言語で、仕事では使い続けますが、少なくとも今のところ、PythonにできてRubyにできないことはありません。

Nibbly November

11月はこれまでで最も難しい月、アセンブリ言語でした。手書きで書くことはもはや一般的ではありませんが、親しんでおくと役に立ち、面白い話題です。私は、現在と未来のウェブにとっての重要性からWebAssemblyを選びました。通常はコンパイル先として使われるもので(手で書くものではありません)、そういう物好き向けのツールも存在します。

今月は思いがけずよく準備ができていると感じました。構文はClojureのように感じられ、言語構造はZachtronicsのTIS-100のように感じられました。あらゆる操作を一から始めなければならないのが、妙に楽しく、古風に感じられました。実際にこれをこの方法でやり遂げなければならないとしたら嫌でしょうが、それまでの間は楽しい珍品でした。惜しみなくコメントを付ければ、ほぼ読めるものを書けました。

(module
  (func (export "eggCount") (param $number i32) (result i32)
	(local $res i32) ;; result
	(local $remainder i32) ;; loop counter

	(loop $loop

  	;; $res =
  	(local.set $res
    	;; $res +
    	(i32.add
      	(local.get $res)
      	;; $number % 2
      	(i32.rem_u
        	(local.get $number)
        	(i32.const 2)
      	)
    	)
  	)

  	;; $number //= 2
  	;; (keep on stack)
  	(local.tee $number
    	(i32.div_u
      	(local.get $number)
      	(i32.const 2)
    	)
  	)

  	;; this will keep looping until remainder is 0
  	br_if $loop
	)

	local.get $res
  )
)

最大の障害は、ドキュメントやリソースの不足でした。どのグローバル関数が使えるのかさえ分かりにくいものでした。ただ、実際にこれを使うわけではないので、乗ってしまえばそれほど気にはなりませんでした。

12月は、他のカテゴリに当てはまらない言語でこの年を締めくくりました。3月で1つ分を重ねたため、今月は2つの言語を完了する必要がありました。

私はWrenから始めました。他の何よりも『Crafting Interpreters』で知られるBob Nystromが作った言語です。細部へのこだわり、小さなフットプリント、トップダウンの設計に心を惹かれました。すべてが非常によく考え抜かれているように見えます。その丁寧さは、変数のスコープやプライバシーのルールの細部に表れています。コンパイラーは小さく、大量のコメントが付いているので、言語実装に興味があるなら優れた学習リソースになります。

Wrenは少し粗削りで、ほとんど放棄されているようですが、おもちゃの言語としてはそれで十分だと思います。これに本番環境での使用を期待して入ってくる人はいません。非本番用の言語の居場所は、世の中に確かにあるのです。

もう1つ:Lua

今月の2つ目の選択はLuaでした。Wrenとは対照的に、信じられないほど実用的です。簡単に埋め込めるため、RedisのスクリプトやFactorioのMODなど、多くの場所で使われています。オブジェクトモデルには少し慣れが必要でしたが、すぐに生産的になれるだろうと感じました。何でもできる構造としてのテーブルには、すぐに愛着が湧きました。ツール類も良好でした。パッケージマネージャーはすぐに動作し、VSCode拡張はコメントベースの型注釈を面倒なくサポートしてくれました。

すぐにLuaが必要なものがあるわけではありませんが、広く使われているため、道具箱に入れておくべきもう1つの優れたツールです。

まとめ

この言語の旅は、思っていた以上に楽しめました。新しい実用的なスキルをいくつか身につけただけでなく、視野がすっかり広がった気がします。

次に何をするかといえば、Rustをもっとたくさん学ぶことだと思います。エンジニア向けツールの世界におけるその重要性は今や明らかで、自分が頼りにしているものたちを読み、貢献できるようになっておきたいのです。

具体的な成果物を考えているわけではありませんが、Rustの本を一冊読むこと、1年前に経費で落としたRust for JS devsコース、そしてExercismのトラックを丸ごと1つ終えることがあります。少なくとも1つのオープンソースプロジェクト(おそらく私の新たなお気に入りのプログラムであるJust)に貢献したいと思っていますが、この先1年がどうなるか見てみましょう。

それでは、楽しい休暇を。そして2023年の残りも良い時間をお過ごしください!

  1. 未使用の変数がコンパイルエラーになるって? まったく、勘弁してほしい ↩

  2. 実は最初にC++を試しました(大学以来書いていませんでした)。ただ楽しくなくて、やめてしまいました ↩

  3. これも、「実際の」RubyがStripe内での私の経験と異なる点の1つです。両方を試せてよかったと思います ↩

2024年01月05日(金) · 役に立ちましたか?