セカイノカタチ

世界のカタチを探求するブログ。関数型言語に興味があり、HaskellやScalaを勉強中。最近はカメラの話題も多め

fold沼について

久しぶりに、#rpscalaにて発表をしました。

最近TLなんかでfoldの話しが盛り上がっていたので、foldについて簡単にまとめたスライドです。

fold沼について

あまり、深みのある話しは無いのですが、Markdownで書いたのでブログにも転載しておこうと、下記に貼り付けます。

Scalaのfold

scala.collection.immutable.List なんかについているメソッド

↓使い方

 List(1,2,3).fold(0)(_+_)

定義

foldメソッドは、だいたいここで定義されている。

シグニチャ

def fold[A1 >: (K, V)](z: A1)(op: (A1, A1) ⇒ A1): A1

既知のサブクラスは220個ほど。標準ライブラリのcollectionは、全部foldできる。


その他の定義

検索して調べる。

こんな感じのjsで抽出。

$('.entity a').map((i,n) => console.log(n.innerText))

以下のクラスは、GenTraversableOnceを継承しておらず、独自にfoldを定義している。

※FutureのfoldはDeprecatedで、objectに定義されていて、TraversableOnce[Future[T]]を畳み込むだけなので無視


TraversableOnce.fold

もう一度、シグニチャ。

def fold[A1 >: (K, V)](z: A1)(op: (A1, A1) ⇒ A1): A1

なお、foldLeftのシグニチャ。

def foldLeft[B](z: B)(op: (B, A) ⇒ B): B

違い

  • foldは、並列計算を意識しており、結合則を満たす必要がある(a->b->cを(a->b)->cでもa->(b->c)でも計算できる必要がある)
  • その為、型を変更できない(A->B)

型を変更できない(A->B)

例えば、foldLeftであれば、下記のようにreverseを実装できる

List(1,2,3,4).foldLeft(List.empty[Int])((a,b) => b::a)

foldでは出来ない!(List[Int]の場合、Intの処理しか出来ない)

結論: foldLeft/foldRightの方が柔軟性が高く便利(であるが、並列化可能なため、foldの要求を満たす計算はfoldで実装しておいたほうが良い)


Option.fold

とにかく多用します(当社比)。

User.find(1).fold("unknown user"){u => u.userName}

Option/Try/Eitherという、失敗系のコンテナから値をスムーズに取り出すのに使います。

※ とある方面ではコンテナを失敗系・状態系などと分けたりします。なんと、Listも失敗系に分類されるので、今回登場するコンテナは全て失敗系です。ズコー。


Option map f getOrElse isEmpty

scaladocには下記のような注釈があります。

This is equivalent to scala.Option map f getOrElse isEmpty

これと同義だということです。

カッコが省略されてますね。

Option.map(f).getOrElse(isEmpty)

となります。

mapメソッドは、AからBへコンテナの値を変換するメソッドです。

getOrElseは、OptionがNoneだった場合に返す値を定義しています。


先程の例で言うと、これでも問題ないということです。

User.find(1).fold("unknown user"){u => u.userName}
↓
User.find(1).map(u => u.userName).getOrElse("unknown user")

失敗する可能性のある計算には、OptionやEither, Tryなんかを使いますが、最終的には別の型(Resultとか)で返すというパターンになります。

失敗系の計算を連結するには、map/flatMapが定番ですが、最終的に値が取り出せません。

find(x).map(hoge).map(fuga).map(foo).map(bar)... // どこまでもOption型の計算

値を取り出すための方法の1つがfoldという事になります。

foldの場合、先にデフォルトの値を書き、getOrElseはmapの後に書くことになります。単に好みの問題ですが、(u => u.userName)の部分は、実際には数行になることが多いので、ソースの見通し上foldが好まれるのだと思います。


Either.fold

Eitherのfoldは、結果がLeftだった時の処理、Rightだった時の処理と2つの関数を取ります。

