再読・すごいH本

とうとうと言うか、やっとと言うか、オイラーの携帯に下記のような Cメールが着信したぞ。

有料動画の未納料金が発生しております。
本日ご連絡無き場合、法的手段に移行致します。
グーグルサポート
03-6XXX-XXXX

へー、グーグルって有料動画をやってるんだ。知らなかったなあ。どんな動画を見せてくれるのか、聞いてみるかな。オイラーの知らない間に、新種のサービスを始めたのかな。

最近、グーグルもEUから未納料金を徴収されて減益になったらしいんで、その穴埋めに乗り出したのかな? でも、こういう連絡をCメールで投げてくるっておかしいよね。

電話してみようかしらね。その前に、番号案内(電話系のDNS)に、逆引きの問い合わせをして、この電話の持ち主がグーグルサポートである事を確認しておきたいな。あれ、そんなサービス 提供してるのか? ひょっとして正引きしか出来なかったりして。

えと、こんな事で電話代を払うのもったいないな。昭和の名残でまだテレカを持ってるんで、 有効利用したいぞ。こやつが使える公衆電話って、近くに有ったかなあ。最近、とんと見かけないぞ。 テレカをフェードアウトさせるために、NTTが撤去しちゃって、そんじょそこらに無いな。

何日か待ってみたけど、ちっとも法的手段とやらに訴えてこないので、拍子抜けしてますよ。 グーグルサポートさん、有言実行してね。そうじゃないと、信用無くすよ。

stack haskell on Ubuntu

この所、haskell熱が再燃してる。すごいH本 こと、 『すごいHaskell 楽しく学ぼう』って すごい名前の本を再読してるんだ。ああ、再読と言うと語弊があるな。出版された当時、すぐ 入手して途中まで読んでほったらかしにしてたからね。

楽しく学ぼうって事は、裏を返せば難しいよって事だ。そうでなければ、わざわざ楽しくなんて 付けないからね。オイラーに取って難しいと思うのは、やたら出て来る学術用語。モノイドだとかファンクターだとか意味不。これらが分かればいいんだろうね。少なくとも議論に参加 出来ると思うから。

昔、関東に住んでいた頃、Real World Haskellの翻訳者達が主催する読書会に参加した事が あったけど、正直議論に付いていけなかったぞ。その主たる原因は、用語が理解出来なかった 事だろうと思ってる。まあ、Haskellは数学ですからね。

で、熱いさなか熱い所でパソコン片手にやってると、脳内沸騰、熱中症発症となると思うんで、 Windows7機をクーラーが有る居間に移動した。

その機にはHaskellが入っていない。ウブ16.04がVMWare上に有るんで、入れてみるか。 唯一の心配は、emacsが古いんで、interoがまともに動くかだな。

時間はかかったけど、stack上でHaskellはちゃんと動いた。 そして、interoも動いた。

commercialhaskell/intero

によると、キーバインドは下記のようだ。

Key binding	Description
M-.   	Jump to definition
C-c C-i 	Show information of identifier at point
C-c C-t 	Show the type of thing at point, or the selection
C-u C-c C-t 	Insert a type signature for the thing at point
C-c C-l 	Load this module in the REPL
C-c C-r 	Apply suggestions from GHC
C-c C-k 	Clear REPL
C-c C-z 	Switch to and from the REPL

H本を読むための参考資料と言うか要約を下記に見つけておいた。

ウォークスルー Haskell

10分で学ぶHaskell

Haskell のお勉強

1から学ぶHaskell: プログラマ向けのHaskell入門

識者によると、色々な資料に当たって、色々な切り口を知るのが良いそうな。

Haskellには副作用がないのか?

Haskellと副作用の議論の続き

とか、色々と主義主張が有るからね。

intero

こやつ、結構ごみを出すので、走らせるのは /tmp の中がよい。ここだと、OSを再起動すれば GCされるからね。

いきなり、emacs hoge.hs とかやって起動する。暫くすると、裏でhaskellが走り出して、馬鹿ユーザーの監視が始まる。そして、間違った事をすると、赤くなって怒り出す。

emacsでファイルを編集したり実験したりしてる時、下記のようなプロセスが動いていた。 何やら、随分大量の引数が与えられているな。

 3159 pts/1    S+     0:05 emacs s1.hs
 3193 pts/3    Ssl+   0:00 /usr/bin/stack ghci --with-ghc intero --docker-run-ar
 3210 pts/3    Sl+    0:08 /home/sakae/.stack/snapshots/x86_64-linux-tinfo6/lts-
 3264 pts/4    Ssl+   0:00 /usr/bin/stack ghci --with-ghc intero --docker-run-ar
 3280 pts/4    Sl+    0:02 /home/sakae/.stack/snapshots/x86_64-linux-tinfo6/lts-

