yunomuのブログ

趣味のこと

手帳とノートと私

システム手帳というデバイスは、並列した大量の情報を処理するために機関銃が一般化して散兵戦が主になった頃の軍で生まれた、「つまりシステム手帳はミリタリーアイテムなんだよ!」って軍オタが言っていたのを聞いたことがありますが、本当かどうかは知りません。

私も何度かシステム手帳を持ってみようとしたことがあったんですが、あまり役に立った事はなく、それもそのはず処理しなけりゃいけない情報そのものがそんなに多くなかったし、幸い私は記憶力が良かった。両親をはじめご先祖様ありがとう。思えば身にかかる問題のほとんどを記憶力で解決してきた気がします。

まあそれはどうでもいいんですが、
ただシステム手帳が役に立ったことがなかったわけでもなくて、就職活動中なんかは役に立ちましたね。並列する時系列のイベント群の例として非常にわかりやすいんじゃないかと思います。テーマというか会社ごとにスケジュールやメモを並べて整理しつつ、移動中にも読み書きできる。まさにシステム手帳の本領発揮。
いやそういう事やった時は結局就職しなかったんですがね。実際に就職した時には並列して受けてないし、手帳はリストラしてスケジュール帳しか持ってなかった。いやスケジュール帳も持ってなかったかも。携帯とGoogleカレンダーがあったしねぇ。

あと手帳が活躍したのは就職後、会社勤めは大っぴらに遊ぶわけにはいかない状況(研修とか)が増えますので、その時の遊び道具として。熱心にメモを取ったりアイデアを練っていると見せかけて、今ここに書いているようなしょうもない文章を書いていたりする。してました。
授業中の暇つぶしといえばペンの分解と落書きが定番ですが、大人になったら万年筆の手入れとブログの記事作りになった。人間は変わらない部分もある。

この時に使っていたのはA6ノートで、理由は当時流行っていたからというか。3年くらい前、本屋のビジネス書とかのコーナーで「100円ノートの活用法」とか「メモの取り方」みたいなのが並んでませんでしたっけ。並んでました。今もあるのかな。
中身はあんまし見てないというか覚えてないんですが、持ち歩くならA6ってちょうどいいんですよね。文庫本のカバーが使えたりするし。

システム手帳ではなくノートを選んだ理由のもうひとつが、新人に締め切りが厳しい並列タスクが多数降ってくるわけでもなし、スケジュールも仕事メモもゲームの感想も全部ひっくるめて一つのノートでいいかな、研修中は自分のデスクなんてのも無いし……、と思っていたらほぼ落書き帳として4ヶ月で10冊くらい消費してしまったんですが、まあそれはそれとして。
ただこの点が結構キモで、
スケジュール帳はスケジュール管理にしか使えないけども、メモ帳やシステム手帳はスケジュール帳から落書き帳へと華麗に変身することができる。いやしない方がいいんですけど、システム手帳ならさらに落書きを無かったことにできるわけですし。素晴らしいですね。

それはそれとして、今の私にはデスクもあるし、それほど情報が溜まって忙しいわけでもないので、B5のノートを会社に置きっぱなしにする運用をしています。なんか思いついたら自分にメールを送ればいいし。
ただ最近というか、プライベートでもノートを使いたいと思う事があって。なんかプログラム書いてる時とか。でも会社のノートはアレやソレな情報が色々あるからあんまし動かしたくないし。

んで別にノート買っても良かったんですが、最近iPad買ったのでせっかくだから使えないかなぁと思ってあれやこれや試しているところです。この記事の元はiPad+スタイラスペン+手書きアプリで書かれています。

ここからが本題。iPad買いました。
Retinaモデルの9.7インチ。miniもいいけどちょっと小さいかなと思って。

なんとなくタブレット使ってみたいなぁと思って事前にタブレット使ってる友人数人に聞いてみたところ、妙なソフトを入れたり妙なデバイスをつないだりしないならiPad一択じゃないのという感じだったのでiPadにしました。
買ってみた結果、サイズはまあ人に見せたりするならこのサイズだろうけど、寝ながら使うとか歩きながら使うならminiがいいんじゃないでしょうかという感じですね。いやminiは使ってないのでわかんないんですが、ついでに他のタブレットの事も知りません。
大きくていいところは、画面が大きいことですね。字も大きく書ける。