def fold[C](fa: (A) ⇒ C, fb: (B) ⇒ C): C

正直、matchで書くのと大差ないので、matchでいいんじゃ?

hoge match {
  case Left(x) => ...
  case Right(x) => ...
}

Try.fold

Tryのfoldは、結果がFailureだった時の処理、Successだった時の処理と2つの関数を取ります。

def fold[U](fa: (Throwable) ⇒ U, fb: (T) ⇒ U): U

正直、match(ry


まとめると

  • Scalaのfoldは、TraversableOnce系とそれ以外に別れる
  • TraversableOnce系のfoldは結合則を満たす必要があるが、並列化可能
    • そのため計算結果を別の型に出来ない。foldLeft/foldRightの方が柔軟性が高いが用途に応じて選択すると良い
  • それ以外のfoldは、失敗時と成功時の値や処理を振り分けつつ最終的な値を取り出すのに便利
    • getOrElseやmatchを使うかは好み(だと思う)

おまけ

Haskellの場合、foldは、Foldableという型クラスになっている。

class Foldable (t :: * -> *) where
  fold :: Monoid m => t m -> m
  foldMap :: Monoid m => (a -> m) -> t a -> m
  foldr :: (a -> b -> b) -> b -> t a -> b
  foldl :: (b -> a -> b) -> b -> t a -> b

コンテナの中身がMonoidである必要があり、mappendで自動的に畳み込まれる。

Scalaのfoldと同じくMonoidである必要がある(つまり結合則を満たす)。

ブログ版追記

gitter.im

rpscalaの後、Gitterにて吉田さんから突っ込みをいただきました。

発表時点では、foldについて、シグニチャーが「(A,A) => A」となっているので、「型が変えられない!(不便!)」と書いたのですが、畳込みの型が同じであることによって、並列処理可能というメリットがあります。

GenTraversableOnceのscaladocにも、下記のような注釈があります。

The order in which operations are performed on elements is unspecified and may be nondeterministic.

要素に対する実行順は規定されていないということです、つまりバラバラに計算してつなぎ合わせるような実装も想定されているということです。

ちなみに、実際に評価順が並列になるような実装がないか標準ライブラリ内を探したのですが、現時点ではそのような実装は無いようでした。

Scala職人の朝は早く、foldの沼は深い・・・。

輪廻はIOモナドだった。またはIOモナドは輪廻だった

Loop

みなさん、輪廻してますか?

「輪廻とはなんなのでしょうか?」という話題にも興味があるのですが、それは今回は置いといて、この話は完全にネタです。^^;

まず、輪廻と一口に言っても、仏教だけの用語ではなく、インド方面に広く浸透している考え方なので、様々な解釈があり得ると思います。

そのため、ここでは、輪廻のモデルを「仏教思想のゼロポイント: 「悟り」とは何か」から引用します。

まず、「無我」であるところの衆生が輪廻する仕組みについて確認しておこう。この点については、明治生まれの仏教学者で、かつての東京帝国大学教授であった木村泰賢による図式がわかりやすいので、次にそのまま引いておこう。

A-A'-A''-A'''-An...anB-B'-B''-B'''-Bn...bnC-C'-C''-C'''-Cn...cnD......dnE...

ここにA、B、C、D、Eとあるのは、木村の用語で言えば「五蘊所成の模型的生命」、さきほどの私の解説の言葉で言えば、「認知のまとまり」もしくは「経験我」に当たるものである。例えば「太郎」という名前の人がいたとして、それも実際には縁生の五蘊の和化合であり、したがって誕生時から死没時まで常に変化を続けていて、そこに固定的な実体は存在しないのだが、いちおうその「変化する認知のまとまり」を、仮に「太郎」(右の図ではAあるいはBなど)と一貫して名付けておくわけだ。

そして、A-A'-A'‘-A’‘’-Anというのは、もちろんそのAの時系列にしたがった変化を表す。右に述べたように、Aと名指される認知のまとまり(経験我)も、実際には縁生の現象に過ぎないから、それは時々刻々と(仏教用語を使えば刹那ごとに)流動・変化を続けており、それは誕生時から死没時まで続くわけである。

そのように変化し続けるAは、ある時点(An)で死を迎える。そこで起こるのが転生である(図式の…はそれを示す)。そこでBという新しい五蘊の和化合を得たとすると、その形は大いにAと相違しているようではあるが、そこにはやはり、anというAの「経験的積聚」、即ち、Aの積み重ねてきた行為(業)の結果が、潜勢力としてはたらいている。そしてそのBという認知のまとまりが、また刹那ごとの変化を続けていく(anB-B'-B'‘-B’‘’-Bn)。

仏教思想のゼロポイント P92-93より

つまり、輪廻というのは人の生死にかかわらず、刹那(非常に短い時間)の間でも「少しづつ」輪廻しているということです。

我々の体をとってみても、同じ主体でありながら、時間とともに全く同じということは無く、少しずつの変化が積み重ねられています。そして、それがある時、生命としての身体は朽ちて、次の主体に移り、そこでまた輪廻していくという考え方になります。

ここでは、この考え方を輪廻として捉えます。

IOモナド

ここで話を一旦変えて、IOモナドについてです。

qtamaki.hatenablog.com

モナドが何かについては、今までも色々な人が挑戦しては微妙に手応えがない感じになるという難解な問題なので、以前に書いたブログを晒してお茶を濁したいと思います。

IOモナドの詳細については、専門的になりますので割愛しますが、正確さを犠牲にして単純化すると、こんな感じのものです。

世界 → 処理1 → 世界' → 処理2 → 世界'' → 処理3 → 世界'''

つまり、「世界を受け取って何らかの操作を行い、変化した世界を返す処理」があった時に、その処理を色々つなげて連鎖していく事により、複雑な処理をあたかもブロックを組み合わせるかのように作り上げていくことが出来るという、便利なプログラミングテクです。

僕は、先程の「輪廻の図示」を見た時に「ピン」と来てしまいました。

この形、何かに似ています。

そうです、輪廻の図とIOモナドの図が驚くほどそっくりなのです!

輪廻というと、どうしても「個人の魂」のような物の変遷を思い浮かべてしまいがちですが、少しづつ変化する主体というものは無い、つまり無我なので、個人と世界の境界線というものは案外あやふやだったりします。

IOモナドは、世界を受け取り、ちょっとだけ変化した世界を返します。この世界の変遷を定義した処理があれば、それは「輪廻と区別がつかない」という事になりうる可能性があります。

IOモナドは、あくまで「コンピューターの世界の中のプログラムの世界の中の関数の世界」と現実世界を結ぶために考え出された仕組みに過ぎず、実際に世界全体を受け取るほどのキャパはありません。^^;

が、原理上はIOモナドによって輪回を定義でき、世界をも定義できるということです。

そして、もし貴方がプログラマーでモナドに関する知見を有するのであれば、輪廻の仕組みが、思ったよりも簡単なものだと認識されたかもしれません。

自分自身を定義するIOモナドアクションを定義することも夢ではないかもしれないですよ?

・・・という事で、適当な思いつきを文章にしてみただけのテスト。でした。

真理というのは、色んな形をしていて、相似的な構造をしていると、僕は思います。

こんなヘンテコな組合せでも、裏に潜む本質的な構造について思いを巡らすのも面白いかもしれません。

仏教思想のゼロポイント: 「悟り」とは何か

仏教思想のゼロポイント: 「悟り」とは何か

Amazon Dash Buttonをエマージェンシーコールボタンにしてみた

f:id:qtamaki:20170323151509j:plain

キュキュット Dash Button

キュキュット Dash Button

エリエール Dash Button

エリエール Dash Button

さて、昨年末に日本で発売された Amazon Dash Button ですが、「どうやらハックできるらしい」ということを知って、買ってみました。

本当は、12月に買ってあったのですが、色々忙しくて三ヶ月ほど放置していましたが、「エマージェンシーコールが欲しいな」と思い、簡単なハックをしました。

直接のきっかけはコレ。

東京の自宅に四歳の息子がいて、自分は酒田に居ることが多いため、母親(かみさん)が倒れると、この漫画の状況になります。

これは泣ける。

ということで、今回実現したい仕様は「Amazon Dash Button が押されたら特定のアドレスにメールが届く」です。

簡単そうですね。

主に参考にしたのは、この記事です。

qiita.com

実際には、こちらの記事でも使用している「node-dash-button」というライブラリを使用しました。

動作原理

上記の記事にもありますが、簡単に動作原理を説明します。

前提

Amazon Dash Button は、初期設定でWifiの接続設定を行うようにできており、ボタンを押すと、インターネット経由でアマゾンに1-Click注文処理を投げます。node-dash-buttonは、この時の通信を「傍受」して別の処理を行います。Amazon Dash Button には一切修正を入れずにButtonが押された時の処理を定義できます。ただし1-Click注文を未設定状態にしておかないと、「注文されます」(^^;

動作

  1. Amazon Dash Button を押すとネットワークの接続を確認するため信号(arp probe)が出ます
  2. この信号を監視するサーバーを立てておき、この信号をリアルタイムに検知します
  3. MACアドレスによって、特定の Amazon Dash Button であることが判定可能なので、特定のMACアドレスから信号が来たら定められていたアクションを行います

原理は簡単で、「node-dash-button」は、dash-buttonという名前がついていますが、他のネットワーク機器(スマホやPC)でも、接続されたときにアクションを定義できるはずです。^^;

Amazon Dash Button の設定

Amazon.co.jp ヘルプ: Dash Buttonの設定を管理する

こちらの設定ページの通り設定します。ただし、最後の商品選択のところで、処理をやめて放置します。^^;

最後まで設定してしまうと、もちろん 商品が届きます

と、ここまで書いてて思ったのですが、「商品が届いてしまう」という副作用を無視すれば、アマゾンから注文メールが届くので、無改造で「エマージェンシーコール」として機能するな。

子供に持たせる場合、誤操作で商品が届くとちょっとアレですが。^^;

Amazon Dash Button のMACアドレスを知る

Amazon Dash Button のMACアドレスを調べるのはそこそこダルいですね。

僕は、node-dash-buttonを起動して、表示されるMACアドレスを使いました。

以下のような手順です。

git clone https://github.com/hortinstein/node-dash-button.git
cd node-dash-button
npm install
sudo /path/to/node/node/bin/node bin/findbutton

sudoが必要なので、nodeのパスを絶対パス指定しないと動かないと思います。

こんな感じのログが表示されます。

Possible dash hardware address detected: 34:d2:70:6f:e5:c0 Manufacturer: unknown Protocol: udp
Possible dash hardware address detected: 34:d2:70:6f:e5:c0 Manufacturer: unknown Protocol: arp
Possible dash hardware address detected: ac:63:be:9a:29:3d Manufacturer: Amazon Technologies Inc. Protocol: udp
Possible dash hardware address detected: ac:63:be:9a:29:3d Manufacturer: Amazon Technologies Inc. Protocol: arp

ボタンの機体によって、プロトコルの表示が違うのは謎です。

実施

さて、準備はこんなもんなので、プログラムを書いて「node-dash-button」と「Nodemailer」を使って、ボタンが押されたら特定のアドレスにメールが届くようしてみました。

github.com

注意点としては、ARPを監視するため、 管理者権限で実行しないといけない ということです。

そして、ARP監視に、「libpcap-dev」を利用するため、MacやWindowsでは動かないと思います。VMを立てるか、Linux(Debian系が良いと思う。僕はUbuntuでやりました)を利用しましょう。

あと、Nodemailerが、Node.js V6.x以降でないと動かないので、パッケージから入れたnode.jsでは動かないと思います。本家のサイトから最新版をDLしてきて入れましょう。

npm install
sudo /path/to/node/bin/node main.js

とするだけで動きます。

main.jsの中身も相当ベタに書いてあるので、難しいことはないと思います。^^;

fromのメールアドレスや、Amazon Dash Button のMacアドレスの部分は書き換えてください。

一応、動作確認したときに届いたメールのスクショを貼っておきます。

f:id:qtamaki:20170323161601j:plain

JavaとGenericsと私(と、時々モノイド)

mattn.kaoriya.net

というわけで、通りすがりに見かけた記事ですが、Genericsの使い方について、ちょっと引っ掛かったのでコードを書いてみました。

import java.util.List;
import java.util.Arrays;

public class Foo {
    public static <T> T sum(List<T> list) {
        int ret = 0;
        for (T i : list) {
            ret += i; // コンパイルエラー
        }
        return ret;
    }

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(5,2,3,1,4);
        System.out.println(sum<Integer>(list));
    }
}

元のコードがこんな感じで、コンパイルエラーなんですけど、対処方法として、謎のインターフェースを定義して、型キャストとかも発生していたので、こんな感じにしたほうが良いよ?と言いたかっただけです。

import java.util.List;
import java.util.Arrays;

interface Addable<T> {
    T add(T a, T b);
    T zero();
}

public class App {
    public static <T> T sum(List<T> list, Addable<T> adda) {
        T ret = adda.zero();
        for (T i : list) {
            ret = adda.add(ret, i);
        }
        return ret;
    }
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(5,2,3,1,4);
        System.out.println(sum(list, new Addable<Integer>() {
            public Integer add(Integer a, Integer b) {
                return a + b;
            }
            public Integer zero() {return 0;}
        }));
    }
}