親分と思われるpid 3193のプロセスがどんな引数で起動したか、調べて整理してみた。 こんな所にもドッカーが顔を出してるぞ。あれれ、オイラーはドッカーなんて入れてないんだ けど、どうなっちゃってるの? ドッカーの一部がstackに組み込まれているって事かな。

sakae@clr:/proc$ cat 3193/cmdline
/usr/bin/stack ghci
  --with-ghc intero
  --docker-run-args=
    --interactive=true
    --tty=false
    --no-build--no-load
    --ghci-options-ignore-dot-ghci
    --ghci-options-odir=.stack/global-project/.stack-work/intero/intero3159iFW
    --ghci-options-hidir=.stack/global-project/.stack-work/intero/intero3159iFW

上の例はdockerが入っているClearLinuxでの結果だけど、Windows7機のウブでも、同じコマンドが使われてた。(勿論、Windows7機はi386な石で動いてて、正真正銘doclerは入っていない)

こんな事で時間を潰してもあれなんで、

dec

オイラーがポイントフリースタイルで初めて書いたのを肴にごちゃごちゃやってみる。 ポイントってのは、関数に与える引数の事ね。下記のように、関数定義にも関わらず、引数を省略しちゃう書き方。

inc = (1 +)

dec = flip (-) 1

C語で言う所の、n++ と n-- だ。正直に明かすと、decは最初からこう書けなかった。 色々コードを書き、実験につぐ実験でこうなった。

上で出て来たdecについて考える。なお、これを大文字で綴ればDECとなって、あの名機である pdp11やvaxを生んだ会社になるけど、それはもう彼方な話だからしない。

デクリメント。n--の事。素直に書くと

dec n = n - 1

引数を一つ取る関数だ。関数定義は、匿名関数に名前を付けたものってのが基本。分かり易く 左辺に引数を置く書き方は、構文糖衣だ。

dec = \n -> n - 1

これをghciから評価すると

λ :t dec
dec :: Integer -> Integer
λ dec 5
4
λ dec 3.14

