なんか考えてることとか

変な人が主にプログラミング関連で考えていることをまとめる。

「Python滅ぼす協会に入会したい」にマサカリを投げる協会に入会したい

バカの山

はじめに

これはPython滅ぼす協会に入会したいという記事に対して良くも悪くも話題になっているので、容赦なくマサカリをぶん投げさせていただく記事となっております。

「なぜ令和にもなって動的型付け言語を使うのか」へのツッコミ

まず細かいことを言うが、動的型付言語ではない。動的型付言語だ。型について語るわりには、型について不勉強である。もっと細かいことを言えば、動的型付き言語というのも見方によっては正しくない場合もある。

私個人としては、JavaScriptは動きのあるWebページを作るのに必須という事情があることを考慮してもPythonよりも酷い。なぜよりにもよってJavaScriptを引き合いに出したのか。

たとえば、Pythonでは(+)演算子(int, str)に適用すると実行時にエラーになる。JavaScriptではエラーにならず、暗黙の型変換を行い、無理やり正しい結果にする。まぁここは私がJavaScriptで特に嫌いなところ、というだけの話なのだが。

1 + 'a'     # -> TypeError ←まだわかる

1 + "a";    // -> "1a" ←!?!?!?

また、Pythonは確かに実行時にしか型をチェックしてくれないが、実行前に静的型チェッカーであるmypyを使う手もある。これを無視して「TypeScriptがあるからまだよい」と結論付けるのはいかがなものか。

それはそれとして、私はどちらかと言えば静的型寄りの人間であり、動的型付き言語は実際に実行しないと、型が間違っていることすらわからないし、実行前にチェックするぐらいなら、最初から静的型検査してくれたほうが助かるので、そこだけは共感できる。

シンタックスもキモい」へのツッコミ

ただHaskellという名前を出したかっただけだろ

まず、mapだ。配列に対して操作を加えていきたいのに、記述位置を戻る必要がある。
戻らずに書き下せるように、適用順を変える演算子だとかモナドだとかHaskellの爪の垢を煎じて飲ませたい。

まず、初めに言いたい。なぜこの文脈でモナドやらHaskellやらが話に出てくるのだろう。申し訳ないが、ここまで来るともはや意味がわからない。

ちなみに、適用順を変えるという話についてだが、Haskellの場合、関数はすべてカリー化されているので、工夫次第ではそういうことも可能だ(ただ、Haskellでこういう書き方をする人はあまりいないと思うが・・・)。

fmap' :: Functor f => f a -> (a -> b) -> f b
fmap' = flip fmap

main :: IO ()
main = print $ someList `fmap'` \x -> y     -- someList, yは自由変数

これも、Haskellを引き合いに出したのは間違いではないかと思ってしまう。どうしても関数型プログラミング言語で引き合いに出したいのなら、関数型かつオブジェクト指向プログラミング言語であるScalaを出すべきであった。

そもそもfmapという関数は、超ざっくりと言えばa -> bを、Functor f => f a -> f bに変換する関数であるから、むしろFunctor f => f a -> (a -> b) -> f b(逆カリー化するとFunctor f => (f a, a -> b) -> f b)という型の関数にするのは不自然である*1。この辺りもfmapに対して認識の齟齬があるなぁと言わざるを得ない。

そもそもそんな積極的に三項演算子使うなよ・・・

v1 if t else v2が違和感のある構文というのには割と同意だけれど、Pythonは文指向なので、素直にif文を使えば良いのでは。

冗長で、醜く、プログラマのメンタルモデルに一致しない。

まるで自分が「プログラマの代表」であるかのように言っているが、私個人としては、そもそも三項演算子(ここではt ? v1 : v2に限定する)を使いまくるようなメンタルモデルは持ち合わせていないので、理解できない(まぁこの人が三項演算子を使いまくっていることもわからないのだけど。ただわざわざ批判するということは、そういうことだろう)。

私にとって、三項演算子はあまり用いたくないものだ。なぜなら、三項演算子は一つの式で条件分岐と等価な処理が書けるので、確かにカッコ良くは見えるかもしれない。だが、その式が複雑になればなるほど、却って他人には意図が理解できない、いわゆる可読性の低いコードになる。
もちろん、場合によっては用いた場合のほうがシンプルで読みやすくなることもあるため、その場合は使うのがベストではあるのだが、Python自体がシンプルな構文故にif文に置き換えても読めないレベルとまではいかないため、大した問題にならないと思われる。

もしあなたが三項演算子を使いまくっている人であるならば、10年後、また読んでみると良いだろう。「誰だこのクソコードを書いた奴は!あ、俺か・・・」となっているであろうから。