手書き性能については、スタイラスペンはBAMBOOを使っていますが、これは結構いい。ただ私は普段は万年筆を使っているので、ペンを立てて少し力を入れて書くのに慣れなくて最初ちょっと手こずりました。シャーペンやボールペンに近い感じじゃないかと思います。

アプリは無料のUPAD Liteを使っています。評判よかったので。
あとは有料ですがNoteshelfというのも書き味が良いと評判で、実際良かった。誤入力防止機能(手をついても入力されにくくなる)があるので電車内で使うならこっちかなぁ。どちらにしても電車内では使いづらいですが。
システム手帳的に使うなら何にしても有料アプリを買った方が良さそうでした。

慣らし運転のつもりがずいぶん書いてしまいました。
テキスト起こし面倒かと思ったけど推敲だと思えば大した手間でもないですね。

やはり来世は文具屋になるべきでは。

ノートにこだわりが出てくる

久しぶりに日記的な文章を。あ、お酒は飲んでません。

最近、主戦場が徐々にソースコード以外の部分にも及び始めていて、それにともない武器の優位性や重要度も変化しつつあって、つまりノートをまたよく使うようになってきました。雑多な色々を整理したいならいまだに手書き最強な気がします。ここ半年くらいはソース書いてるだけだったのでほとんど要らなかったんですが。
メイン武器じゃないなら別に大してこだわる必要はなくて、例えばノート以外だと半年前までVimはvimrcすら書いてなかったしキーボードも安物でしたし。うーん何の仕事してたんだろう。

で、ノートの話です。

仕事でノートを使いたい場合は会社が支給してくれるんですが、そのノートが2種類あって。ひとつは会社オリジナルのノート、もうひとつは普通のいわゆる大学ノート。
私はずっと大学ノートの方を使ってたんですが、最近会社ノートがリニューアルされて、そのタイミングで大学ノートの方が支給されなくなってしまって、会社ノートしか無いのでとりあえず使っていたんですけど、非常に使いづらくてイライラする。
というかそもそもノートくらい使いやすいの自分で選べよという気もしますけどね。会社にいる時の仕事にしか使わないので、これで年収が増えるとかいうわけではないんだけど。

まあ前置きはいいとして、
会社ノートが気に入らないということは逆になにがしかこだわりがあるんじゃないかなと思って、わざわざコンビニとかではなく文具屋に出かけていくということは漠然と良いノート像があるんだろうなぁ。いや文具屋はコンビニより近所にあるんですが。せっかくだから銀座の伊東屋にも出かけてみようかなぁとかいう気分になるというのは、こだわりがあるんでしょう。

ということで何にこだわっているのか書き出してみることにしました。

大きさ

A6からB5くらい。
会社でしか使わないし移動しながら使うこともないので小さい必要は無くて、むしろ1日分とか1テーマ分とかを1ページに収めたいので大きくていい。でも打ち合わせとかそれなりに持ち歩くし、机が無い時もよくあるからあんまし大きくない方がいい。A4は大きすぎる。
仕事以外でも使ってて持ち歩いてた時はA6が良かったですけど、それより大きくてB5くらいまでがいいですね。

枚数・重さ

1テーマで60ページ使い切るとかいう事は滅多に無いので30枚でもいい。むしろ移行時期に2冊持っててもいいくらいの厚さ軽さがいい。
ただまあ40枚とか60枚とかでも別にいい。分厚くなければ。
FreeBSDのソース読んでた時は一瞬で1冊埋まりましたけど。

強度

必要ならカバーを掛けるしそんなに必要無いんだけど、あんまし弱いのは面倒くさいよねぇという感じ。逆に厚くなったり重くなったりしない程度でいい。

綴じ

こだわってない気がする。
切り取れるようにミシン線なんかはついてない方がいいです。人に渡すなら別途清書するだろ。

罫線

横罫の6〜8mmくらい。8mmはちょっと大きいかなぁという気もするけど、どうせこれに沿って書くわけじゃないし。無視するのに必要なのかというと、やっぱりあった方が書きやすい気がする。少しは横に揃うし、ベースが揃うとそれだけで結構見やすくなりますよね。
5mmはB5だと小さすぎるかなぁ。小さいと本気で無視しちゃうのであまりよろしくない。
方眼も同じくうるさすぎて無視してしまう。

上余白