まあ、関数のシグネチャーが変わってしまっているのですが、「加算」を抽象化したインターフェースを定義して、そいつを渡してあげると型キャストも発生せずにコード量も増えすぎず、Genericsな感じで拡張できます。

Doubleの加算をしたいときも、上記で匿名クラスを渡している部分を下記のようにするだけです。

new Addable<Double>() {
    public Double add(Double a, Double b) {
        return a + b;
    }
    
    public Double zero() {
        return 0.0;
    }
}

まあ、これってもろモノイドなんですけど・・・。

ちょっと本質的な話

コンビニにおやつを買いに歩いてたら、ちょっと考えがまとまったので追記します。

元記事のコードと僕の挙げたコードの違いは何でしょうか?

元記事では、「Number」や「Int」といった「データ」を抽象化していました。

それに対して、僕のコードでは、「加算」という「計算」を抽象化しています。

「計算」を抽象化するという概念は、関数型言語ではオーソドックスなスタイルであり、そのために型クラスという概念が日常的に利用され、アドホック多相を実現しています。

これに対して、オブジェクト指向では、「対象」を抽象化するというアプローチが一般的に採られ、「データ」を抽象化する傾向にあります。

このアプローチは、処理をうまく抽象化できず、歪を生みます。

歪を正すために更に大きな歪みを作り続ける袋小路に陥ることもよくあります。

