たぶんニシキヘビの中にいる (原題: Maybe in Python)

(翻訳記事じゃ) ないです。

Maybe はいいですよね。 値がある、という状態と値がない、という状態を非常に扱いやすくしてくれます。 リリンが生み出した文化の極みです。

なので、Python で似たようなことができないかと思い実装してみました。

本体は[こちら](https://gist.github.com/BlueRayi/fb999947e8f2a8037e7eea7ff9320f90)にあげてあります。 ぜひ遊んでみてマサカリを投げるといいと思います。

## 仕様

一部は “[null安全でない言語は、もはやレガシー言語だ](https://qiita.com/koher/items/e4835bd429b88809ab33)” を露骨に意識した構成になっておりますので、合わせてお読みいただくと良いと思います。

### 基本

この Maybe は抽象クラス`Maybe`を継承した具象クラス`Something`と`Nothing`として実装されています。 `Something`は値があることを示し、`Nothing`は値が欠落していることを示します。

`just`関数は`Something`のインスタンスを作成します。 `nothing`は`Nothing`の (唯一の) インスタンスです。

```python
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 です。

```python
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`関数に渡すことで得られます。

```python
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`であれば等しいとします。

```python
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`として使うには、

1. その Maybe が値を持つことを確認し、
2. `Maybe[T]`を`T`に変換しなければいけません。

if 文と後述する強制アンラップ演算子を使用することで以下のように使うことは可能です。

```python
m = just(913)
if m.has_value:
    print(m.i + 555)
```

```
1488
```

しかし、このふたつの工程が分離していると、以下のような思わぬハプニングが発生しえます。

```python
m = just(913)
if m.has_value:
    print(m + 555) # アンラップ忘れ、Maybe[int] と int の加算はできない
```

```
TypeError: unsupported operand type(s) for +: 'Something' and 'int'
```

```python
m = nothing
print(m.i + 555) # Nullチェック忘れ、m が Nothing の場合ぬるぽが飛ぶ
```

```
maybe.NullpoError: it GOT no value.
```

実は Maybe はイテレーション可能で、`Something`は中身の値を一回だけ生成し、`Nothing`はなにも生成せずに`StopIteration()`例外を発生させます。 これを利用すると、for 文を使用して以下のように Null チェックとキャストを同時に行えます。

```python
m = just(913)
for v in m:
    print(v + 555) # ここは m が Something のときだけ実行される
```

```
1468
```

ちょうど Swift の Optional バインディングと似た使い心地になるので、私はこれを “Maybe バインディング” と呼んでいます。 Optional バインディングとの違いは、シャドーイング (ブロック内で同じ変数名を使用すること) ができない点と、値が欠落している場合との分岐が少々煩雑になる点です。

順に見ていきましょう。 もしシャドーイングのようなことをする場合、Python の変数スコープの関係上、コメントの行で`m`が`Maybe[int]`から`int`に変わってしまいます。

```python
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 では次のように書けます。

```Swift
let m: Int? = nil
if let v = m {
    print(v + 555)
} else {
    print("ひょっとして君は俺が嫌いなのかな?")
}
```

`if`と`else`の対応は美しく、他の追随を許しません。 そしてこのふたつのブロックは文法上対になっています。

しかし Maybe ではこのように書かざるを得ません。

```python
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`を考えます。

```python
def safe_int(s):
    try:
        return just(int(s))
    except ValueError:
        return nothing
```

ところで、この関数にとあるサイトで利用する “年齢” テキストボックスから飛んできた文字列が入るとします。 普通は HTML 側で数字しか入力できないように制限しますし、前段の JavaScript で前処理なども行うでしょう。

そういう場合、事実上この関数が`nothing`を返す可能性はないと言えます。 それでも`Maybe[int]`は`int`として使えないから長々と Maybe バインディングを書かなければいけないのでしょうか? それでは`None`を使うほうがマシということになります。

そういうときにこそ強制アンラップ演算子`.i`を使用すればよいのです。 ちなみに演算子とか言っていますが実態はプロパティです。

```python
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 癒合演算子” も見てください)。

愚直に書けば次のようになります。

```python
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.`を使えば簡潔に書けます (例によってホントはプロパティです)。

```python
l = get_foobar_list('hogehoge')
num_0 = l.q.count(0)
```

また、添字に対しても同様に`.q[]`が利用できます。

```python
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 バインディングで書くこともできますが……

```python
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 癒合演算子[^coalesce]`>>`が用意されています。

```python
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” を得ることができます。

```python
x = foo >> bar >> baz >> qux >> 999
```

一方、短絡評価ではないので右辺が評価されてしまう点には注意してください。

ちなみに、この演算子は右ビットシフト演算子をオーバーロードして実装されています。 そのため、演算の優先順位も同一となります。 つまり、加法演算子よりも低く、比較演算子よりも高いです[^operator-precedence]。 これは C#[^c-sharp]、Swift[^swift]、Kotlin[^kotlin] の Null 癒合演算子の優先順位に近いですが、一方で PEP 505 で提案されている None-aware 演算子の`??`がほとんどの二項演算子よりも高い優先順位を持つ[^pep-505]のとは大きく異なります。 PEP 505 が正式採用される日が来たら (来ない気もしますが) 注意してください。 また、自動的に`>>=`も定義されています。

```python
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
```

余談も余談ですが、オーバーロード先として`>>`を選んだのは、

1. 手書きの “?” が “>” から下に伸ばして打点するように書く人がいる (形状の類似)
2. キーボードにおいて “?” と “>” は隣り合っている (入力操作の類似)
3. 複数並べて書いたとき、左から順に値をチェックしていくのを視覚的にも理解しやすい (処理ロジックの可視化)
4. 他の言語における Null 癒合演算子の優先順位に近い (混乱しにくい)

などが理由です。 ~~下に行くほど合理的な理由に見えますが、恐ろしいことに下に行くほど後付けの理由であり、筆者が適当に Maybe を作っていたのがよくわかります。~~

[^coalesce]: 一般的に Null Coalescing Operator の定訳は “Null 合体演算子” ですが、“合体” だと “欠けた部分を埋める” というニュアンスに乏しいので、“傷口がふさがる” という意味の “癒合” を使用しています。
[^operator-precedence]: https://docs.python.org/ja/3/reference/expressions.html#operator-precedence
[^c-sharp]: https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/operators/
[^swift]: https://developer.apple.com/documentation/swift/swift_standard_library/operator_declarations
[^kotlin]: https://kotlinlang.org/docs/reference/grammar.html#expressions
[^pep-505]: https://www.python.org/dev/peps/pep-0505/#the-coalesce-rule

### `map`メソッド (from `map`)

Null 条件演算子には実は弱点があります。 それは、Maybe オブジェクトにしか使用できない点です。 `SafeNavigationOperator`を引数に与えることもできません。

どういうことかというと、こういうことはできません。

```python
l = just([1, 2, 3, ])

s = .q.sum(l) # 文法エラー
s = sum(l.q) # 弾け飛ぶ例外

# どうしてくれんのこれ
# ｵｱｧｧｧｱｧｧｱｧｰｰｰｰｰｰ♥
```

Maybe バインディングで書くことはできますがやっぱり煩雑です。

```python
l = just([1, 2, 3, ])

for l0 in l:
    s = just(sum(l0))
if not l:
    s = nothing
```

これを Maybe オブジェクト側から何とかするために、`map`メソッドが存在しています。 `map`メソッドを使用すると以下のように書けます。

```python
l = just([1, 2, 3, ])

s = l.map(sum)
```

`map`メソッドに渡された関数は結果を`just`でくるんで返します。 値が欠落していれば当然`nothing`が返ります。 関数にはラムダ式も使えるので、以下のようなこともできます。

```python
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]`に適用してみましょう。

```python
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 段潰すことができます。 自然変換 μ ってやつです。

```python
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`メソッドがあればそれが使えます。

```python
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 引数以上をとる関数に使うのには少々骨が折れます。

```python
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`関数を使用するとこれを簡単に書けます。

```python
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`を返します。

```python
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`を返します。

```python
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` と考えることができます。 
>
>  　　 — [SwiftのOptional型を極める](https://qiita.com/koher/items/c6f446bad54442a28bf4#array%E3%81%A8optional)

```python
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 という共通のコンテキストで利用するために使用できます。

```python
# 辞書になかったとき価格の代わりに 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 のシリアライズ方式として指定できます (ただしネイティブなといっても略)。

```python
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](https://github.com/ekampf/pymaybe)” というものがあります。 PEP 505 でも言及されている有名所さんのようです。

こちらの大きな特徴は、Maybe をまるで裸の値のように取り回せるというところです。 具体的には README.rst にある以下のコードが参考になるでしょう。 `maybe`はこちらでいうところの`maybe_from`、`or_else`はこちらでいうところの`>>`演算子にあたると思ってください。

```python
>>> 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)年』を恐れる必要はない](https://qiita.com/koher/items/6c855ddbda8797af4605))。 これが 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をやめてSwiftを使おう](https://qiita.com/koher/items/4aea541fd0f0d8a9a166)

>  Objective-C の `nil` の扱いは一見安全なように見えて、問題の発覚を遅らせ、解決をより難しくしているだけです。潜在バグを生みやすいという点で、僕は Objective-C 方式は最悪だと考えています。かつて、 Objective-C で書いたプログラムを Java に移植したときに、 Java で `NullPointerException` が発生して初めて Objective-C 側の `nil` ハンドリングが不適切だったことに気付くということが何度もありました。 
>
> 　　 — [null安全でない言語は、もはやレガシー言語だ](https://qiita.com/koher/items/e4835bd429b88809ab33)

もちろん、それが Null 安全のために生産性を落とさなければいけないというわけではありません。 Nullable な変数に対する Null チェックは “かつての言語だって” 必要なことでしたし[^null-check]、この Maybe ではむしろそれを簡略化するための様々な方法を実装しました。 もし Null チェックなしにぶっつけ本番したければ、そのための Unsafe な操作さえあります。 この辺は[数年前に語り尽くされた話](https://qiita.com/koher/items/e4835bd429b88809ab33#null-%E5%AE%89%E5%85%A8%E3%81%AF%E7%94%9F%E7%94%A3%E6%80%A7%E3%82%82%E9%AB%98%E3%82%81%E3%82%8B)ではありますが。

とはいえ、Python を好んで使用する方はそういったガバガバ性は百も承知でとにかく “書きたいことを素直に書ける” ことを重んじているとは私も思います。 ですので、PyMaybe の思想が間違っているとは口が裂けても言えません~~っつーか私こそ世間に反逆する`Maybe[T]errorist`であると言えます~~。 ですが、まあ半分お遊びというか、自分が書きたいものを書ききったということでどうかお目こぼしください。

[^null-check]: こんな一個人製の Maybe では非現実的な話ですが、もしも[`nothing`のみを使い、組み込みの`None`を使用しないように](https://twitter.com/kmizu/status/782360813340286977) (発生しうるときはすぐに`maybe_from`でくるむように) できれば、裸の値には一切 Null チェックをする必要がなくなります。 実はこれが Null 安全な言語の最も大きな利点で、言語仕様にこのような仕組みが組み込まれている Null 安全な言語は “必要な Null チェック” さえすればよくなります。 一般的な “Null 安全な言語では Null チェックを強制される” という認識は正しくはあるのですが、感覚としては真逆です。 参考: [【アンチパターン】全部nil(null)かもしれない症候群](https://qiita.com/jnchito/items/6161647b624fb51cb815)