(翻訳記事じゃ) ないです。
Maybe はいいですよね。 値がある、という状態と値がない、という状態を非常に扱いやすくしてくれます。 リリンが生み出した文化の極みです。
なので、Python で似たようなことができないかと思い実装してみました。
本体はこちらにあげてあります。 ぜひ遊んでみてマサカリを投げるといいと思います。
仕様
一部は “null安全でない言語は、もはやレガシー言語だ” を露骨に意識した構成になっておりますので、合わせてお読みいただくと良いと思います。
基本
この Maybe は抽象クラスMaybe
を継承した具象クラスSomething
とNothing
として実装されています。 Something
は値があることを示し、Nothing
は値が欠落していることを示します。
just
関数はSomething
のインスタンスを作成します。 nothing
はNothing
の (唯一の) インスタンスです。
from maybe import just, nothing
a = just(42)
b = nothing
print(a)
print(b)
Just 42
Nothing
Maybe はプロパティhas_value
を持ち、Something
はTrue
を、Nothing
はFalse
を返します。 またSomething
はそれ自体 Truthy であり、Nothing
はそれ自体 Falsy です。
print(a.has_value)
print(b.has_value)
print('-----')
if a:
print('a is Truthy')
else:
print('a is Falsy')
if b:
print('b is Truthy')
else:
print('b is Falsy')
True
False
-----
a is Truthy
b is Falsy
Maybe は値を入れる箱と考えることが出来ます。 Maybe 自体も値ですから、Maybe は入れ子構造になりえます。 Maybe は “深さ” という概念をもちます。 裸の値の深さは 0 です。 Nothing
の深さは 1 です。 Something
の深さは “中身の深さ + 1” です。 深さはdep
関数に渡すことで得られます。
from maybe import dep
a2 = just(a)
b2 = just(b)
print(a2)
print(b2)
print('-----')
print(dep(42))
print(dep(a))
print(dep(b))
print(dep(a2))
print(dep(b2))
Just Just 42
Just Nothing
-----
0
1
1
2
2
Maybe は等値比較ができます。 Something
は互いがSomething
であり、その中身が等値であれば等しいとします。 入れ子構造ならば再帰しながら掘っていくことになります。 Nothing
は互いがNothing
であれば等しいとします。
print(a == 42)
print(a == just(42))
print(a == a2)
print(a == b)
print(b == nothing)
print(b == b2)
print(b2 == just(nothing))
False
True
False
False
True
False
True
Maybe バインディング (Null チェックに伴うキャスト)
Maybe[T]
をT
として使うには、
- その Maybe が値を持つことを確認し、
-
Maybe[T]
をT
に変換しなければいけません。
if 文と後述する強制アンラップ演算子を使用することで以下のように使うことは可能です。
m = just(913)
if m.has_value:
print(m.i + 555)
1488
しかし、このふたつの工程が分離していると、以下のような思わぬハプニングが発生しえます。
m = just(913)
if m.has_value:
print(m + 555) # アンラップ忘れ、Maybe[int] と int の加算はできない
TypeError: unsupported operand type(s) for +: 'Something' and 'int'
m = nothing
print(m.i + 555) # Nullチェック忘れ、m が Nothing の場合ぬるぽが飛ぶ
maybe.NullpoError: it GOT no value.
実は Maybe はイテレーション可能で、Something
は中身の値を一回だけ生成し、Nothing
はなにも生成せずにStopIteration()
例外を発生させます。 これを利用すると、for 文を使用して以下のように Null チェックとキャストを同時に行えます。
m = just(913)
for v in m:
print(v + 555) # ここは m が Something のときだけ実行される
1468
ちょうど Swift の Optional バインディングと似た使い心地になるので、私はこれを “Maybe バインディング” と呼んでいます。 Optional バインディングとの違いは、シャドーイング (ブロック内で同じ変数名を使用すること) ができない点と、値が欠落している場合との分岐が少々煩雑になる点です。
順に見ていきましょう。 もしシャドーイングのようなことをする場合、Python の変数スコープの関係上、コメントの行でm
がMaybe[int]
からint
に変わってしまいます。
m = just(913)
print(m) # ここでは Maybe[int]
for m in m:
print(m + 555)
print(m) # ここでは int
Just 913
1468
913
すでに Null チェックをしているのだからこのほうが便利だと思いますか? しかしそれは罠です。 もしm
の値が欠落している場合Maybe[int]
のままです。 あなたはこの先 “Maybe” という同じコンテキストでm
を扱うことは許されず、int
のように見えるm
がnothing
である可能性に怯えながら過ごさなければならないのです。 これではなんのための Maybe かわかりません。 単にNone
を使用しているのと大差がない (どころかより面倒になっている) じゃないか。
つづいての相違点です。 値があるときだけ実行したいのではなく、値があるときとないときで処理を分岐したい場合があるでしょう。 Swift の Optional では次のように書けます。
let m: Int? = nil
if let v = m {
print(v + 555)
} else {
print("ひょっとして君は俺が嫌いなのかな?")
}
if
とelse
の対応は美しく、他の追随を許しません。 そしてこのふたつのブロックは文法上対になっています。
しかし Maybe ではこのように書かざるを得ません。
m = nothing
for v in m:
print(v + 555)
if not m.has_value:
print("ひょっとして君は俺が嫌いなのかな?")
for
とif
の対応というのはおそらく生まれてこの方誰も見たことがないでしょう。 それにこのふたつのブロックは隣接して書いてあるだけで文法上の対でもありません。 さらに言えば、if not m.has_value
とはずいぶんと長いです。 これについてはもともとNothing
が Falsy ですからif not m
でもよいのですが、すこし文意が読み取りづらい欠点もあります。
とはいえ、Null チェックとキャストが分離されているよりも遥かに安全であり、書きやすさもあります。 基本的には.i
の使用は禁止し、Maybe バインディングを使用するのがよいでしょう。
強制アンラップ演算子 (from !
, !!
)
str
からint
への変換を試みて、失敗したらnothing
を返す関数safe_int
を考えます。
def safe_int(s):
try:
return just(int(s))
except ValueError:
return nothing
ところで、この関数にとあるサイトで利用する “年齢” テキストボックスから飛んできた文字列が入るとします。 普通は HTML 側で数字しか入力できないように制限しますし、前段の JavaScript で前処理なども行うでしょう。
そういう場合、事実上この関数がnothing
を返す可能性はないと言えます。 それでもMaybe[int]
はint
として使えないから長々と Maybe バインディングを書かなければいけないのでしょうか? それではNone
を使うほうがマシということになります。
そういうときにこそ強制アンラップ演算子.i
を使用すればよいのです。 ちなみに演算子とか言っていますが実態はプロパティです。
s = get_textbox('年齢')
age = safe_int(s).i # .i によって age には整数が代入される
if age >= 20:
...
手間がなく便利に思えますが、もしnothing
に対して.i
を呼び出すと古き良きアレが元ネタのNullpoError
が飛びます。 繰り返しになりますが、強制アンラップ演算子はあくまでも “危険な (Unsafe)” 操作であると考え、ロジック上絶対にnothing
になりえない箇所でのみ使用するべきです。
Null 条件演算子 (from ?.
, ?->
)
ここにMaybe[list]
があるとします。 あなたはこの中にいくつ0
があるか調べたいと思いました。 Maybe ということはnothing
な可能性もあると思いますが、大抵の場合リスト自体が存在しないとしたら、期待するのはやはりnothing
でしょう(いやいや0
が欲しいって人もいる? そういう方は次の “Null 癒合演算子” も見てください)。
愚直に書けば次のようになります。
l = get_foobar_list('hogehoge')
for l0 in l:
num_0 = just(l0.count(0))
if not l:
num_0 = nothing
確かに書けますが……はっきり言って煩雑です。 もしl
がlist
であればnum_0 = l.count(0)
ですむ処理を、4 行もかけて書きたくありませんよね。
Null 条件演算子.q.
を使えば簡潔に書けます (例によってホントはプロパティです)。
l = get_foobar_list('hogehoge')
num_0 = l.q.count(0)
また、添字に対しても同様に.q[]
が利用できます。
l = get_foobar_list('hogehoge')
print(l.q[3])
# 以下の代わりに使える
# for l0 in l:
# print(just(l0[3]))
# if not l:
# print(nothing)
なぜこのようなことができるのでしょう? .q
は、SafeNavigationOperator
という抽象クラスを継承したオブジェクトを返します。 SafeNavigationOperator
は__getattr__
と__getitem__
をオーバーライドしています。値をもつ場合SomethingSNO
が返されます。 こちらは値の情報を保持していて、__getattr__
や__getitem__
は中身の値のそれをjust
にくるんで返します。 値が欠落している場合NothingSNO
が返され、__getattr__
や__getitem__
はただnothing
を返します。
ここで勘のいい方は疑問に思うかもしれません。 some_list.count
はメソッドであり、呼び出し可能オブジェクトです。 maybe_list.q.count
は関数をさらに Maybe でくるんで返されるわけです。 それをl.q.count(0)
のように扱えるということは、Maybe 自体呼び出し可能オブジェクトなの?
実はそのとおりです。 Something
を呼び出すと中身を呼び出し、その結果をjust
にくるんで返します。 Nothing
を呼び出すと単にnothing
を返します。 この仕様によって先に挙げたようなことが可能になっています。 この仕様はあくまでも.q.
と同時に使用するためのものです。 それ以外で使用するのは、個人的には魔術的に思うので控えたほうがよいと思います (後述する Maybe 同士の四則演算などを実装していない理由に繋がります)。
ところで、?.
を実装している言語の中でも、いわゆる “オプショナルチェーン” を採用している場合では Null が見つかった時点で終了してしまいます (いわば短絡評価) が、.q.
の場合はnothing
のまま処理を続けます。 nothing
に対して.q.
をしてもnothing
が出るだけで、伝播した結果として得られるのが Null 相当物であることにかわりはありませんが、この違いについてはしっかり認識しておく必要があるかもしれません。
ちなみに、__setattr__
はオーバーライドしていないのでfoo.q.bar = baz
みたいなことはできません。 これは私の技術的な限界であり、__setattr__
をオーバーライドしたときに生じる無限ループをどうしても解決できなかったためです。 助力願いてぇ……。 foo.q[bar] = baz
のほうはできると思いますが対称性が崩れるのでいまのところ実装していません。
Null 癒合演算子 (from ?:
, ??
)
Null の場合にデフォルト値で埋めたいというのは Null を扱う上で最も多い要求でしょう。
もちろん Maybe バインディングで書くこともできますが……
l = get_foobar_list('hogehoge')
num_0 = l.q.count(0) # ちなみにここでは num_0 は Maybe[int] で……
for v in num_0:
num_0 = v
if not num_0:
num_0 = 0
# ここまで抜けると num_0 は int になります。
ちゃんと Null 癒合演算子1>>
が用意されています。
l = get_foobar_list('hogehoge')
num_0 = l.q.count(0) >> 0 # num_0 は int
Null 癒合演算子の動作は一見シンプルに見えます (し、実際シンプルです) が、少々注意が必要です。
左辺の値が欠落しているときは話はカンタンで、右辺をそのまま返します。
一方で左辺が値を持つときは興味深い (Interesting の訳語的な意味で) 動作をします。 左辺が右辺より深いときは、左辺の中身と右辺を再度癒合します。 右辺が左辺より深いときは、左辺をjust
でくるんでから再度癒合します。 同じならば左辺を返します。 ただし、右辺が裸の値だった (深さ 0 の) ときは、右辺をjust
でくるんで再度癒合し、その中身を返します。
外から見ると似たような動きをするor
演算子の方は左辺が Truthy ならそのまま返すのにくらべてずいぶんと複雑な動きです。 なぜこんなことをしているのかというと、再帰呼び出しの結果として必ず右辺と同じ深さの値を返すためです。
“デフォルト値を設定する” という文脈において期待するのは、値があろうとなかろうと同じように扱えることでしょう。 Maybe は入れ子可能なため、いくぶんか煩雑な動作が必須になります。 ただしそれは内部の話であり、使う側は “常に同じ構造の値が返る” というシンプルな扱いができるでしょう。
たとえば、複数の Maybe があったとき、それらの構造にとらわれず、以下のようにして “最右辺と同じ構造 (この場合裸の値) の、一番早く値が現れる Maybe” を得ることができます。
x = foo >> bar >> baz >> qux >> 999
一方、短絡評価ではないので右辺が評価されてしまう点には注意してください。
ちなみに、この演算子は右ビットシフト演算子をオーバーロードして実装されています。 そのため、演算の優先順位も同一となります。 つまり、加法演算子よりも低く、比較演算子よりも高いです2。 これは C#3、Swift4、Kotlin5 の Null 癒合演算子の優先順位に近いですが、一方で PEP 505 で提案されている None-aware 演算子の??
がほとんどの二項演算子よりも高い優先順位を持つ6のとは大きく異なります。 PEP 505 が正式採用される日が来たら (来ない気もしますが) 注意してください。 また、自動的に>>=
も定義されています。
a = just(42)
b = nothing
print(a >> 0 + 12) # 42 ?? 0 + 12 なら 54 となる(ハズ)
print('-----')
a >>= 999
b >>= 999
print(a)
print(b)
42
-----
42
999
余談も余談ですが、オーバーロード先として>>
を選んだのは、
- 手書きの “?” が “>” から下に伸ばして打点するように書く人がいる (形状の類似)
- キーボードにおいて “?” と “>” は隣り合っている (入力操作の類似)
- 複数並べて書いたとき、左から順に値をチェックしていくのを視覚的にも理解しやすい (処理ロジックの可視化)
- 他の言語における Null 癒合演算子の優先順位に近い (混乱しにくい)
などが理由です。 下に行くほど合理的な理由に見えますが、恐ろしいことに下に行くほど後付けの理由であり、筆者が適当に Maybe を作っていたのがよくわかります。
map
メソッド (from map
)
Null 条件演算子には実は弱点があります。 それは、Maybe オブジェクトにしか使用できない点です。 SafeNavigationOperator
を引数に与えることもできません。
どういうことかというと、こういうことはできません。
l = just([1, 2, 3, ])
s = .q.sum(l) # 文法エラー
s = sum(l.q) # 弾け飛ぶ例外
# どうしてくれんのこれ
# オアァァァアァァアァーーーーーー♥
Maybe バインディングで書くことはできますがやっぱり煩雑です。
l = just([1, 2, 3, ])
for l0 in l:
s = just(sum(l0))
if not l:
s = nothing
これを Maybe オブジェクト側から何とかするために、map
メソッドが存在しています。 map
メソッドを使用すると以下のように書けます。
l = just([1, 2, 3, ])
s = l.map(sum)
map
メソッドに渡された関数は結果をjust
でくるんで返します。 値が欠落していれば当然nothing
が返ります。 関数にはラムダ式も使えるので、以下のようなこともできます。
a = just(42)
print(a.map(lambda x: x / 2))
Just 21.0
map
メソッドに関しては語るべきことはあまりありません (とくにモナドをご存じの方にとっては)。
bind
メソッド (from flatMap
)
さっき作ったsafe_int
関数を思い出してください。 str
を引数にとってMaybe[int]
を返すやつです。 map
を使ってこれをMaybe[str]
に適用してみましょう。
s1 = just('12')
s2 = just('hogehoge')
s3 = nothing
print(s1.map(safe_int))
print(s2.map(safe_int))
print(s3.map(safe_int))
Just Just 12
Just Nothing
Nothing
safe_int
が Maybe を返し、さらにmap
メソッドがjust
でくるみ直すので入れ子になってしまいました。 たぶんこのプログラムを書いた人はこの結末は望んでいなかったでしょう (入れ子に “できる” こと自体は表現力を高めます。 たとえば JSON のパースの結果、null
が入っていたならjust(nothing)
、そもそもそのキーが存在していなかったならnothing
と表現できたりするので)。
いままで秘密にしていましたがjoin
関数を使うと入れ子を 1 段潰すことができます。 自然変換 μ ってやつです。
from maybe import join
print(join(s1.map(safe_int)))
print(join(s2.map(safe_int)))
print(join(s3.map(safe_int)))
Just 12
Nothing
Nothing
そして、なにより望まれているのは、“map
してjoin
” をひとつにすることです。 bind
メソッドがあればそれが使えます。
print(s1.bind(safe_int))
print(s2.bind(safe_int))
print(s3.bind(safe_int))
Just 12
Nothing
Nothing
Haskell を使いこなす変態方にはmap
メソッドが<$>
演算子でbind
メソッドが>>=
演算子といえばすぐに分かるでしょう。 裸の値を返す関数にはmap
、Maybe を返す関数にはbind
と覚えるといいでしょう。
do
関数 (from do
記法)
map
やbind
は当然ひとつの Maybe オブジェクトから呼び出すので、2 引数以上をとる関数に使うのには少々骨が折れます。
lhs = just(6)
rhs = just(9)
ans = lhs.bind(lambda x: rhs.map(lambda y: x * y))
print(ans)
Just 54
ちなみに内側がmap
なのはx * y
で裸の値を返す関数のため、外側がbind
なのはmap
メソッドの返り値が Maybe だからといえます。 覚えていますか?
do
関数を使用するとこれを簡単に書けます。
from maybe import just, nothing, do
lhs = just(6)
rhs = just(9)
ans = do(lhs, rhs)(lambda x, y: x * y)
print(ans)
Just 54
do
関数は引数がすべて値を持つときだけ中身を取り出して関数を実行します。 ひとつでも値が欠落しているときはnothing
を返します。 do
という名前の由来は Haskell ですが、アルゴリズムは極めて反モナド的です。 許し亭ゆるして。
ちなみにdo
関数の引数は Maybe のみしか取ることができません。 一部の引数として裸の値を使用したいときはjust
でくるみましょう。
そのほかこまごましたこと
コンテナとしての Maybe
Maybe はコンテナでもあり、len
関数とin
演算子が使用できます。
len
関数はSomething
は常に1
を、Nothing
は常に0
を返します。
a = just(42)
b = nothing
a2 = just(a)
b2 = just(b)
print(len(a))
print(len(b))
print(len(a2))
print(len(b2))
1
0
1
1
in
演算子は、右辺の中身が左辺に等しいとき、True
を返します。 それ以外のときはFalse
を返します。 Nothing
は常にFalse
を返します。
print(42 in a)
print(42 in b)
print(42 in a2)
print(42 in b2)
True
False
False
False
さきほど述べたように、Maybe はイテレート可能です。 そのため、たとえばリストへの変換が可能です。 @koher 氏は以下のように述べましたが、まさにその通りの結果になります。
Optional
は中身が空かもしれない箱でした。別の見方をすれば、Optional
を最大で一つしか要素を入れられないArray
と考えることができます。
al = list(a)
bl = list(b)
print(al)
print(bl)
[42]
[]
この辺の仕様を使う機会があるのかは謎です。
ネイティブの世界とつながる
Maybe には一対になった関数とメソッド、maybe_from
とto_native
があります。 関数とメソッドですが一対です。 これらは Python のネイティブな型と Maybe をつなげる働きをします。
maybe_from
は、ネイティブな値をとって Maybe を返します。 just
との違いは、None
が与えられたときにjust(None)
ではなくnothing
が返ること、Maybe が与えられたときにそのまま返すことです。 この関数は返り値の欠損をNone
で示す既存の関数やメソッドの返り値を、まとめて Maybe という共通のコンテキストで利用するために使用できます。
# 辞書になかったとき価格の代わりに None を返す既存の関数
def get_price(d, name, num, tax_rate=0.1):
if name in d:
return -(-d[name] * num * (1 + tax_rate)) // 1
else:
return None
# ...
from maybe import maybe_from
p = get_price(unit_prices, 'apple', 5) # int が来るかもしれないし None が来るかもしれない
p = maybe_from(p) # p は unit_prices に 'apple' があるかどうかに関わらず Maybe[int]
to_native
メソッドは逆に、Maybe を ネイティブな値にします (ただし、ネイティブなといっても中身がそもそも何らかのライブラリで用意されているオブジェクトの場合は単にそれが出ます)。 .i
と違い、nothing
ならNone
を返します (だからといって “安全な” アンラップ演算子として濫用してはいけません) し、ネストしていても再帰的に掘っていき必ず裸の値を返します。 map
は引数が欠落しているとき全体を欠落とみなしますが、こちらは値がないという意味としてNone
を与えることを期待する関数の引数に使用できます。 また、ネイティブな値が返ることを利用して JSON のシリアライズ方式として指定できます (ただしネイティブなといっても略)。
import json
from maybe import Maybe
def json_serial(obj):
if isinstance(obj, Maybe):
return obj.to_native()
raise TypeError ("Type {type(obj)} not serializable".format(type(obj)))
item = {
'a': just(42),
'b': nothing
}
jsonstr = json.dumps(item, default=json_serial)
print(jsonstr)
{"a": 42, "b": null}
PyMaybe との違いとか “ただ面倒なだけじゃん?” に対する応答とか
Python で Maybe というと、既存のライブラリとして “PyMaybe” というものがあります。 PEP 505 でも言及されている有名所さんのようです。
こちらの大きな特徴は、Maybe をまるで裸の値のように取り回せるというところです。 具体的には README.rst にある以下のコードが参考になるでしょう。 maybe
はこちらでいうところのmaybe_from
、or_else
はこちらでいうところの>>
演算子にあたると思ってください。
>>> maybe('VALUE').lower()
'value'
>>> maybe(None).invalid().method().or_else('unknwon')
'unknwon'
Maybe でくるんである値に対して、裸の値に対するメソッドをコールしています。 また Null に対するメソッドコールは無視されて Null が伝播しているのがわかります。 私の Maybe のように一旦取り出してとか、.q.
とかする必要がありません。 四則演算などの Dunder メソッドも実装されているようです。
なんだ、こちらのほうが便利じゃないか。 余計なエラーも出ないし……とお思いの方も多いと思います。
しかし、私がそのような仕様で Maybe を実装したのには理由があります。 それは、まさに “エラーを発生させるため” なのです。
Maybe[T]
とT
の違いを正しく意識しないとすぐエラーが出るというのは、逆に言えばその違いを意識する手助けになります。 混同するとすぐエラーが出るし、文字列に変換すると余計なJust
という接頭辞がつきます。 それは、“正しく書かないと正しく動かない” ということを保証するためのものです。 間違えていたらすぐ気づくのです (参照: マジレスすると『Optional(2018)年』を恐れる必要はない)。 これが Maybe を直接呼び出してほしくない理由でもあります (違いを隠蔽してしまうので。 ホントは文字列変換も消してしまいたいけど……)。
Null 安全とは、ぬるぽを起こさない仕組みであると一般的には理解されますし、究極的な目的がそこであることは間違いないでしょう (と、いきなり Null 安全について語り始めましたが、私の Maybe の型ヒントはダメダメなので Null 安全のために利用することは事実上不可能です)。 しかし、本当に Null 安全に必要なものは “Nullable と Non-null が区別されること” です。 なぜ Java がだめだったのでしょう? それはあらゆる参照型にnull
を代入でき、逆の立場から言えばどんな参照型の値もnull
である可能性があることでした。 T
が Null かもしれないということは、つまりT
と書いているのにT
として使えないことを意味します。 すべきです。 そこへきて Nullable と Non-null の垣根をさらに取り払うことは—T
でないものをT
のように扱えるようにするということは—むしろ、危険なことなのです。
Objective-Cには、
nil
に対するメソッドコールはエラーにならずに無視されるという悪しき仕様があります。(中略)
しかし、この便利さと引き換えにObjective-Cは恐ろしいリスクも背負っています。本来、
nil
のチェックをしなければならない箇所でnil
チェックを忘れてしまったけれども、たまたまうまく動いてしまうことがあります。そしてある日、特定の条件が重なったり、ちょっとした修正を行ったときにそのバグが顕在化するのです。Objective-C の
nil
の扱いは一見安全なように見えて、問題の発覚を遅らせ、解決をより難しくしているだけです。潜在バグを生みやすいという点で、僕は Objective-C 方式は最悪だと考えています。かつて、 Objective-C で書いたプログラムを Java に移植したときに、 Java でNullPointerException
が発生して初めて Objective-C 側のnil
ハンドリングが不適切だったことに気付くということが何度もありました。
もちろん、それが Null 安全のために生産性を落とさなければいけないというわけではありません。 Nullable な変数に対する Null チェックは “かつての言語だって” 必要なことでしたし7、この Maybe ではむしろそれを簡略化するための様々な方法を実装しました。 もし Null チェックなしにぶっつけ本番したければ、そのための Unsafe な操作さえあります。 この辺は数年前に語り尽くされた話ではありますが。
とはいえ、Python を好んで使用する方はそういったガバガバ性は百も承知でとにかく “書きたいことを素直に書ける” ことを重んじているとは私も思います。 ですので、PyMaybe の思想が間違っているとは口が裂けても言えませんっつーか私こそ世間に反逆する。 ですが、まあ半分お遊びというか、自分が書きたいものを書ききったということでどうかお目こぼしください。Maybe[T]errorist
であると言えます
-
一般的に Null Coalescing Operator の定訳は “Null 合体演算子” ですが、“合体” だと “欠けた部分を埋める” というニュアンスに乏しいので、“傷口がふさがる” という意味の “癒合” を使用しています。 ↩
-
https://docs.python.org/ja/3/reference/expressions.html#operator-precedence ↩
-
https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/operators/ ↩
-
https://developer.apple.com/documentation/swift/swift_standard_library/operator_declarations ↩
-
https://kotlinlang.org/docs/reference/grammar.html#expressions ↩
-
https://www.python.org/dev/peps/pep-0505/#the-coalesce-rule ↩
-
こんな一個人製の Maybe では非現実的な話ですが、もしも
nothing
のみを使い、組み込みのNone
を使用しないように (発生しうるときはすぐにmaybe_from
でくるむように) できれば、裸の値には一切 Null チェックをする必要がなくなります。 実はこれが Null 安全な言語の最も大きな利点で、言語仕様にこのような仕組みが組み込まれている Null 安全な言語は “必要な Null チェック” さえすればよくなります。 一般的な “Null 安全な言語では Null チェックを強制される” という認識は正しくはあるのですが、感覚としては真逆です。 参考: 【アンチパターン】全部nil(null)かもしれない症候群 ↩