僕自身、関数型言語の考え方を学ぶ前は、オブ脳的な考えで、設計や実装を行っていました。

その頃は、「なぜ処理が冗長になってしまっているのか?」理解できませんでした。むしろ、「これが限界なんだ」と自分に言い聞かせるようにコードを書いていたのかもしれません。

関数型プログラミング賛辞のような文章になってしまいましたが、「万能ですごい!」と言うつもりもないですし、僕自身関数型プログラミングに対してそんな感情を持ちません。

ただ、両方のアプローチを理解することで、確実にコンピューティングが豊かになることは間違いないと思います。

FirefoxにWebAssemblyが搭載されたらしいので試してみた

Firefox、ゲームなどをネイティブ並に高速実行する「WebAssembly」を採用 - PC Watch

こんな感じで、PC系のニュースサイトで話題になっているので試してみた。

WebAssembly とは、ブラウザ上でバイトコードを実行できるようにする標準規格で、今まで実質的にJavaScript一択だったブラウザ上のプログラム実行環境を拡張するものとして、とても期待している。

qiita.com

とりえあえず、「WebAssembly」でググると一番上に出てくるこの記事を参考に、公式サイトっぽいところを覗いてみる。

Developer’s Guide - WebAssembly

WebAssembly のコンパイラをインストールして、C言語のサンプルコードを走らせるガイドになっていた。