上部には余白が必要。まあこれは普通にテーマとか項目名を書くからなんですが、できれば余白みたいな形で広くて一定の大きさが確保されている方が良い。
白紙だとつい上から書いちゃうので、そういう意味でも罫線ついた上での余白である方が嬉しい。
日付とかなんとかの欄は端にある分にはいいけど、自由度高い方がいいです。

横余白

横の余白はできれば無しで、罫線が端まで印刷されてるのがいい。1ページ目を目次にしてテーマごとに耳を塗りつぶしたりするから。
最近のメモの書き方みたいな本によく載ってますが結構便利ですよね。

紙そのものがどうというよりペンやインクとの相性なんですが。裏写りしないとか乾きが良いとか、当たり前のようにその方がいいよねという話ですね。
ペンは万年筆で顔料系のインクを使ってますが、まあコクヨのCampusノートとかでも普通に良かったのであまり気にしてないといえば気にしてない。

白じゃなくてちょっと色がついてる方がいいかなぁという気はするものの、別にいいや。
白に黒インクだと緊張するというかちょっとひっかかる人というのは結構多いらしいというか、私もその口なので、私はインクをブルーブラックにしてるというのもある。
文具屋に行くとクリーム色系のノートも結構多いですよね。

こんなものかなぁ。
案外こだわってる気がしますけど、まあおおむね普通の大学ノートで良いという話です。Campusのスリムシリーズ横罫6mmとかは割と最強なんじゃないかと思っています。
まとめると会社ノートの気に食わない所は、上下に余計な線とか項目が印刷されている事と、重要な場所に会社ロゴが入っていてものすごく邪魔な事と、何故かインクが乾かずに汚れる事です。
議事録を書くだけならPCでよくて、スケジュール帳にもTODO管理にも日記にもスワップ領域にもできるのがノートの良さでしょう。

目下の悩みは、ソース書いてるだけならissue trackerにメモればいいんですが、それ以外の問い合わせ対応とかなんとかをやりはじめた瞬間にメモが全部手元のノートになってしまって、周りに見えなくなってしまう事ですね。清書して公開するというのがまともな解決策ではあるんですが、そんなちゃんとしたものでもないし。公開しようがしまいが結局このブログみたいな口語体で書いてるわけだし、できればそのままアップロードできるといいんだけどなぁとか。このページに書き込むとGitHubに反映されますとかだと素敵ですね。

よし、来世は文具屋になろう。

record update

Haskellのrecordを使っているとこういう事をよくやる。

data Test = T { a :: Int, b :: String }

updateA :: Test -> (Int -> Int) -> Test
updateA t f = t { a = f (a t) }

Test型のデータのaをf関数を使って更新したい。

例えばほら、Stateにはmodify関数があるわけじゃないですか。

modify :: (s -> s) -> State s ()

という感じで、sを更新する関数を引数にとる。実際とはちょっと違うけど。でもレコードでもこういう感じの事がしたい。

具体的にはこう。

update name f rec = rec { name = f (name rec) }

recordのフィールドラベルまで引数として渡したいわけです。これコンパイルできないんですけどね。

さてどうしたものだろうかと思っていたところ、2010年にHaskell-cafeで同じ事を言っている人がいらっしゃいました。
[Haskell-cafe] record update
どうやらよくあるどうしようもない話題みたいです。最初の人がやりたいことと大体同じで、例としてState.modifyを挙げるところまで同じというかまさに私もsomeStateTransformみたいな事が頻出するコードを書いていてどうにかならんもんかと思って調べ始めたんですけど。
Jonathan Geddesさんの例:

someStateTransform :: State MyRecord ()
someStateTransform = do
     modify $ \record->record{field1 = (++"!") $ field1 record}
     ...

これは書きたくないよねぇ。

で、このHaskell-cafeのrecord updateスレは結構盛り上がってて、いろんな案が出てきたんですが決定打が出ないまま終わってしまいます。話自体は結構盛り上がってて、いろんなライブラリが紹介されててそれはそれで面白かったんですが。

しょうがない、やるか。

-- \f r -> r { label = f (label r) }
upd :: Name -> ExpQ
upd label = do
    f <- newName "func"
    r <- newName "rec"
    lamE
        [varP f, varP r]
        (recUpdE
            (varE r)
            [(,) label <$> [|$(varE f) ($(varE label) $(varE r))|]]
        )