<interactive>:24:5: error:
    * No instance for (Fractional Integer)
        arising from the literal `3.14'
    * In the first argument of `dec', namely `3.14'
      In the expression: dec 3.14
      In an equation for `it': it = dec 3.14

ちゃんと、整数を受け取って整数を返す関数ですって言ってきた。だから、浮動小数点の 数値を与えると、文句を言われるのだ。なお、カッコよいlambdaがプロンプトに使われて いるけど、intero作者の拘りだろう。

で、簡易的な書き方、dec n = n - 1 から、nを削除する事を考える。いわゆる式の簡約化だな。なんたって数学ですから。

いきなりは削除出来ないので、右式を変形する。

dec' n = (-) n 1

引き算の記号は、左右に数値(相当)を従えている。こういうのを中置記法と言う。馴染みの ある書き方だ。これを、左側に持ってくる事が出来る。(lispではこの書き方しか出来ない) 記号を左に持ってくる時は、カッコで括るという約束があるんだ。

そもそも、関数型言語の元祖はLisp。一番左側に関数名を置いて、続いて引数を書き連ねる。 どこまでが引数が並んでるかを明確にするため、全体をカッコで括るってのが、Lispの約束。 数値演算のプラスとかアスター記号もこの規則に倣い、一番左に置いておく前置記法しか なかった。で、世の中から、バッシングを受けた。 前置記法は慣習とかけ離れてる?

しゃーない、世の中の習慣に合わせておくかって、ひよった記法が生まれた。記号が前にくるか 中間に来てるかの区別が必要。それで、上のように(前置する時は記号をカッコで括る)したってのが真相。

左式にダッシュを付けたのは、元になるのを変形したよっていう、数学的な書き方に倣ったものだ。決して導関数じゃないので、注意の事。

dec'' = (-) 1

更に変形して、両辺からnを削除した。これで試してみると

λ dec'' 5
-4

期待通りにならない。どうやら、

λ (-) 1 5
-4

のように、解釈されている。(計算する時、引数であるnは、一番右側に配置される)という事は、(-) 1 ってのが、一つの関数と見做せるんだな。

はて、どうする? こういう時に登場するのが、flipだ。門前の小僧、習わぬ経を読むの 例え通り、読書会で覚えた、変な関数。まあ、H本にも登場するので、それなりに需要は有るって事だな。

型を見る。flipは、引数に一つの関数と2つのアーギュメントを取る関数だ。引数になってる 関数は、aとbを受け取りcと言う型の値を返す。実際その関数に引数を渡す時、引数の順番を 入れ替えてから実行。

λ :t flip
flip :: (a -> b -> c) -> b -> a -> c

これを使えば、

dec n = flip (-) 1 n

で(定義では、両辺のnを削除と言うか書かない通の方法になってるけど)、引数の順番がhulipの作用でひっくり返り、正しい結果が得られる。まあ、遊びだな。

Prelude> succ 12.3
13.3
Prelude> pred 12.3
11.3

Preludeには、万能のinc、decが備わっているぞ。無理しないでこちらを使えって言う落ちですな。 これで終わっては申し訳ないので、もう少し無駄してみる。predとかはどういうコードなの?

Prelude> :t pred
pred :: Enum a => a -> a
Prelude> :list pred
cannot list source code for pred: module GHC.Enum is not interpreted

残念ながら、ソースは見れない。でもヒントを貰ったよ。hoogleからpredで検索。Preludeに 有るよってんで追って行くと、ソースへのリンクが出て来る。そこをクリックすると、GHC.Enmuに到達した。

    succ                   = toEnum . (+ 1)  . fromEnum
    pred                   = toEnum . (subtract 1) . fromEnum

複雑な深層部にめまいがしますよ。

hugs

上で調べたように、システム由来のソースを当たるのは、面倒です。でも、ソースを当たって 腕が上がるとういうのは、今までの経験で証明されてます。

そこで登場するのが、昔ながらのhugsです。OpenBSD 6.1では、portsからさすがに消えてしまったけど、Debianでは、Debian9でもまだパッケージになってますんで、さくっと 入れておくのが吉。GHCと違って、容量も15Mにも満たないですから。

見る場所は、/usr/lib/hugs/packages/hugsbase/Hugs にあるもの。手始めにPrelude.hsってのは、定番です。

debian:Hugs$ wc * | sort -nr
  5614  27832 202245 total
  2020  10066  65355 Prelude.hs
   477   2116  15395 Word.hs
   367   1748  12373 Int.hs
   350   1319   9632 GenericPrint.hs
   262   1644  10372 Numeric.hs
   262   1133   7559 Internals.hs

まあ、行数でTop5ぐらいを見ておけば、十分でしょう。 以下、Prelude.hsに有った、predの部分。

class Enum a where
    succ, pred           :: a -> a
    toEnum               :: Int -> a
    fromEnum             :: a -> Int
    enumFrom             :: a -> [a]              -- [n..]
    enumFromThen         :: a -> a -> [a]         -- [n,m..]
    enumFromTo           :: a -> a -> [a]         -- [n..m]
    enumFromThenTo       :: a -> a -> a -> [a]    -- [n,n'..m]

    -- Minimal complete definition: toEnum, fromEnum
    succ                  = toEnum . (1+)       . fromEnum
    pred                  = toEnum . subtract 1 . fromEnum
    enumFrom x            = map toEnum [ fromEnum x ..]
    enumFromTo x y        = map toEnum [ fromEnum x .. fromEnum y ]
    enumFromThen x y      = map toEnum [ fromEnum x, fromEnum y ..]
    enumFromThenTo x y z  = map toEnum [ fromEnum x, fromEnum y .. fromEnum z ]

型宣言と関数定義は、隣接させる必要は無い。上記のようにしてもOKとな。それで、上での定義は、総称的なもの。続いて、個別の型に対する定義が出てる。一例をあげると、

instance Enum Int where
    succ           = boundedSucc
    pred           = boundedPred
boundedSucc, boundedPred :: (Num a, Bounded a, Enum a) => a -> a
boundedSucc x
  | x == maxBound = error "succ: applied to maxBound"
  | otherwise     = x+1
boundedPred x
  | x == minBound = error "pred: applied to minBound"
  | otherwise     = x-1

instance Enum Float where
    succ x                = x+1
    pred x                = x-1

instance Enum Double where
    succ x                = x+1
    pred x                = x-1

なんだか、Cフラフラ語とかJava語に出て来て、ユーザーを混乱に貶める、同名関数の定義が なされている。型が違うから、名前は同じでも違う関数と言い張るあれだ。内部はマングルとか 言うので管理してるのか脳?

このIntの定義を見ると、Intには、範囲が有るんよ、それを逸脱しようとすると、そんな事は 出来んがなと、ガード(マン)が、しっかり見張ってるんだな。H本の型の所ににも説明が出てたね。

なにはともあれ、ノウハウ満載のPrelude.hsは、Haskellを勉強するなら家宝にしても良いぐらいの一品だと思うぞ。Haskellの規格は、(19)98年版から2010年版と進化してるけど、根底は 一緒だから、基本を押さえておくのが良策だ。

上で出て来た、subtractってどうなってるか? H本ではこやつ、特殊事例だからねって、わざわざ断り書きされてたぞ。

λ :t subtract
subtract :: Num a => a -> a -> a
λ subtract 4 6
2

はて、定義はどうなってるか? 一発で分かる人は大したものだと思うぞ。オイラーは、まだ関数脳症に罹患してないので、分からん。よって、家宝を調べてみる。

subtract       :: Num a => a -> a -> a
subtract        = flip (-)

ここまで、ポイントフリースタイルが拡張出来るのか。なんか、先祖の凄さが感じられるな。 まだまだ、修行が足りませぬ!! (!!)も関数ってのを見かけたぞ。

λ [1,2,3,4,5] !! 3
4
λ flip (!!) 3 [1,2,3,4,5]
4

ちょっと真似してみた。

λ idx = flip (!!)

<interactive>:6:5: error:
    parse error on input ‘=’
    Perhaps you need a 'let' in a 'do' block?
    e.g. 'let x = 5' instead of 'x = 5'
λ let idx = flip (!!)
λ idx 3 [1,2,3,4,5]
4
λ idx 3 "abcdefg"
'd'
λ :t idx
idx :: Int -> [c] -> c

ghci中で関数定義する時は、let が必要なのね。

と、ここまで書いてきて、理解不足に気が付いた。ポイントフリースタイルで書けば、 引数が2つあっても省略出来るって、これは結果を見ればそうなってるだけね。

上の flipは、3つの引数を取る関数。そのうちの1個の引数(実行したい関数)をいれた 定義に名前を付けたって事ね。だから、flipが実行出来るには、後引数2個の補充だ必要。 この時点は、その2個の引数の名前なんてどうでもよい。

haskellは、関数の引数の個数(アリティー)を掌握してるんだな。(ってか、定義時に引数の型と個数をしっかり決めてるじゃん。型の事ばかり強調されてるけど、個数の事はあからさまに説明してないね。そんなの見れば分かるって事か。にぶいオイラーは、この重要性にやっと気が付いた)静的言語だから、 不定個の引数を取る、関数は無いんだな。また、複数個の返り値を返す機構(Lispで言う多値) も無い。という理解で良いのかな。

xmonada on OpenBSD

Haskellのアプリって無いの? 学習のためだけのものじゃないよね。ずーと考えていたら、 xmonadってのが有る事を思い出した。

この際だから、OpenBSDに入れてみよう。だって、この辺境のOSでは、stackが動かないので ちょっと他のOSから仲間はずれにされているからね。

ちゃんとxmonadは、パッケージになってた。その案内によると

xmonad

が、総本山だ。世にも珍しい、タイル型のウィンドウマネージャだ。.xinitrcに、exec xmonad って、書いてから、startx すれば良い。ただののっぺらぼうな画面が出てくる。

ターミナルを出したいなら、Shift+Alt+Retrun する。終了は、Shift+Alt+q だ。

xmonad.hsという、たたき台になるconfigが用意されてるので、~/.xmonda/xmonad.hsに コピーして、いろいろ手を加えられる。

import XMonad
import Data.Monoid
import System.Exit

import qualified XMonad.StackSet as W
import qualified Data.Map        as M

-- The preferred terminal program, which is used in a binding below and by
-- certain contrib modules.
--
myTerminal      = "xterm"

-- Whether focus follows the mouse pointer.
myFocusFollowsMouse :: Bool
myFocusFollowsMouse = True
 :

こんな具合に、もろにhaskell語で書かれている。面白いから暫く使ってみるかな。

NixOS

上のxmonadのダウンロードをクリックしたら、対象OSに見知ったもの以外が混じっている 事に気が付いた。NixOSですって。なんか微妙な名前の奴だなあ。何だろうこれ?

依存性地獄を解決するディストリビューション非依存のパッケージマネージャ、Nix

関数型パッケージマネージャを用いているLinuxディストリビューション『NixOS』をインストールする

NixOSのインストール

NixOSでHaskellな環境

haskellとxmonadは切っても切り離せない関係だな。

etc

Write Yourself a Scheme in 48 Hours

48時間でSchemeを書こう

scheme-in-48-hours (src)