Windows, MacOS, Linuxがサポートされているようで、Windowsのサポートがおもったより手厚いのは、マイクロソフトの支援が入っているからかな?

Windows上でビルドするのは心許ないので、VM上のLinuxでビルドしてみることに。

とりあえず、下記の環境が必要とのことなので揃える。

  • CMake
  • gcc
  • python(2.7.x)

コマンド実行。

$ sudo apt-get install -y build-essential cmake

phtyonは、もとから2.7系が入っていたので準備オッケー。

$ git clone https://github.com/juj/emsdk.git
$ cd emsdk
$ ./emsdk install sdk-incoming-64bit binaryen-master-64bit
$ ./emsdk activate sdk-incoming-64bit binaryen-master-64bit
$ source ./emsdk_env.sh

ほぼおまじない通りに実行すれば環境が整う。

ただし、VMにメモリを4GB割り当ててたんだけど、足りなくてリンカが落ちた。8GBにしたら通ったので、メモリは多めに積んでおきたい。

$ mkdir hello
$ cd hello
$ echo '#include <stdio.h>' > hello.c
$ echo 'int main(int argc, char ** argv) {' >> hello.c
$ echo 'printf("Hello, world!\n");' >> hello.c
$ echo '}' >> hello.c
$ emcc hello.c -s WASM=1 -o hello.html