これを使ってこう。

data Test = T { a :: Int, b :: String } deriving (Show)

main :: IO ()
main = do
    let test = T 1 "abc"
    print test                  -- => T {a = 1, b = "abc"}
    print ($(upd 'a) succ test) -- => T {a = 2, b = "abc"}

TemplateHaskellで作ればいいじゃないという。いいんだけどなんか、微妙に微妙。
exercises/record/Main.hs at master · yunomu/exercises · GitHub

そしてやっぱり似たような事を考える人はいるのでした。
mkEditor - Data.SemanticEditors
この場合は式じゃなくて関数定義を作ってますけど。

どがんかならんでしょうか。

jhcをビルドする

環境:Mac OS X 10.8.2, GHC 7.4.2

jhcはだいたい下記の記事に書いてあるとおりなんですが(autoreconfやaclocalが無い時はautoconfとautomakeをMacPortsとかでインストールするとたぶん大丈夫)、いろいろ引っかかりましたので。
簡約!λカ娘(4)の紹介とjhcのすゝめ - Metasepi

darcsは公式サイトから落としてくる。
Darcs - Download Darcs
バイナリで配布されているので、解凍してパスの通ったディレクトリに置くだけ。

で、いざやってみると、

% darcs get http://repetae.net/repos/jhc
% cd jhc
% autoreconf -i
% ./configure
% make
(略)
DrIFT src/C/FFI.hs -o drift_processed/C/FFI.hs
make: DrIFT: No such file or directory
make: *** [drift_processed/C/FFI.hs] Error 1

という感じになる。DrIFTとはなんぞや。

cabalで探すとそれらしいのが見つかる。

% cabal list DrIFT                                                                                                                                        [jhc]
* DrIFT-cabalized
    Synopsis: Program to derive type class instances
    Default available version: 2.2.3.2
    Installed versions: [ Unknown ]
    Homepage: http://repetae.net/computer/haskell/DrIFT/
    License:  BSD3

* pugs-DrIFT
    Synopsis: DrIFT with pugs-specific rules.
    Default available version: 2.2.3.20120717
    Installed versions: 2.2.3.20120717
    Homepage: http://pugscode.org/
    License:  BSD3

よくわからないけどDrIFT-cabalizedはインストールできないし、pugs-DrIFTは違うものっぽい。
けどもサイトの方にソースアーカイブがおいてあるので、それを取ってきて自分でビルドすればいいじゃないということになる。
http://repetae.net/computer/haskell/DrIFT/drop/

% wget http://repetae.net/computer/haskell/DrIFT/drop/DrIFT-2.2.3.tar.gz
% tar xvzf DrIFT-2.2.3.tar.gz
% cd DrIFT-2.2.3
% make
Making all in src
make  all-am
/usr/bin/ghc  -i. -i. -hidir . -odir . -o DrIFT --make ./DrIFT.hs
  
DrIFT.hs:20:18:
    Could not find module `System'
    It is a member of the hidden package `haskell98-2.0.0.1'.
    Use -v to see a list of the files searched for.
make[2]: *** [DrIFT] Error 1
make[1]: *** [all] Error 2
make: *** [all-recursive] Error 1

うえええ

どうもhaskell98に依存してるっぽくて、cabalにあったDrIFT-cabalizedがインストールできないのもここ関係のもよう。
仕方ないのでcabalファイルを書く。こんなものでよかろう。
DrIFT.cabal

name:                DrIFT
version:             2.2.3
build-type:          Simple
cabal-version:       >=1.8

executable DrIFT
  main-is:             DrIFT.hs
  build-depends:       haskell98
  hs-source-dirs:      src

これを使ってビルドする。

% cabal-dev build
Building DrIFT-0.1.0.0...
Preprocessing executable 'DrIFT' for DrIFT-0.1.0.0...
[ 6 of 23] Compiling GenUtil          ( src/GenUtil.hs, dist/build/DrIFT/DrIFT-tmp/GenUtil.o )

src/GenUtil.hs:486:18:
    Could not deduce (Show a) arising from a use of `st'
    from the context (Integral a)
      bound by the type signature for
                 showDuration :: Integral a => a -> String
      at src/GenUtil.hs:(486,1)-(491,28)
    Possible fix:
      add (Show a) to the context of
        the type signature for showDuration :: Integral a => a -> String
    In the first argument of `(++)', namely `st "d" dayI'
    In the expression:
      st "d" dayI ++ st "h" hourI ++ st "m" minI ++ show secI ++ "s"
    In an equation for `showDuration':
        showDuration x
          = st "d" dayI ++ st "h" hourI ++ st "m" minI ++ show secI ++ "s"
          where
              (dayI, hourI) = divMod hourI' 24
              (hourI', minI) = divMod minI' 60
              (minI', secI) = divMod x 60
              st _ 0 = ""
              st c n = show n ++ c

これは普通にコンパイルエラーを直してしまう。。
src/GenUtil.hs:486行目のshowDuration関数のシグネチャでクラス制約を付け足せばよい。

- show Duration :: Integral a => a -> String
+ show Duration :: (Integral a, Show a) => a -> String

あとは普通にビルドできます(たぶん)。

% cabal-dev build
...
Linking dist/build/DrIFT/DrIFT

出来上がったものをパスが通ったディレクトリに置けばOK。

jhcに戻ってビルドする。

% make
...
pandoc jhc_man.mkd -s -f markdown -t man -s  -o jhc.1
make[2]: pandoc: No such file or directory
make[2]: *** [man] Error 1
rm jhc_man.mkd options.mkd
make[1]: *** [jhc.1] Error 2
make: *** [all] Error 2

おおぅpandocさん……。
あとはどうにかしてpandocをインストールして、sudo make installで終わりです。

Haskellで特定のモジュールのコンパイルが遅すぎる

という話があって。

具体的にはaws-sdk(https://github.com/worksap-ate/aws-sdk)の開発中の話で、
aws-sdkではTravis CI(https://travis-ci.org/)を使ってビルドだけのテストをしているんですが、そのテストっていうかビルドが頻繁に失敗するようになってしまった。
原因はAWS.EC2.Typesのコンパイルで、このモジュールのコンパイルだけで5分以上かかる。その間何も画面出力などはされなくて、Travisでは何も出力がされないまま5分経過するとビルドプロセスが殺されてしまうという。

これはいけませんというか、いや別に普通にTravis以外ではビルドできてるからいいといやいいんだけど、これでTravisを切り捨てるのもかっこ悪いしねぇどうしようかと。
それとは別なのかもしれないけど32bit版LinuxだとAWS.EC2.Typesのコンパイル中にghcがsegmentation faultで落ちるという問題もあって、いい加減このモジュールどうにかした方がいいんじゃないかと思っていました。

当時のソースはこんな感じ。
aws-sdk/AWS/EC2/Types.hs at v0.9.2.1 · worksap-ate/aws-sdk · GitHub

データ定義が140個並んでいて、全部がEq, Show, Readのインスタンスになってて、あとその中でFromTextクラスのインスタンス化してる型が、TemplateHaskellを使うものが64個、TemplateHaskellを使わないものが8個という感じ。72個。

これ一見TemplateHaskellが重いのかなぁと思うんですけど、TemplateHaskellが絡むモジュールだけを分割しても大して変わらないし、モジュールを大雑把に2つに分けても結局その根本のAWS.EC2.Typesのコンパイル時間はあまりかわらないし、なんなんだこれと思って、exportするシンボルの数が多いとか最適化とかそういうのかなぁでもデータ定義しか無いし、

などとバカな話も考えはじめていたんですが、
との提案を受けたので、1日かけてもいいのでよろしくって言って彼にやってもらいました。

私が最初にやったテキトウな分割と違って、きちんとデータ間の依存関係を調べて分割してくれました。すげえ、ありがとうございます。
結果: aws-sdk/AWS/EC2/Types at f43a8b589db1271c9183339873a2115e61a7d9ce · worksap-ate/aws-sdk · GitHub
diff: Split EC2/Types. · f43a8b5 · worksap-ate/aws-sdk · GitHub

で、これをやった結果、ファイルが19個増えたものの、AWS.EC2.Typesのコンパイルが速くなるってもんじゃなくて、コンパイル時間が全体で60%ほど短くなりました。いや短くなりすぎだろうという気もしますが、短くなったものは仕方ないじゃないか。
ともあれ成果出てよかった。

よく考えてみるとHaskellのデータ構造ってC言語みたいな単純なメモリマップとは違って、コードに近いんですよね多分。いやghcが吐くコードがどんなものか知りませんけど。
まあそれが関係あるのかどうかはわかりませんけど、データ間の依存関係の計算に時間がかかってたんじゃないかなぁと。@amkkunが分割してくれた結果、ある程度の依存関係はモジュール分割の段階で解決してしまっているので、それで劇的に速くなったんじゃないかなぁ。同一モジュール間でデータの関係とか調べるのかな、そうだとしたら140個は多すぎるよな。
そういう感じでした。
そもそもexportが重いとかだったらAWS.EC2もかなり重くなってしかるべきなので、その予想はさすがに無かったかな。

ということで、シンボルが多すぎる時は気をつけましょうというか、適切にインタフェースを定義したモジュールに分割しましょうというお話でした。あとこれ関係で、できるだけexportするシンボルはきちんと列挙しておいた方がいいかもしれないとも思った。

32bit Linuxについては検証するの忘れてたからまた今度。

Haskell忘年会でAWS SDK for Haskellについて喋った

去る12月16日に行われたHaskell忘年会なるイベントでしゃべってきました。
忘年会もなにも勉強会とかにはあんましで出かけないのでほとんどの方ははじめましてという感じ? いや知ってる人も割といましたね。同僚とか同僚とか。

その時の発表スライドです。


要は作りましたというお話です。作りました。

Hackageにも登録してあります。
http://hackage.haskell.org/package/aws-sdk
ソースはGitHubにあります。
worksap-ate/aws-sdk · GitHub

まあそんなような感じです。

聞く側としては話題のFreeモナドの話がちょっとおもしろそうな感じでした。「そのライブラリもFreeで書いたらテスト楽になるんじゃないの?」って言われて、確かに面白そうな気もするけどバリバリConduit依存なのでちょい厳しいのかもしれません。いやFreeは全然使ったことないのでなんとも言えませんけど。

あとはmasterqさんにいっしょにOSカーネル書こうぜーって絡まれてたのが面白かった。
ビデオメモリ直書きで画面表示とか、泣きながらstop or rebootデバッグ(無限ループを仕込んで、何も起きなければそこまでは到達している、再起動がかかったらそれ以前でバグっているという画面が使えない時のprintfデバッグ的な技。タイムスライスで結局死んだりする)をした日々とか、やりたいようなやりたくないような感じだけどやると結構楽しいんですよねーっていう話をしていました。

忘年会なんで、終始割とゆるく楽しむ感じで、面白かったです。

少しはテストを楽しくやる(QuickCheck)

テストって別にやりたくないわけじゃないっていうかやりたいんですけど、なんかつい後回しになってしまうというか。
でも例えば外部システムとの連携部分だったりすると私は割と真面目にテストを書くんですが、普段はなんかなんとなく面倒臭い。面倒くさいと面倒くさくないの境界線は何なんだ。

外部システム連携みたいな入出力系のテストがそれほど苦じゃないのは、どっちにしろ動作確認で動かすからなんですよね。
一方でそれ以外の部分、いわゆる計算というか、モジュール間のデータのやり取りだったりデータ変換だったりとか、そういう部分のテストがなんで面倒くさいかといったら、それは足し算をテストする時の事を考えるとだいたい想像つくと思いますけど、

@Test
void testPlus() {
    assertEqual(plus(1, 1), 2);
    assertEqual(plus(1, -1), 0);
    assertEqual(plus(2, 3), 5);
    assertEqual(plus(-5, 4), -1);
    ...

そこで浮かんでくる感情というのは「わかりきってるじゃんそんなこと」だったり、「a+b=cのa,b,cをどのくらい用意すればいいのかわからない」だったり、「いや並べておけばいいんだけどそんな単純作業面倒くさい」だったりで、一言で言うと面白くないわけです。面白くない。

これなんとか面白くならないものか。
……という観点で出会ったわけでは全然ないんですけど、ちょっとテストを書く用事ができて、QuickCheckを使ってみたらこれが割と面白かったのです。

QuickCheckというのは話には聞いたことがあったんですけど、要は関数の性質を検査するものらしいと。

性質とはなんぞや。
例えば、リストをソートする関数があった場合、実装がバブルソートだろうがクイックソートだろうが、それ以外の何か高速なアルゴリズムで実装されていようが、同じ入力に対する出力結果は同じです。こういう時に性質が同じと言う。
例えば、リストの順序を逆にする関数があった場合、任意のリストに対してその関数を2回適用すると元に戻るという性質がある。
例えば、ある数値を文字列に変換してそれをさらに数値に変換すると元の数値に戻ってほしい。
そういうものですよね。

そういう時にQuickCheckではこう書くらしい。といいつつこれコンパイルすらもしてないから動くのかはわからないけども。

import Test.Hspec
import Test.Hspec.QuickCheck (prop)
import Test.QuickCheck

spec :: Spec
spec = do
    describe "sort test" $ do
        prop "int" (test_sort :: [Int] -> Bool)
        prop "string" (test_sort :: [String] -> Bool)
    describe "reverse test" $ do
        prop "int" (test_reverse :: [Int] -> Bool)
        prop "char" (test_reverse :: [Char] -> Bool)
    describe "transform test" $ do
        prop "int to string" test_int_str

-- バブルソートとクイックソートは同じ結果になる
test_sort :: Ord a => [a] -> Bool
test_sort xs = bubble_sort xs == quick_sort xs

-- リストの逆順の逆順は元と同じになる
test_reverse :: [a] -> Bool
test_reverse xs = xs == reverse (reverse xs)

-- 数値を文字列に変換して、それを数値に変換すると元に戻る
test_int_str :: Int -> Bool
test_int_str i = i == parseInt (toString i)

ここのtest_sort, test_reverse, test_int_strで、さっき挙げた例と同じ事をやっています。
この3つの関数は全部何らかの入力を受けて、関数の性質が正しければTrueを返すようにしています。
じゃあこの「何らかの入力」というのは何なのかというと、任意の[Int]だったり[String]だったりIntだったり、それはprop関数が値の型から自動的にランダムな値を選んで適当に突っ込んでくれるらしい。

どういう仕組なのかというと、これ。
Test.QuickCheck.Arbitrary
Test.QuickCheckをimportするとIntとか[Int]とかCharとかそのあたりがあらかたArbitraryのインスタンスになって、arbitrary関数を呼び出すと、その型のランダムな値が取り出せるようになる。このランダムな値を使ってpropが適当に入力を生成してテストしてくれるというわけらしい。
生成する型インスタンスの情報が必要なので、sortとかreverseでは型注釈を書かないといけなかったわけです。
運悪く間違えてテストが通ってしまうとかありそうな気がしないでもないけど、まあ自動テストして何度も回していればいつか気づくでしょうとか、そういう感じでしょうか。そこら辺はよくわかりませんが。

で、自分で作ったデータ型に関してもArbitraryのインスタンスにしてっていうか、あの辺のモジュールにあるGenを生成するchooseとかelementsだとかいう関数を使ってarbitraryを実装してあげれば、任意の値を使った性質のテストが書けるという。

例は私が書いたものじゃないんですけど。山本先生のIPアドレス表現関係のライブラリです。
iproute/test/RouteTableSpec.hs at readaddr · yunomu/iproute · GitHub
自作のIPv4とかIPv6のデータをArbitraryのインスタンスにして、それぞれのCIDRもvalidなものを自動生成するようなコードが上の方に固まっています。

これに対して私がPull Requestを送った時に書いた異常系のテストがこっち。
iproute/test/IPReadSpec.hs at readaddr · yunomu/iproute · GitHub
InvalidIPv4とかそういうデータを定義して、異常な入力値を作っている。ここでは例えばIPv4のCIDRのマスクビット数が0-32以外の値である場合を作っています。文字とか突っ込んでもよかったなぁ。
これらを使ったテストを下の方に書いてます。今回はread関数をテストしたかったんですけど、SafeのreadMayを使ってテストするのもアリかなと思ってこんな感じにしてみましたっていうか、readMayでも問題なく使えるようにするのが目的だったんですが。

こういうのを使って正常な値を自動生成するとか異常な値を自動生成するとかいう作業は割と楽しかったりする。というか楽しかったのです。
どうやってテストできるだろうとか正常値をどう表現しようとか考えたりするのは大変だけど楽しいですよね。コピペは楽しくない。

参考というか、実際は参照してなかったりするけども。
Haskellの単体テスト最前線 - あどけない話
あと全然関係ないけどPull Requestする時に参考になった。
お気軽 pull request - Quickhack Diary