ChatGPTをプログラミング言語開発に役立てる
久し振りの更新です。巷では先日リリースされたばかりのGPT-4oの話題でもちきりですが、私も当日深夜2時のライブストリーミングを見てその後すぐにGPT-4oを試しています。性能に関する雑感としては
- 全般的にはGPT-4-Turboより頭が良い
- Claude 3 Opusと比較すると、お堅い & 無難な回答を返す傾向あり
- ただし、Opusよりハルシネーションは起きにくい印象
- 画像認識の性能が凄い
辺りでしょうか。特に最後の点は特筆すべきことで、GPT-4-Turboの画像認識よりだいぶ性能が向上したおかげで今までだとやりにくかったことも簡単にできるようになっています。その際たるものが先日バズった
GPT-4oの画像認識力と理解力をもってすればいけるやろと思ってやってみたら実際いけた。
— kmizu (@kmizu) 2024年5月14日
ペーパープロトタイピングから最初のHTML書き起こすのにかなり使えるのでは。
つーか指示そのものを画像の中に書いたの読み取ってくれるの何か世界の壁を超えて対話してる感があって凄い#GPT4o pic.twitter.com/3XHMFg3yye
でしょうか。とはいえ、今回の本題は画像認識機能でなくタイトルの通り、プログラミング言語開発にGPT-4o(の画像認識部分でない部分)を役立てるというものです。
ちょくちょく停滞気味なのが玉に瑕ですが、私は現在日本語プログラミング言語「ぬこ」を開発しています。
特徴としては
- ある程度日本語ぽく書ける
もし(...) x でなければ y
など
- Hindley-Milner型推論
- 関数型プログラミング言語
- 不変データ構造、高階関数のサポート
などが挙げられます。さて、プログラミング教育をやるようになってここ数年、漠然と考えていたことがありました。それは抽象構文木の概念をどうやって初心者に教えるかということです。実際、Scratchは抽象構文木を割と素直に視覚化したプログラミング言語および開発環境ともいえ、知られているようにプログラミング教育で大きな成果を収めています。
しかし、一方でScratchである程度「プログラム」が書けるようになっても、そこから先のテキストベース言語に移行する際に大きなギャップがあり、ここでつまづく人も少なくありません。
そんなこんなで色々考えを巡らせていましたが、今夜、ふと「プログラミング言語内でvisualize(1 + 2)のようにしたら、グラフィカルな形でASTを表示してくれればいいんでは」という(誰でも思いつきそうな)着想が浮かびました。実際、Nimではdumptreeという機能があるそうです。
ただ、調べてみた限り、dumptreeはアスキーアート風味に木構造を表示する代物のようで、惜しいけど求めてるものとはちょっと違うという感じでした。
ともあれ自作のプログラミング言語なら自分で作りこむしかないのですが、最初思い浮かんだのはJavaで書かれた木構造(かグラフ構造)描画ライブラリを使うことでした。しかし、調べてみると木構造の方は実用に耐えるものがあまりなさそうでしたし、グラフ構造描画ライブラリを使って木構造を描画するのは多少手間がかかります。
なんとかいいアイデアがないものかと考えていたのですが、ふと「JTreeを使って、素直にNukoの抽象構文木をビジュアライズすればいいんじゃね?」と思い至りました。というわけでChatGPTの出番です。
こんな感じのリクエストを出したら、見事にASTを受け取ってSwingのJTreeとして表示するライブラリができました。それを早速Nuko言語に組み込んでみた結果が以下です。
可視化した結果はまだ粗削りですが、これは手で修正してもよし、さらにChatGPTに依頼してリッチな見た目にしてもらうことも容易でしょう。
驚くべきは、最初にアイデアを思いついてから、ChatGPTに質問して実際に組み込むまでわずか1時間で完了したことです。少なくとも以前の私だったらここまで素早く思いついたアイデアを自作の言語に組み込むことはできなかったでしょう。ChatGPT様!といった感じです。
ChatGPT (GPT-4o) でなくても、Claude 3 OpusやGemini 1.5Pro、GPT-4-Turboでもできそうなことではあるのですが、以前に比べて一発でほぼ動くコードが出る確率が高くなったように感じます。
そんなこんなで、ChatGPTにコード生成させると便利な例の紹介でした。
Osaka-City.Mokumoku Vol.1を公開しました!(5/15(水))
大阪に引っ越してくる前はKyoto.Mokumokuの方で集まってもくもく会をやっていたのですが、自分の住まいから京都市内まではやや遠いので通いづらくなったこともあり「大阪市内でもくもく会やりたい!」と思い、サクッとイベント立ち上げてみました。場所はJR大阪環状線駅の「西九条」駅徒歩2分程度の駅近の貸し会議室です。ちなみに、Kyoto.Mokumokuの運営は既に京都市内にお住まいの方にお譲りしてる感じです。
osaka-city-mokumoku.connpass.com
大阪市内あるいは近郊にお住まいの方で、対面でもくもく会やりたい!とか、懇親会でどうでもいいことを駄弁りたいという方が居られましたら、参加ご検討いただけると嬉しいです。貸し会議室を借りる都合上、参加費500円になってしまったのは心苦しいのですが……。 もくもく会なので、本当に気軽に参加していただければーと思います。あるいは身近にそういう方が居られましたら宣伝いただけると嬉しいです。
おまけ:Claude 3 Opusによる添削結果
以下、添削した文章です。修正点には下線を引いています。 大阪に引っ越してくる前はKyoto.Mokumokuの方で集まってもくもく会をやっていたのですが、自分の住まいから京都市内まではやや遠いので通いづらくなったこともあり、「大阪市内でもくもく会をやりたい!」と思い、サクッとイベントを立ち上げてみました。場所はJR大阪環状線の「西九条」駅から徒歩2分程度の駅近の貸し会議室です。ちなみに、Kyoto.Mokumokuの運営は既に京都市内にお住まいの方にお譲りしている感じです。 [https://osaka-city-mokumoku.connpass.com/event/318116/:embed:cite] 大阪市内あるいは近郊にお住まいの方で、対面でもくもく会をやりたい!とか、懇親会でどうでもいいことを駄弁りたいという方がおられましたら、参加をご検討いただけると嬉しいです。貸し会議室を借りる都合上、参加費500円になってしまったのは心苦しいのですが……。 もくもく会なので、本当に気軽に参加していただければと思います。あるいは身近にそういう方がおられましたら、宣伝していただけると嬉しいです。 主な修正点: 「やりたい」の後に「を」を追加し、文法的に正しい文章にしました。 「居られましたら」を「おられましたら」に修正し、敬語表現を統一しました。 「参加ご検討」を「参加をご検討」に修正し、自然な日本語の表現にしました。 「参加していただければーと」の「ー」を削除し、文章を簡潔にしました。 「居られましたら」を「おられましたら」に修正し、敬語表現を統一しました。 全体的に、文章は良くまとまっており、伝えたい内容が明確に表現されています。細かな文法や表現の修正を行いましたが、大きな変更は必要ありませんでした。
技術イベント「Nextbeat Tech Bar:第一回ライブラリ開発について考える会」を5月24日(金)に開催します!
プログラマーの皆さんは日々(?)ライブラリを作成したり、あるいは使っていたりするのではないかと思います。しかし、ライブラリ開発やメンテナンスには様々な苦難が伴います。たとえば、継続してメンテナンスするための労力の捻出、バージョンアップの時の互換性の確保、ドキュメントの整備・更新など色々な苦労があるかと思います。ライブラリ開発には様々な苦悩が伴うにも関わらず、これまでそのようなライブラリのメンテナンスについての知見はあまり共有されていなかったように思います。というわけで、5月24(金)19:00-21:00に以下のような技術イベントを開催することにしました。
ライブラリ開発に苦悩する方々、あるいはライブラリを開発してみたいけど、どうやって開発すればいいのかわからない、など様々なソフトウェアエンジニアの方々の参加を歓迎したいと思います。ハイブリッド開催ですので東京在住でない方はリモート参加もできます(筆者は当日に大阪から新幹線で移動します)。イベントを盛り上げていきたいので、様々な方の参加・発表をお待ちしております!
「プログラミング言語開発」教育用言語MinisにJSONベースの具象構文を付け足してみた
皆様、お久しぶりです。去る2月10日(土)、2月11日(日)に筑波大学情報科学類にて特別講義の講師をやってきました。といっても、私が全日担当したわけではなくOB一人が一コマを自分の得意分野について講義をするオムニバス形式のものです。
私はといえば去年やったのと同様、JavaScriptで抽象構文木を「手で」組み立てて解釈・実行するプログラミング言語Minisとその処理系を作るという講義を行いました。講義当日はスライドにミスがあることに途中で気づいたり色々あってテンパりましたがそれはそれとして。
元々、私が担当した「プログラミング言語作成概論」の趣旨は
プログラミング言語を作るというのはとても簡単な作業なのに、プログラマにすらあまり知られていないのはけしからん。 とはいえ、実際に作ってみせないと実感が湧かないのが人情。 抽象構文木をJavaScript上で組み立てて、それをevalする関数を書くことを通してインタプリタの作成方法を学ぼうじゃないか!
的なものです。たとえば、以下のプログラムは講義で作ったプログラミング言語Minisのもの(実際にはこれそのものを解析する構文解析機はまだ作ってませんが)です。MinisのことはわからなくてもC系言語を一つかじったことがあればおおよその意味はわかるかと思います。
a = 100; b = 200; if(a < b) { 500; } else { 1000; } // 500が返ってくる
しかし、これくらい単純な構文を持ったプログラミング言語でさえ「構文解析」というプログラミング言語にとって本質でないものが挟まるせいで無駄に行数が増えるのが現実です。ならばということで、以下のように直接抽象構文木を書かせれば(ナイーヴな)インタプリタの本質であるevalの記述に専念できるじゃあないかというのが講義を通して伝えたかったことでした。
const expression = tSeq( tAssign('a', tInt(100)), tAssign('b', tInt(200)), tIf(tLt(tId('a'), tId('b')), tInt(500), tInt(1000)), ); // 上のプログラムをJSの関数呼び出しを通して構築したもの expect(evaluate(expression, {})).toBe(500);
さて、多少プログラミング言語に心得のある皆様ならおわかりのようにこのアプローチは問題なく機能しますが、ただ、せっかくプログラミング言語を作ったのだから構文も設計したいのが人情です。比喩でいえば GUIかCUIかはプログラムの本質ではないけど、GUIの方が結果がわかりやすくていいよねみたいな。
というわけで今年は具象構文も追加することにしたのですが、しかし、構文解析の話をするには時間もありませんし、概念を理解するのも結構手間です(難しいものではないですが)。というわけで間をとってJSONでMinisの構文を書けるようにしたのでした。たとえば、上記のプログラムをMinisのJSON形式で記述すると以下のようになります:
{ "type": "seq", "expressions": [ {"type": "assign", "name": "a", "value": 100}, {"type": "assign", "name": "b", "value": 200}, {"type": "if", "condition": {"type": "<", "operands": [{"type": "id", "name": "a"}, {"type": "id", "name": "b"}]}, "then": 500, "else": 1000 } }
しかし、見ればわかる通り、元の抽象構文木をそのまま書き下したときより見づらくなってしまっています。
このJSON形式の構文を書き下したのは講義よりしばらく前でしたが、これだとちっとも嬉しくない……というわけで、JSONはJSONでももっと簡潔に書けるように考え直しました。
上記のJSON形式の問題点はすべての式をJSONのオブジェクト(あるいは値)として表現してしまっているせいで、必ずラベルが必要になる点です。しかし、よくよく考えればJSONを使うからといって別にオブジェクトを使う必要はないわけで、以下のように配列形式でいいわけです。
[ ["assign", "a", 100], ["assign", "b", 200], ["if", ["<", ["id", a"], ["id", "b"]] 500, 1000]]
かなりMinisのプログラムを簡潔に書けるようになりました。と、ここで「それってほとんどS式では?」というツッコミが聞こえてきそうですが、概ねその通りです。強いていうとS式と違ってJSONにはシンボルがプリミティブにないために、余計なダブルクオートが必要になる程度でしょうか。
ともあれこのようにしてJSONでプログラムを表現してあげれば、後は簡単。JSON.parse(str)
で入力文字列をJSONとしてパーズした後は、抽象構文木に変換するロジックさえ書いてあげれば既存のMini言語に具象構文を与えることができます。
プログラミング言語Minisはナイーヴなインタプリタや上記のJSON構文を解釈できるものも含め、以下のリポジトリからソースを取得することができます。もし興味が湧いたら触っていただければと思います。単純な言語とはいえどこかにバグがある気もするので、バグ報告も歓迎します。ではでは。
ネット小説を書き始めて三年以上経って気づいたこと:鯛粗は重要
元々、幼少期から私は全くもって特に小説家あるいはクリエイター志望ではありませんでした(一貫してたのは学究路線の方)。その一方で、年齢を重ねるに連れて、色々な娯楽を読み耽るほどに「なんでこの終わり方なんだよ!」とか「ここでこのキャラを退場させなくていいだろ」とか「せっかく仲良し方向の路線だと思ってたのに、どうしてギスギスに……」という思いが募っていく一方でした。
そんなとき、ふと、ネット小説家界隈で言われたことの一つを思い出したのでした。要旨だけを抜きだすと「じゃあその君のモチベーションを軸にして実際に、自分が納得の行く物語を作ってみなよ。もしそれが読者の心に響いたのならきっと読まれるよ」というものです。
そういう言葉を聞いてやってみるか、やってみないかは当然個人の自由ですけけども、私自身はネット小説「論評」界隈の「読んだことも、書いたこともなく」、どうせなろうってこうでしょうっと論評する一部……でない人たちの不誠実さは好ましく思っていませんでしたから、正当な論評をするためにも、もちろん、自分が好きな物語を作るためにも小説を書き始めてみようと一念発起してアカウントを作成したのが、確か2019年の夏頃でしょうか。
ちなみに、ネット小説といってもなろう一強の時代はとうに過ぎており、後発のカクヨムはある意味で既になろうを追い越している部分もありますし。他にもアルファポリスなどメジャーな小説投稿サイトはあります。
さて「とりあえず書くか」とジャンルを決めてなろうに連載を始めたわけですが、読者がとにかく来ない、来ない……。PVを見ると全く読まれてないわけでもないようだけど……みたいな状態が約半年続きました。
さらに処女作はあまりにもリアリティを重視しすぎてしまったが故に途中で私のほうでスクラップにすることになってしまいました。作者になってみるとわかるんですが「キャラクターが動いてくれない」って心理的に本当につらい。この初作での苦い失敗経験はそのリバイバル版というべき作品でだいぶ改良されることになるのですが、それはともかく。
処女作はそんな感じで無惨に終わりを迎えましたが、その次に思いつきで書いた数十話程度の長編(中編?)小説は一部の熱心な読者が毎話コメントつけてくださったおかげで、無事に主人公たちにきちんとハッピエンドを迎えさせてあげられました。今でもその読者さんは私の作品に「いいね」を入れてくれたり、場合によって感想書いてくださるのですが、彼(彼女?)のコメントがなかったら挫折してただろうなーと思うと思い出深いものです。
こうして(心情的な意味での)処女作はなんとか無事ハッピエンドを迎えることができました(カクヨムで★160以上、最終話PVが1217なので、心情的な処女作としてはなかなかものかもしれません)。ちなみに最終話PVは最終話「だけ」読んだ人がカウントされる可能性があるので注意ですが、最終話直前あたりで1154PVなことと他の話数でのPVを考えると、1000名近い読者の方が読んでくれたことになります。
ネット小説の世界というのは消費が非常にはげしいので「流し読みしただけの人」たとえば、1話1分でほんとうに「あらすじ」だけ見るような大味な読み方をした人もカウントされることの留意しないといけませんが、ともあれまあまあだったのではないかなと。
心情的な意味での処女作はまずまず成功しましたが、そうすると次も書いてみたくなるのも人情というもの。詳細を書くと色々バレそうなのですが、次ではもうちょっと思春期っぽい話を70話超で描いてみることにしました。ヒロインの特徴とか描いちゃうとこれまた割と簡単に検索でヒットしそうなのでぼやかしておくと「方言」が一つのキーワードと言えましょうか。
こちらの作品はまた、前とは別の、すっっっごく熱心な読者さん(+前作でついた読者さん)がどんどん応援コメントや応援レビューくださったこともあって、ラスト数話は睡眠時間を削って完成にこぎつけました。こちらはカクヨムで★230以上、最終話PVが1200くらいでしたが、読者さんの熱量という意味では(SNSからの反応も一部に)圧倒的にこちらが上でだいぶ自信がついたものでした。
応援コメントで「続きが読みたいです」が割と真剣に多かったので実際に後日談を別作品で作ってしまったくらいです……。
ともあれ、そうしてだらだらと創作活動を続けて三年以上になります。
長編も10話以上完結させて、短編も多数生産して(短編は生産数が多すぎて、他の作者には見られない異常な数値を叩き出しているので、書くとやはりわかりそうなので控えます)。カクヨムでは★100くらいは普通に(下記始めの頃は、★100もらえるくらいの作品を書くのが最初の目標とも言えました)、なろうの短編でもジャンル別日間ランキング5位以内にはうまくやれれば(1位に入ったことも)入れるくらいには書き慣れてきたのかなと思うところです。
正味の固定読者数は推計ですが、なろうの方で少なくとも約300(逆お気に入りの数から推計)、カクヨムの方はもうちょっと感覚的ですが200~300名くらいはいそうな感じです。そんな中、気づいたことがあるのでちょっと書き留めておこうかなと思ったのでした。
たとえば、ネット小説の、特になろうでの小説執筆が普通の小説執筆と違うところに「ランキングバトル」という側面があることです。「小説家になろう」では読者が読んだ小説についてポイント形式で投票できるのですが、それにもどついて「総合」「各ジャンル別」について1~100位以内のランキングを一日三回更新されます。そのランキングで上位に上がれば読者の目につきやすくなり、つまるところより読んでもらいやすくなり、ますます評価が得られやすいという好循環が生まれるわけです。私も一時期、短編小説でいかにして日間ランキング5位以内に入るかの手法を考えるのに腐心していた時期があり、ある意味射幸性のあるギャンブルチックな側面ともいえるかもしれません。
また、ネット小説は特に供給量が豊富な故に、タイトルとあらすじの付け方がまずいと、いくら本文が良かろうが致命的に読まれないということも重要です。仮に恋愛小説を書くとして「僕と彼女のヒトナツの恋」などという平凡なんだかちょっと気取ったのかわからないような題名は言語道断と言っても過言ではありません。
何故かというと、読者は皆忙しいので、本文を読むべきか切るべきかをできるだけさっさと判断したいからす。半分過ぎてから、これじゃないんだよなと思って時間を無駄にしたくないのです。「僕と彼女のヒトナツの恋」というけど、彼女は病気でこの世を去るのか?それとも、単純に夏に出会った相手との恋なのか?などなど、男性向け恋愛小説について読者が特に知りたいヒロインについての情報量がほとんどこのタイトルが含まれていません。タイパ世代という言葉が頭をよぎりましたが、データ上は世代を問わずに見受けられるようなので、まー人間、娯楽が溢れればそうなるさってことかもしれません。
いい「タイトル」というのも曲者でして、なろうだけでなくカクヨムでも、結局「タイトルだけでおおまかな内容を連想できるくらいには長くなっていないといけない」という原則は基本的に変わっていません(長文がmustなのではなく内容が連想可能がmustなのがポイントです。これを利用すれば短いタイトルでもうまくハマるのも作ることはできます)。ここで「美しいタイトル」をつけたいと思う、普通の作者の美意識や願望と「いやでも読まれないと意味ないしな」という現実主義との葛藤が生まれるのです。これを読んでる読者の方々、なろうなどの小説における長文タイトルは別に作者が好き好んでそうしたわけではなくて、そうしないと小説が生き残れない、より正確には読まれずに放置されるだけになるという前提条件があるのだと思えば、少しは理解できるのではないでしょうか。それでもなお、私には葛藤が残りますが。
あとは「あらすじ」もですね。ここでの「あらすじ」は本来の意味でのあらすじとは違うのが曲者で、いわゆる煽り文の類である必要があります。リアル書店に並ぶ書籍は、編集さんが帯のコメントなど色々考えてくださるわけですが、ネット小説家だとこの部分も当然ながら全部自分でやる必要があるというわけです。この「タイトル」と「あらすじ」が重要というのは、ある意味多くの読者に読んでもらうときの記事の書き方の基本みたいなものであって、小説を書き始めてから以前と違う観点で文章を意識するようになった……気がします。
他にも、なろうのランキングで上位に入ると途端に荒らし感想が増えてくるとか、カクヨムはなろうに比べればだいぶ平和だなとか他にもありますが、今日はとりあえずタイトルとあらすじが重要ということで。
Scala 3で型レベル自然数をやってみたらすごく簡単になってた
Scala 3、正式リリースされてからそこそこ経ってますが皆さん使ってますか?実は自分は、主にIntellij IDEAのScala 3対応に不安もあってScala 2.13系列をずっと使い続けて来たのですが、最近はScala 3対応も進んできたようなので乗り換えを始めることにしました。で、手始めにということで型レベル自然数(型レベルペアノ数)を定義してみることにしました。
型レベル自然数は読んで字の如くで、型のレベルで自然数をエンコーディングしちゃおうって技です。依存型のある言語なら最初からあるので、わざわざ型レベル自然数とか大仰な言い方しなくてもいいのですが、それはそれとして論より証拠。早速、Scala 3で型レベル自然数を定義してみます。
enum Nat { case Zero case Succ[A <: Nat](v: A) }
定義はこれだけです。簡単ですね。Scala 2だと
sealed trait Nat object Nat { case object Zero extends Nat case class Succ[A <: Nat](V: A) extends Nat }
のように書かなければいけなかったのでとっても面倒でした。さてさて、とりあえずこれで型レベル自然数の定義はできたのですが、よく使う一桁の自然数は別名定義しておきたいですというわけで、次にこんなん書きます:
type _0 = Zero.type type _1 = Succ[_0] type _2 = Succ[_1] type _3 = Succ[_2] type _4 = Succ[_3] type _5 = Succ[_4] type _6 = Succ[_5] type _7 = Succ[_6] type _8 = Succ[_7] type _9 = Succ[_8]
この辺はScala 2時代と変わらず面倒くさいです(もしお手軽に型エイリアスを大量生成できる技知ってる人いれば教えてください)。で、この次が本題なのですが、型レベルで自然数を表すからには当然加算や減算などの演算を定義したいですよね。これがScala 3になってかなり楽になりました。たとえば、足し算を表す型コンストラクタPlus
は次のようにかけます。
type Plus[A <: Nat, B <: Nat] = (A, B) match { case (x, Zero.type) => x // 右辺が0なら左辺の型を返す case (x, Succ[y]) => Succ[Plus[x, y]] // そうでなければxとyをPlusした型をSuccする }
ここでは、
- 型の別名付け機能:
type Plus[...] = ...
- 型のパターンマッチ: 型の別名定義内で型へのパターンマッチをできる
という二つの機能を使っていますが、再帰的な足し算の定義をそのまま素直に書き下せています。Scala 2だと型パターンマッチも型の別名定義中の「型の再帰」も素直にかけませんでしたから、この快適さにビックリです!
続いて掛け算(Multiply
)を定義しましょう。掛け算は当然ながら足し算を使って定義できるので、以下のような感じでしょうか。
type Multiply[A <: Nat, B <: Nat] = (A, B) match { case (_, Zero.type) | (Zero.type, _) => Zero.type // 両辺の片方が0なら0 case (x, `_1`) | case (`_1`, x) => x // 両辺の片方が1なら1 case (x, Succ[z]) => Plus[x, Multiply[x,y]] // y >= 2のとき、x * y = x + (x * (y - 1)) }
この型レベル演算がうまく行っているか確かめるためにmainメソッドをつけてみましょう。Scala 3ではクラスがなくてmainメソッドだけのプログラムも許されるようになりました(明らかにKotlilnを意識した仕様変更ですね)。
def main(args: Array[String]): Unit = { val one: _4 = null.asInstanceOf[Plus[_2, _2]] val six: _6 = null.asInstanceOf[Multiply[_2, _3]] val nine: _9 = null.asInstanceOf[Multiply[_3, _3]] println("Hello") }
これらのプログラムにNat.scalaという名前をつけて保存し、
scala Nat.scala
としてみれば問題なくHelloが表示されます。さて、上記ではone, six, nineの型を正しく指定しましたが、次のように間違った型を指定するとどうなるでしょうか。
val wrong: _2 = null.asInstanceOf[Multiply[_4, _2]]
これを含めたプログラムをコンパイルすると次のようになります。
-- [E007] Type Mismatch Error: /home/mizushima/repo/scala-nats/Nat.scala:36:35 --------------------------------------------- 36 | val wrong: _2 = null.asInstanceOf[Multiply[_4, _2]] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | Found: Nat.Succ[Nat.Succ[Nat.Succ[Nat.Succ[Nat.Succ[_3]]]]] | Required: _2 | | longer explanation available when compiling with `-explain` 1 error found Errors encountered during compilation
要求されていた(Required)な型は_2
だけど、実際にはNat.Succ[Nat.Succ[Nat.Succ[Nat.Succ[Nat.Succ[_3]]]]]
= _8
だよというエラーですね。というわけでScala 3になってこういうお遊びもやりやすくなたのでした。
嫉妬心がほとんどないという話
すっごく個人的な話なんですが、生まれてからもうすぐで40年になろうという歳になって思うわけです。
「自分は嫉妬心ないなあ」と。
妬み嫉みは自分より遥か高みにいる人に対しては起きなくて、自分と拮抗する実力の持ち主との間で起こるともよく聞きますが、そういう相手に対しても
「まあ、その人なりに頑張ったんだろうし」
くらいにしか思わないです。男女関係だって、かつて好きだった異性が結婚した話を聞いたときも別段ショックを受けた覚えがありません。「まあ良かったよね」くらい?
そんな風にしてずっと生きてきたものだから、他者の嫉妬心や嫉妬してしまって自己嫌悪する気持ちがイマイチ理解できなかったりします。
30代は結構ストレスが多い10年という一面はありましたが、嫉妬心がほとんどないので、誰かと比べて境遇を嘆くことがなかったのは良い事だった……のかもしれません。実際、メンタル的に一番しんどい的ですら、楽しそうな様子の誰かを見て心がしんどくなる、なんてことはなかったですし。
オチはないんですが、実際問題、嫉妬心が(ほとんど)ない人ってどのくらいいるんでしょう?ググっても時々いるくらいには珍しくない存在みたいですが。