サンプルコードのコンパイルもおまじない通り。

組み込みのhttpサーバーを立ち上げれば、実行される様子を見ることができる。

$ emrun --no_browser --port 8080 .

これは、単なるhttpサーバーっぽいので、apacheやnginx、http-serverなど好きなものを使ってOKっぽい。

一応スクショを貼っておくけど、実際 WebAssembly が動いているところは、「Hello, world!」と表示されている部分のみ。それ以外の部分は、コンパイラが吐くhtmlで描画されている。はっきり行って、htmlコードもゴテゴテしていて、どの部分が WebAssemblyに最低限必要なコードなのか全く不明。^^;

f:id:qtamaki:20170309144650p:plain

先のコンパイラでは、出力ファイルに「hello.html」を指定しているが、実際には、「hello.html, hello.js, hello.wasm」の3つのファイルが出力されていて、核心となるのは、hello.wasmのみ。

htmlとjsは、実行画面をそれっぽく見せるためのスタブだ。

これだけだと、本当にWebAssemblyが実行されているのかわかりにくいので、最初の記事で例示されていたデモサイトの動作も確認してみた。

https://1984weed.github.io/webassembly-sample/src/nqueen/

Firefox 51.0.1でアクセスすると、このようにWebAssemblyオブジェクトが見つからないというエラーが、コンソールに出て「Wasm execute」ボタンが機能しない。

f:id:qtamaki:20170309145341p:plain

Firefox 52ならば、そのようなエラーは出ないし、「Wasm execute」が実行できるのでちゃんと動いているようだ。

f:id:qtamaki:20170309145404p:plain

WebAssemblyをサポートしてるかどうかは、「WebAssembley」オブジェクトの有無で確認できるってことみたいだ。

そして、このデモサイトは、ChromeのCanary版を想定して書かれたようなので、クロスブラウザで動作していると言うことも確認できた。流石は標準規格。