あとこれはどうでも良いことだけど、普通はthen if elseみたいな書き方はしないと思う。

JavaScriptObject連想配列を比較するな

辞書型もわざわざsome_dict["some_key"]のように書く必要がある。some_dict.some_keyのようには書けない。

えぇ・・・(困惑)

本当に申し訳ないが、この発言には非常にガッカリした。この発言が事実なら、この人はJavaScriptの基礎も理解できていないことになる。JavaScriptObjectは、もともと連想配列として使うことを想定されたものではない。どちらかと言えば、レコード型のような使い方を想定している。
確かに昔のJavaScriptでは、連想配列として最適なオブジェクトが存在しなかったため、仕方なく連想配列としてObjectを使うケースもあった。だが、今はもっと適したMapオブジェクトがあり、こちらを使ったほうがJavaScriptとしてはよりモダンだ。
developer.mozilla.org

ここから何が言いたいのかと言うと、連想配列側の立場から言わせてもらえば、JavaScriptObjectと、Pythondictを比較するのは、そもそも間違っているということだ。
「RustのHashMapはCのstructと同じようにアクセスできない」と言っているのだと書けば、この人の主張のおかしさがよくわかるだろう。しかもこれら連想配列を「キモい」と言っている。つまりこの人にとってRustのHashMapは「キモい」存在だったのである。

話を戻すが、JavaScriptObjectの初期化に相当するコードは、Pythonだとこうだ。

class some_dict:
    some_key = ...
    ・
    ・
    ・

# もしくは

some_dict = type('some_dict', (), {
    'some_key': ...,
    ・
    ・
    ・
})

some_dict.some_key  # クラス変数へのアクセス

もっとも、あまり馴染みがないからわかると思うが、この書き方はあまり良い書き方ではない。より良く、極力JavaScriptObjectに近い(おそらく想定しているのはTypeScriptのような)書き方をするのであれば、dataclassを用いるべきだ。

import dataclasses

@dataclasses.dataclass
class SomeDict:
    some_key: # ここに型ヒントを書く
    ・
    ・
    ・

some_dict = SomeDict(some_key=..., ...)
some_dict.some_key

dataclassなら、後でdictにも変換できる。

dataclasses.asdict(some_dict)

ほかにも代数的データ型のようにも扱えたりと、いろいろメリットはあるのだが、蛇足となるので、ここでは解説しない。

代入式ってそんなメリットある?

a = 10の代入文は「文」であり「式」ではない。

むしろこれは長所だ。代入が文であるということは、式としては使えないということ。つまり、具体的には条件分岐で比較演算子(==)と間違えてしまう心配がないということだ。こっちのほうがよほど人間工学的ではないだろうか。
ちなみに人間工学を重視しているRustも、代入は式ではあるものの、その型は()である。つまり、Rustのほうでも条件分岐で間違えることはない*2し、この人の思っているようなことはできないだろう。

それでわざわざa := 10のような醜い構文がある。しかもトップレベルでは使えない。

トップレベルで使えなくて良い。少なくとも、可読性を重視する私はそれができたとしても喜ぶことはないだろう。

醜い構文と言うが、Cよりも先輩であるPascalでは代入演算子:=を割り当てているし、Goでも変数宣言と同時に代入する場合に:=を使っている。
そもそも、元来数学で等式として使われてきた=を、代入という形で使うのは数学的には正しくなく、むしろ意味合い的には:=のほうが正しいと言える。それを=としたのは、プログラミングにおいて代入のほうが使う機会が多いため、簡単な記号で表したかったからだとされている。

= を代入に使うのは FORTRAN が起源とされる。FORTRAN は文字・記号の種類を非常に少なく設計しており、なおかつプログラムでは頻繁に使われる代入を簡単に表せるようにするため、このような言語仕様になった。

等号 - Wikipediaより引用

醜いというのは、もはや数学に対する冒涜とも言えるのではないだろうか。

lambda x: ...は確かに冗長かもしれないけど・・・

これは確かに冗長だと私も思うし、嫌いなのもわかる。ただ、Pythonはもともと関数型プログラミング言語として開発されたわけではなく、ラムダ式は後から追加されたものなので、これに関しては仕方ないところがある。lambdaラムダ式であるということが明確にわかるし、元来のラムダ式(\lambda x.\ \cdots)に近い構文にしてくれているので、これはこれで良いんじゃないかとも思う。
もっと欲を言えば、:.であったり(構文的に難しかったのかもしれないが)、デフォルトでカリー化されたりしてほしかったが。

