これまで未婚の父として娘のヤギさくらちゃんを育ててきましたが、2日後に命を奪う決断をしました。
(12/3(火) 14:22 さくらちゃんは旅立ちました)
ある日、突然目が見えなくなり、次の日には立つことができなくなっていました。
自由にならない体にもどかしさを感じ、目が見えない恐怖にのたうち回りながら
体をぶつけ、顔をぶつけ、歯も折れてぼろぼろになってしまいました。
信頼できる獣医さんの愛にあふれた治療の結果、危篤状態は免れたものの
根本的な回復にはいたらず、2週間の介護を経て安楽死の判断をしました。
1月生まれで間もなく3歳でしたが、その日を迎えることはありません。
人間で言えば18歳くらいでしょうか。
人間の都合で親や兄弟と引き離し、最後は自分の意志とは関係ない第三者によって命を奪われる。
死後は一面のきれいなお花畑で、仲間たちとお花を片っ端からむしゃむしゃ根こそぎ貪り食って過ごしてもらいたいものです。
むさぼるさくらちゃん#さくらちゃん日記 pic.twitter.com/PKrcJiRJVe
— 🐐ヤギ魔法少女さくらちゃん(Kadzuya Okamoto) (@arowM_) June 17, 2019
さて、さくらちゃんを我が家に迎え入れたときのことを思い起こすと、
ちょうどそれは私がElmを使い始めた頃と重なります。
そこで、この記事では在りし日のさくらちゃんを偲びながら、
Elmに不慣れだった頃の私が書いた恥ずかしいコードを供養していきます。
何のために恥ずかしい過去をさらけだすのか
はじめたころの至らぬコードを見るのは、恥ずかしくてたまらないものです。
ましてやそれを公衆の面前にさらけだすことは言わずもがな。
しかし、それを行うことには大きな意味と価値があると信じています。
さくらちゃんが生きた3年に満たない日々にも、人々を癒やす大きな使命があったように。
さくらちゃんは癒やしの塊なので、お散歩中に偶然通りがかった方に「さっき嫌なことがあったけど、気持ちが癒えました」って言われました#さくらちゃん日記 pic.twitter.com/CwQCMoRxhy
— 🐐ヤギ魔法少女さくらちゃん(Kadzuya Okamoto) (@arowM_) June 8, 2019
その価値とは、たとえばElmに入門した人が、次のレベルに行くために。
Elmをはじめたころに自分が書いたコードに対して、今の私自身がどのように恥ずかしさを感じるか。
これはElmに入門された方が、ご自身のコードを次のレベルに持ち上げるための示唆となることでしょう。
ながいヤギ#さくらちゃん日記 pic.twitter.com/0mT00OyZSe
— 🐐ヤギ魔法少女さくらちゃん(Kadzuya Okamoto) (@arowM_) May 30, 2019
そしてElmに触れたことがない人が、雰囲気を感じるために。
過去には当たり前に行われていたことが、今では理由あって非推奨になっていることがあります。
Elmは互換性のために負の遺産を残すことよりも、今をよりよくすることに価値を置き、
互換性を失った部分は移行ツールなどで補助する方針を採ることが多いです。
昔の私のコードからそのようなElmの変遷を嗅ぎ取ることもできるでしょう。
おもしろさを感じていただけたのであれば、Elmはどんな人にオススメできないか や Elmをはじめる人が最初に読むページ をぜひご覧ください。
あるいは、Elmに限らずプログラミング能力を高めたい方のために。
Elmの公式ガイド(和訳版)では、Elmを学ぶことがJavaScriptのコードをよりよいものにすると述べられています。
これはElmの 考え方 が普遍的に他の言語を使う上での新しい「気づき」を与えるものであり、
その「気づき」がいろいろな場所で役立つことを意味しています。
同じように、私の恥の感覚が多くの方に「気づき」を与えることでしょう。
そして、プログラミングに限らず、次のレベルを目指す方のために。
世の中の優れたものは、多くの場合結果だけが強調されます。
結果だけ見ると「なんてすごいレベルなんだ。自分にはそんなもの作れない。」と壁を感じてしまいがちです。
しかし、実際にはその完成形に至るまでに数えられぬ程の試行と修正が積まれています。
美しく澄んだ上品なスープは最初から澄んでいたのではなく、幾度となくアクをとり、澄んだかと思えばまた出てくるアクをとり、
そうしてはじめて雑味が消えて完成するものです。
文章だって数十回以上の推敲作業の末に、読みやすくおもしろい、ときに感動的ですぅっと体に染み込むものになるものです。
とけてる#さくらちゃん日記 pic.twitter.com/IzaOawcBVV
— 🐐ヤギ魔法少女さくらちゃん(Kadzuya Okamoto) (@arowM_) May 26, 2019
最初は必ず濁って淀んで雑味に満ちたもの。
でもそれを作らずして知識ばかり増やしたところで何も始まらない。
私自身も学生時代には、文法を覚えたプログラミング言語の数を数えて喜んだり、難しい言語機能を理解することにばかり気持ちよさを感じていました。
ポシェとかエスプーマとかプリウスとかカルカソンヌとか、たくさんのかっこいい調理方法をそれぞれ単体で覚えたところで、
拙いながらもそれらを複合的に使って料理を作ることをしなければ、いつまでも本当のいい料理は作れません。
とってもいいあくびが撮れました#さくらちゃん日記 pic.twitter.com/CjxYW9v4c5
— 🐐ヤギ魔法少女さくらちゃん(Kadzuya Okamoto) (@arowM_) June 2, 2019
YAGI にも YAGNI
前置きが長くなりましたが、さっそく過去の恥ずかしいコードを見ていきましょう。
最初のテーマは YAGNI(ヤグニ) です。
YAGI(ヤギ) ではなく YAGNI(ヤグニ) です。
チキンビリヤニでもなく、"You ain't gonna need it." を略したものらしいです。
要するに「まだいらんやろ」ってことです。
ヤギの飼育、特に室内での飼育とは未知なるものです。
マイナーすぎて誰も情報発信しません。
かわいい姿や面白い姿は需要があるのでシェアしますが、
床に敷くマットのオススメや家を留守にするときの注意点などはほとんど共有されません。
さらに、ヤギには個体ごとに特性がありますから、干し草を食べさせるにしても
どこの国でいつ採れたものか、何番刈りなのか、シングルプレスかダブルプレスがいいのかなどは実際に試してみないとわかりません。
かといって、「干し草を食べなかった場合に備えなければ」と先にあらゆるタイプのご飯を買ってきて用意したら、
結局食べきれなくて腐らせてしまいます。
こういうときに役に立つのがYAGNIの考え方です。
- とりあえず何かご飯を食べさせないといけないので、近所でモルモット用に売ってるチモシーを買ってきてみる
- 食べさせてみたら、硬い茎の部分ばかり残している
- だったらもう少し柔らかいものが必要だ
- そこで試しに三番刈りダブルプレスのチモシーを少しamazonで買ってみる
- 気に入っているようだったら、少量ずつ買っていては高く付くので20kgずつ購入してみる
- ちょっと太ってきたらクレイングラスを試してみる
このように、いまどうしても必要なものだけ逐次用意して、実際に他のものが必要になった段階で初めて用意するという戦略です。
Elmにおいても、かつて私はこのYAGNIができていませんでした。
module Model exposing
( Goat
, Model
, age
, goat
, name
, ...
, ...
)
import Monocle.Lens exposing (Lens)
type alias Model =
{ goat : Goat
, ...
, ...
}
type alias Goat =
{ name : String
, age : Int
, ...
, ...
}
goat : Lens { x | goat : a } a
goat =
let
get =
.goat
set v a =
{ a
| goat = v
}
in
Lens get set
name : Lens { x | name : a } a
name =
let
get =
.name
set v a =
{ a
| name = v
}
in
Lens get set
...
...
最初にimportしている Lens というモジュールは、「なんかかっこよく値の更新とかができるよ」というなんかなんとなくかっこいいやつです。
この Lens をつかって定義された goat
や name
といった関数は、以下のように「かっこよく」使います。
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Mask ->
( model
|> .set
(Lens.compose Model.goat Model.name)
"(個人情報保護のため表示できません)"
|> Lens.modify
(Lens.compose Model.goat Model.age)
(\n -> max 0 (n - 2))
, Cmd.none
)
... ->
... ->
なんかよく分からんけどかっこいいですね! なんかよく分からんけど。
#さくらちゃん日記 pic.twitter.com/3pAO2Mt3UO
— 🐐ヤギ魔法少女さくらちゃん(Kadzuya Okamoto) (@arowM_) January 22, 2019
でもよく考えたら、こんなに中二臭い書き方をしなくても、もっと素直に読みやすく書けますね!
update : Msg -> Model -> ( Model, Cmd Msg )
update msg ({ goat } as model) =
case msg of
Mask ->
( { model
| goat =
{ goat
| name = "(個人情報保護のため表示できません)"
, age = max 0 (goat.age - 2)
}
}
, Cmd.none
)
こういう書き方でもスッキリします。
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Mask ->
( { model
| goat = maskGoat model.goat
}
, Cmd.none
)
maskGoat : Goat -> Goat
maskGoat goat =
{ goat
| name = "(個人情報保護のため表示できません)"
, age = max 0 (goat.age - 2)
}
もちろん、Lens が役に立つようなことも、いつかあるかもしれません。
そりゃ、奇跡が起きてさくらちゃんが明後日までに急にすべて元通り元気になる可能性もゼロじゃないですからね!
ホウキたべないでください#さくらちゃん日記 pic.twitter.com/5eEyUkMnBi
— 🐐ヤギ魔法少女さくらちゃん(Kadzuya Okamoto) (@arowM_) December 21, 2018
でも、少なくとも Lens がどうしても必要になるまでは導入する必要はないですし、
よりによって最初の例のように全フィールドに対して Lens を最初から定義する必要はないはずです。
なんとも恥ずかしいコードでした。
自分の都合を押し付けない
ヤギとは本来集団行動が苦手な動物です。
一方でひとりにされると寂しくて「ぶべぇぇぇぇ...」って鳴いちゃう動物です。
僕も集団行動はしたくないけど、家にひとりでいると寂しい動物なので、もうすぐさくらちゃんがいなくなって「ぶべぇぇぇぇ...」って泣いちゃいます。
昨日ビデオ会議してたら後ろで「お父さん遊んでくれなくてひま〜」って主張しだしたさくらちゃん#さくらちゃん日記 pic.twitter.com/vtVqXPvPHg
— 🐐ヤギ魔法少女さくらちゃん(Kadzuya Okamoto) (@arowM_) January 23, 2019
そんなヤギさんなので、自分がしたいように自由にさせてあげないと、ストレスがたまってしまいます。
僕も目覚ましなんてかけずに自分が起きたい時に起きて、さくらちゃんと遊びたい時に遊ばないと、ストレスがたまってしまいます。これからどうしよう。
では、たとえばヤギさんが床に敷いている防水マットを食べだしてしまったらどうしたらいいでしょうか?
あきらかに体に悪いので食べさせたくないですし、このままではマットがなくなってしまいます。
だからといって叱っても、「なんでお父さんはいじわるするのっ!!」と頭突きされるだけで何も変わりません。
「ねぇお父さん、ちょっとくらいいいでしょ?ちょっとくらい頭突きさせてよ」っておねだりしてくる28kgの筋繊維の塊です。
— 🐐ヤギ魔法少女さくらちゃん(Kadzuya Okamoto) (@arowM_) January 14, 2019
お父さんの腕折れちゃうからほどほどにしてね#さくらちゃん日記 pic.twitter.com/iobeMJ1chj
この場合は、なぜマットをかじるのかを考える必要があります。
単純に満たされぬ食欲に支配されているのかもしれません。
うまく立てなくなった今でも、咀嚼がうまくできない今でも、ご飯をあげると「もっとくれ、もっとくれ」と暴れだします。
ヤギの生命力の源はこの食欲なのだと感じる瞬間です。
この食欲がヤギさんのたくましい生命力の源なんだなって思います。#さくらちゃん日記 pic.twitter.com/KYfAdYSIX7
— 🐐ヤギ魔法少女さくらちゃん(Kadzuya Okamoto) (@arowM_) November 21, 2019
食欲が原因なら、ダイエット効果があるクレイングラスを食べ放題にする解決策もあるでしょう。
あるいは歯がなんだかムズムズするのが原因なら、定期的にアルファルファキューブをあげてかじらせておけば解決します。
さて、Elmの場合はどうでしょうか?
Elmは自分の意志と哲学、主張をしっかり持っている生き物です。
Elmがやりたがらないことを強制しても、拒絶反応を示してさまざまな弊害を生みます。
たとえば、Elmは生のHTMLを表示することをひどく嫌がります。
そんなElmに無理にHTMLを表示させた結果、バージョン0.19になったときに拒否されてしまい、「Elmで生のHTMLをそのまま表示させる」のような正規の方法で実装し直す必要が生じました。
さらに、自分の願望を押し付けることばかりに夢中で目的を忘れてしまうと、余計な遠回りをすることになります。
たとえばCSSの名前衝突を避ける目的のためだけに、elm-css-modules-loaderなんていう悪いハックを使った結果、生産性を下げ、また0.19になったときにしばらく使えなくなりました。
Elmのありのままの姿を受け入れたら、本当はBEM記法とか気軽な方法で十分でした。
上の方の黒丸が重なるように寄り目で見ると、立体化したさくらちゃんが真ん中に出現するよ!
— 🐐ヤギ魔法少女さくらちゃん(Kadzuya Okamoto) (@arowM_) February 12, 2019
しかもその状態で顔を左右に動かすと、さくらちゃんがこっちを見てくるよ!#さくらちゃん日記 pic.twitter.com/4NrkkGxKoU
知識の探求をやめない
YAGNIは「必要になるまでやらない」ことでした。
「知らないからやれない」ではありません。
覚えたことをついつい使いたくなる気持ちをおさえながら、
同時に知識を探求し、選択肢を増やしていく必要があります。
それトランポリンじゃなくてベッド……#さくらちゃん日記 pic.twitter.com/z5aNNe3EOn
— 🐐ヤギ魔法少女さくらちゃん(Kadzuya Okamoto) (@arowM_) January 27, 2019
たとえば、ヤギの病気については病気になってから調べたのでは手遅れです。
病気についての情報を事前に得ておいて、必要に応じて予防策を打っていく必要があります。
ところで、ヤギにありがちな病気のひとつに腰麻痺があります。
さくらちゃんを迎え入れた頃に、その予防のために北浦和にある、ヤギも診れると書いてある動物病院に問い合わせたところ
「予防薬を入荷したら連絡する」
と言われ、1ヶ月後に確認のため再度電話しても
「入荷に時間がかかっている」
と回答があり、それから今日にいたるまで連絡はありませんでした。
きっとなにかのっぴきならないご事情があったのでしょう。
一方で、浦和の直井動物病院は大変すばらしい病院です。
ふつうの動物病院は犬か猫しか診れないなか、ヤギも診られるくらいに技術もすばらしく、ペットにも飼い主に対してもホスピタリティにあふれ、
情熱を持って治療にあたり、特殊な症例のさくらちゃんのためにアメリカの文献にあたったり、
各種機関に問い合わせながら治療に当たってくださいました。
もちろん、病気についての知識を得ていても、今回のように原因不明の脳炎のような症状で苦しませてしまうこともあるでしょう。
それでも、ご飯の与え方やヒヅメのケア、鼓腸症などへの配慮について、直井動物病院の主治医から認められたことは、私の中でさくらちゃんへの罪悪感を薄めて前を向いて生きる力になりました。
このように、常に知識を求める姿勢は大きな価値をもたらしてくれます。
特にオチがなくてふわふわしてるだけの動画#さくらちゃん日記 pic.twitter.com/xh3he94sFM
— 🐐ヤギ魔法少女さくらちゃん(Kadzuya Okamoto) (@arowM_) December 28, 2018
それではここで知識がなかった頃の恥ずかしいコードをお見せします。
module Util.Maybe exposing (..)
maybe : a -> (b -> a) -> Maybe b -> a
maybe def f =
Maybe.withDefault def << Maybe.map f
maybe-extra の unwrap と同じものをわざわざ定義して使っています。
Elmのコンパイラーは dead code elimination を行うので、1つの関数だけのために気軽にパッケージをimportできます。
特にmaybe-extra
は elm-community
のパッケージなので、使わない理由もありません。
他にも、list-extra
など、 elm-community/*-extra
パッケージのことを知らなかった頃の恥ずかしい思い出です。
別の例もお見せします。
import Html exposing (Html, div)
import Html.Attributes exposing (id, class)
import Html.Events as Events
view : Model -> Html Msg
view model =
div
(List.filterMap identity
[ Just <| class "parent"
, Just <| id "view-main"
, if model.show then
Nothing
else
Just <| Events.onClick ClickViewMain
]
)
[]
条件によってあったりなかったりする属性をあつかいたい場合です。
簡単なテクニックですが、こういう場合には class ""
を使うと便利です。
import Html exposing (Html, div)
import Html.Attributes exposing (id, class)
import Html.Events as Events
view : Model -> Html Msg
view model =
div
[ class "parent"
, id "view-main"
, if model.show then
class ""
else
Events.onClick ClickViewMain
]
[]
もし属性ではなく要素(Html msg
型の値)の方が、条件によってあったりなかったりする場合は、代わりに text ""
を使うと便利です。
最後に、もうひとつ恥ずかしい例をお見せしてこの項目を締めくくります。
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Mask ->
( { model
| goat = maskGoat model.goat
}
, Task.perform (\() -> PostProcess) <| Task.succeed ()
)
PostProcess ->
...
...
ある Message を処理した後に、別の Message を呼び出したいときのアンチパターンです。
次のように書きかえましょう。
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Mask ->
update PostProcess
{ model
| goat = maskGoat model.goat
}
PostProcess ->
...
...
更新後の Model を自分自身(update
関数)にそのまま渡すだけです。
進化を続ける
昔を思い出して恥ずかしくなるのは、自分自身が当時より進化しているからです。
「なぜもっと早くこうしなかったのか」と思うことがよくあります。
たとえば、さくらちゃんのおトイレも何度か進化してきました。
当初はカバー付きの市販のものでしたが、今では二重底で猫砂を敷いてあり、
おトイレに座ってもお腹が汚れず、また頻繁にペットシーツを交換する手間がなくなりました。
Elmにおいても、いま思い返すと
など、「なぜこんなあって当たり前のものを今まで作らなかったのか」と不思議にすら思います。
さくらちゃんもきっと、いなくなるのではなく、新しい概念上の存在に進化するのでしょう。
今までの物理的法則に縛られた体から、みんなの思い出の中を縦横無尽に行き来できる体にレベルアップして生き続けてくれます。
僕も進化を続けるから、見守っててね、さくらちゃん。