動作方法について端的に説明すると、上記のようにソースコードをコンパイルしてバイトコードを作ったら、WebAssemblyオブジェクトを利用して、下記のようにコンパイル→インスタンス化すれば良いらしい。

function instantiate(bytes, imports) {
  return WebAssembly.compile(bytes).then(m => new WebAssembly.Instance(m, imports));
}

あとは、バイトコードをXHRやfetchAPIで取得して、バイト列を取り出してこの関数に食わしてやればよい。importObjectは、バイトコードに渡す引数だか変数だかになる。

fetch('simple.wasm').then(response => response.arrayBuffer())
  .then(bytes => instantiate(bytes, importObject))
  .then(instance => instance.exports.e());

あと、メモリの管理は、バイトコード側でしなければならなくて、めんどくさそうだ。

var memory = new WebAssembly.Memory({initial:10, maximum:100});

とりあえず、今日のところはこんな感じで、概要はつかめた感じがするのでここまで。

ThinkPadのBluetoothキーボード KT-1255 の使い方メモ

毎度使い方を忘れてググっても碌なのがヒットしないので、端的に簡潔にメモしときます。

  • 電源ON
    • スライド1秒
  • 電源OFF
    • スライド3秒
  • ペアリング
    • 電源ON
    • パソコンから接続
    • パソコンに表示されるパスコードをキーボード側で入力(認証)
    • NFCタグによるペアリングも可能(キーボード左下隅)
  • ペアリング解除
    • Fn + Delete
  • ドライバ
  • ファンクションキーを反転で固定
    • Fn+Esc

ラップトップのキーボードとしては最高に使いやすいんだけど、フルサイズキーボード+マウスには歯が立たないため、お蔵入り気味。

テストしようよ

先日、息子と遊んでいると私達が「作品入れ」と呼んでいるファイルを持ってきて、私にこう言った。

「ここのぶぶんにはいる紙がないんだよねー」

そこで私は、部屋に戻ってA4サイズのコピー用紙を持ってきて提案してみた。

「これなら入るんじゃない?」

すると、息子から驚くべき返答が返ってきた。

「じゃあ、テストしてみよう」

「!!??」

マジ?

今「テスト」って言った?

しかも、学校の試験とかの意味じゃなく「試してみる」って意味で使った?

恐る恐る息子の真意を確かめる。

「テストって?」

「紙が入るかどうかテストしてみようよ」

なんと、事を起こす前に事前に問題点がないか確認しようとしている。

4歳の息子が。

これは、下手するとそこらのSEより賢いぞ。どこで覚えたんだそんなこと。

息子とのエピソードをブログに書いたりすると親バカだと思われかねない(もう手遅れかもしれないが)ので、なるべく控えるように心がけてきたのだけれど、これは流石に驚いた。ブログに載せてしまおう。そして、誤解を恐れずこう言い放ってしまおう。

うちの子、天才かしら?

いい大人たちが仕事で失敗するのは、大概事前に確認せずに事を進めるからだ。

良い仕事というのは段取り8割というが、実際に「動くのか?」「うまくいくのか?」について事前に確認していれば、8割方のトラブルは回避できる。

システム開発であれば尚更、テストやリハーサルというものが重要になってくる。

今回の息子の件は、単なる気まぐれや偶然だったかもしれないが、果たして何人の人間が事前確認を思いつくだろうか。

かくいう私も、今回のタイミングで「テストが必要」という認知には至らなかった。

そして、実際A4のコピー用紙は、「作品入れ」には少し大きく、はみ出した部分をハサミで切る必要があった。

息子は絵を描く前にその事に気づき、無駄に大きくはみ出した絵を描くこと無く、ファイルに作品をしまうことができた。

それは、日曜の昼下がりに起きた小さな奇跡の一コマだった。

f:id:qtamaki:20170221091033j:plain