and / orに「メリットがない」は言い過ぎ

&&||ではなくandorだし、とにかく他言語と差別化を図るわりには無駄に記述量が増えているだけでメリットがない。

むしろ&& / ||は短く書けるということ以外にメリットが見当たらない。

まず、\& / \parallelはあまり論理演算としては使われない。少なくとも、私が論理学や論理回路を勉強してきた中でこの記号を用いた記憶はない。また、すでにあるbit演算子& / |と間違えてしまう可能性があるのはデメリットと言える。

一方、and / orはどうだろうか。実際に数学でも論理演算として用いられるAND / ORとして自然に見ることができるし、bit演算子& / |と間違えてしまう可能性も限りなく0に近いであろう。

したがってand / or演算子は、論理演算として自然に見ることができ、ミスを減らすこともできるというメリットがあるので「メリットがない」はない。しかもorに至っては記述量が同じである。

何度も言うけど、連想配列Objectとして使うな

{foo: number, bar: string}を定義したいだけなのに、

class MyType(TypedDict):
  foo: int
  bar: str

を書かなくてはならない。TypedDictをインポートする必要がある。正気の沙汰ではない。

importはしょうがないにしても、TypedDictよりもdataclassを使え。以上。

定数(不変変数)への破壊的代入は「まったく予測できない」?

当然だが、全てにおいて何がどこで書き変わっているか、まったく予測できない。

これは定数と言うより、不変変数のことを言っている気が・・・まぁ確かにPythonには定数と言う概念がないし、言語によっては不変変数を定数として扱うしかないのだが。

だが、「まったく予測できない」。これは間違いである。先ほども書いたが、実行前にmypyを使えば良い。PythonにはFinal[T]という型ヒントが存在し、これによって不変変数に破壊的代入を行った場合にmypyでエラーとして弾くことができる。
仮にmypyを使わないにしても、IDEによっては型不一致があれば書いている時点でエラーにしてくれるものもあるので、「まったく予測できない」ことはない。

それとPythonには、いくつかイミュータブルなオブジェクトが存在しており、その存在も完全に無視している。

「競プロ以外に正しい使い道がない」へのツッコミ

個人的な感想にはなってしまうのだが、Pythonの使い道については置いておいて、今まで競プロじゃない前提で書いてたんかいって思ってしまった。
つまり言いたいことは上記にほぼすべて集約されている。

「教育現場でPythonが選択されているのだとしたら子供達はあまりにも不幸だ」へのツッコミ

この人はプログラミング教育でPythonが選ばれていないか懸念を示していたが、この懸念は杞憂に終わるだろう。なぜなら、PythonよりもScratchのほうが選ばれるだろうから。これが「幸」かどうかはわからないが。

またプログラミング教育の目的は、あくまで「コンピュータはプログラムで動いていること」を理解させるためであって、「実務コードを書かせる」ためではない。したがって、

国語の教科書の代わりに素人が書いたケータイ小説を渡しているようなものだ。


携帯の画面に収まりきる程度の中身のない文章もどきを書くのならいい。
だが、プログラミング教育で目指すのはそこなのか?


無価値なケータイ小説を多少書ける程度の人材を量産したところでどうなるというのだ。
学ばせるならしっかりと型のある言語を使うべきだ。


素人ケータイ小説の著者が、自ら「小説家」を名乗っていたら滑稽に思うだろう。
Pythonを使ってエンジニアを名乗るというのはそういうことだ。

これらの批判(?)は全部的外れである。
仮に教育者たちがPythonを選択したとしてもそれを批判するのはお門違いである。そんなに選ばれてほしくないのなら、お前が教育者になれば良い。

「SOLID原則を守れ」へのツッコミ

技術的なことは何一つ言っていないので特になし。

最後に

以上である。

全体として、「エンターテインメント」として読むのであればまだ良い。だが、これを「テックブログ」として読むことは、まったくお勧めできない。なぜなら、この記事から正しい情報は得られないからである。


それと、ここからは個人的な話をさせていただくが、ケータイ小説のくだりに関しては、ケータイ小説、もっと枠を広げれば創作小説を馬鹿にしているとしか思えないような酷い内容であった。これはこの記事を「エンターテインメント」として見ても、非常に不愉快である。正直、その界隈の人たちからめちゃくちゃ怒られろと思った。

*1:メソッド(レシーバも引数とする)の場合は都合が良いからこの型になっているに過ぎない

*2:Rustではifの条件項でbool以外を評価したらエラーとなるように設計されているため