主語が違うのか?

SCV?CSV?なんか英語の時間にそういうのがありました。VSOP?
あたしは駅の階段をゾンビみたいに歩きながらふと思いました。
「関数型言語というくらいなんだから関数を主語に考えないと駄目なんじゃないの?」
ようこそ。ここはHaskell理解に苦しんでいるおねーさんの姿をご笑覧いただくブログです。

そういった理解の方法で合っていたかは知りませんが、たとえば

def foo(a, b):
    return a + b

foo(2, 3)

のようなケースにおいて、あたしの主語は「値」でした。2と3を渡して、箱の中でなんかして、結果(上のfooさんの場合だと)5が返ってくる。
2と3をあれして5を取り出すモノが関数だったわけです。あたしにとって。
が、Haskellのお勉強をしていると、次から次へと「関数が返ります」「関数を適用します」のオンパレード。値がどうかといった話はあんまり出てこない。
あたしにとって主語だった「値」不在で、関数という「何かするハコ」の話ばかりだから、あたしはどこに目線を置けばいいのか分からなくなって、そんで混乱しているのではないか。
おお。
なにも解決はしてないが、少し視界がクリアになった感。

お恥ずかしい話ながら

♪ 地球はひとつ 割れたらふたつ

の世界観で生きております。お~ガッチャマーンです。リンゴ切って二個です。0.5個とか言わねえだろうが!って怒るタイプです。一言でいうと算数の初期にすっ転んでいます。
aとbを渡してcを返すもの、とかいう抽象的な話を理解するのが苦手なのかもしれません。おかしいな、二次連立方程式とか因数分解とか、(もう覚えてないけど)あの頃は好きだったのになあ。あれは勘違いだったのかなあ。

ともあれ、ウンウンいってしばらく苦しむのがいいんです。脳の筋トレになると思えば安いものだ。中学生高校生の頃にサボったバチだ、ざまあみろ。
はーつらい。
さしあたり、主語は関数であると唱えながら本を読んでみようと思った次第。

すべて関数です

えーと。
すごいHaskellの61ページから「高階関数」というものの説明が始まっています。

Prelude> :t max
max :: Ord a => a -> a -> a

(本にある記述ではないです。あたしの手元のghciさんに吐かせた中身)
で、これを本では、次のようにも書けますという。

max :: (Ord a) => a -> (a -> a)

はい。はい?

矢印->を型シグネチャに含むものはすべて関数です
(すごいHaskellのp.62)

a->bの「a->」と「->b」と、どっちに捉えたらいいのやら?
最後(右端)にあるのは返り値であるということからすると、「a->」って考えればいいのかな。
a+b+cを返すみたいなのを書いてみる

Prelude> let foo x y z = x + y + z
Prelude> foo 9 10 11
30

型をみてみる

Prelude> :t foo
foo :: Num a => a -> a -> a -> a

このばあいのー、まずいろんな成分を分けないといけない。あたしが読解する用に。

Prelude> :t foo
foo :: (Num a) => a -> a -> a -> a

「Num a」を括弧でくくって、視線から離す。残った問題の「a -> a -> a -> a」を考える。

矢印->を型シグネチャに含むものはすべて関数です

関数に渡すものは変数であると覚えたあたしの脳が、この1行を飲み込むのを拒絶する。厳しい。

a -> a -> a
↑おなじ↓
a -> (a -> a)

は、aの型を引数に取りa->aを返す関数と読むことができると書いてある。

a -> a -> a -> a
↓
a -> (a -> a -> a)
↓
a -> (a -> (a -> a))

みたいな?みたいなと書いてはいますがぜんぜん飲み込めない。いまはまだ、あたしの脳の外周を水カビみたいに浮いてる考え方だ。知恵でなく知識。
aばっかりで全然分からぬ。

x -> y -> z -> v
↓
x -> (y -> z -> v)
↓
x -> (y -> (z -> v))

書き換えてみる。
xを引数で渡して、「y -> (z -> v)」って関数を返す。
その「y -> (z -> v)」は、「z -> v」って関数を返す。
値vが返る。
この無限にタマネギを剥くような感覚。

関数の部分適用という考え方があった。

Prelude> (+) 3 4
7
Prelude> (+) 3
:1:1:
    No instance for (Show (a0 ->
      arising from a use of `prin
    Possible fix: add an instance
    In a stmt of an interactive G
Prelude> ((+) 3) 4
7

ここはなんとなーく分かるようになってきたが、染み込んでない。
型シグネチャっていうの?抽象的な表記をされたとたん、あたしの脳が「これは難しいものだ危険!」みたいになって、受取拒否をしているかのようですよ。
どっかでとつぜん染み込んだりするようになったりするものなので、しばらくはこの負荷をかけ続ける所存でございます。はやくこいブレークスルー。

身についてないっぽいポイント

Haskellです。
Pythonがどんだけ読みやすく書きやすい言語であったか痛感してます。
はーん。

(+)

+とかでなくてもいいんだけど、カッコで括られてる箇所を関数と読取り慣れていない。

(\x y -> x + y)

これが何してんのかすぐに読めない。
どうも関数\xと引数yって読んでしまい、思考が脱輪するのね。

Prelude> :t ($)
($) :: (a -> b) -> a -> b

こういうのを、(a->b)は関数で、引数がaで、bがreturnされる、と読めない。
a -> (a -> b) -> bって書いてくれればいいのに。
「(a->b)って関数」をaに適用します、結果がbですと読むんだろうけれども、まだまだ脳味噌が追いついてくれない。
あー書いててわかってきた。あたし主語として捉える対象を間違ってんだ?そうなこれ?
ホニャララってデータを関数ウンタラで云々してこうなります、ではなく、ウンタラって関数をホニャララに適用して云々、そう読めってことか?

関数型言語の関数を視認できてない。そりゃー読めないわなあ。

表現の種類

おねーさんです。
今日も昨日も行き帰りの電車の中では”すごいHaskell”を読み、速やかに昏睡状態へと移行する動作を続けております。Haskellはすごい。

ここ数日で最もいらいらするのは畳み込みです。
foldl。そうか。foldl1。ちょっとちがう内容だから数字つけたのね。そうか。いや、これは看過できぬ。
きっと深淵で無駄のない洗練された理由があるんだとは思いますけど、いまのあたしはこう思う。
「ほんとに頭の良い人が作ったの?センス悪くない?あまりにも場当たり過ぎじゃない?」
こういうところでガックリきて、はーまじかーなえるわーってなって寝るんだわ。
#foldlというのは左畳込みとかいう処理をする関数さんであるらしいです。なるほどわからん。

tnomura9さんの「tnomuraのブログ」というサイトをこないだ見つけて、たまに読んでいます。
Haskell 記事リスト」というカテゴリがまとめられていて、まだ最初の方だけしか読めてないけれど、書き口がわかりやすくて読みやすい。
すごいHaskellとともにこのサイトを副読本として読んでいこうと思います。ありがたいなあ、こういう記事を置いてくれる人がいて。

いろんな表現があるのだな

その、tnomura9さんのページに例題が出ているので、それをちょいちょいやってみる。

「1から10までの数の2倍の数のリストを出す」

あたし:
Prelude> [x*2 | x <-  [1..10]]

リスト内包表記だ。Pythonで覚えたんだワイルドだろう。

上記のページ:
Prelude> map (* 2) [1..10]

オ-ゥ。
もうここで違う。結果は同じよ?同じなんだけど、map関数を使うこと、「(*2)って関数」を与えること、このへんの発想があたしに欠落しているということがよくわかる。
はーなるほど。次。

「1から10までの数の平方数はどうだろうか。」
次の行を読まずにまず自分で書く。わかってんだよmapだろmap。

あたし:
Prelude> map (^2) [1..10]

で、

上記のページ:
Prelude> map (\n -> n * n) [1..10]

む、むめいかんすう。。。
むろんやってることは同じではございますが、そういう表現もあることを知った上で、どっちの書き方を選択するか、といったふうにしておきたいものです。

Prelude> :t \n -> n*n
\n -> n*n :: Num a => a -> a
Prelude> :t (^2)
(^2) :: Num a => a -> a

Python覚えたての頃、何をやってたかを書き残しておけばよかったなあ。言語ってどうやって覚えるんだっけなー。

ドットつなぎ記法

あたくしにとってドット(.)というのは、文字列結合を指すものにほかなりませんでした。後にいろんな世界で色んな意味をもつ記号であることを知るにいたり、言語ごとの思想の違いであるとかに目がクラクラした次第であり、し続けている次第であります。
ほんでHaskell。
ドットつなぎは、関数合成です(キリ
あー、そっすか。関数の、合成っすか。失敗すると外道スライムが沸いたりするっすか?

すごいHaskellのp99。

import Data.Char
import Data.List

digitSum :: Int -> Int
digitSum = sum . map digitToInt . show

数字列を受け取り文字列に変換して、各文字(数字)の合計をするというサンプル。本にある例文です。
1234と入れたら、1+2+3+4で10を返すと。
で、これがまたわからない。ドットが関数合成なるものであることは見ました本で。
で、これ、えーと、引数どこいった。
これはえーと、あまりにも引数が自明なあまり省略しましたみたいな話ですか?
で、色々こねくり回していると、どうも、

digitSum 1234

は、

(sum (map digitToInt (show 1234)))

と等しいようだということがわかりました。

  1. showは受け取った値を文字列にする、
    1234→”1234”
  2. digitToIntは文字0-fを数値にする、
    “1”→1
  3. mapはdigitToIntを各桁に対して行う、
    [“1″,”2″,”3″,”4”]→[1,2,3,4]
  4. sumで合計する
    1+2+3+4

とすると、

digitSum = sum . map digitToInt . show

の右端から順に読んでいくと同じことになるわけだー?
なんで右から読むのと左から読むのとあるんだ。読む向きがわかんなくて目がチカチカする。。。
リスト内包表記もPythonさん経由で慣れてるというのは大きいにしろ、やはり視線が左右に泳ぐ傾向があり、さくっとわかりやすく、左から右へ、流れるように読みたいものだと思う昨今です。
どちらを先に学んだかでしかないんだろうけど、

Haskell:
sum . map digitToInt . show

これと、

jQueryなど:
uu().yaa().taa()

みたいなのがこう、頭のなかで混ざってですね、マーブル模様になるんすよ。
関数の合成とメソッドチェーンとは違うものか?きっと違うな?少なくとも読む方向は逆だな?
そんなかんじでモヤモヤしております。
違う思想、哲学、体系を学ぶというのは、ほんとうに大変なことだなあ。

このままじゃ脳が外道スライムになるっすよー。

関心の範囲とでもいうの?

使う用語はテキトウで思いつき任せです。
そもそもこの「hogeFunc x y」という書き方自体、あたしはイライラしているんだ。
カッコで括るとかしないと見分けつかなくないのか。なんなんだ。
なーんでhogeFunc(x y)って書かないんだろう。Shiftキー押すの嫌だから?

これは毎日イライライしている問題の中でもとりわけ大きなテーマで、面倒くさいのか何なのかしらないが、なぜ関数名も仮引数も、一緒くたに並べて視認性を下げてるんだろう設計者は阿呆なのだろうかと思っているわけ。

書いてるうちに氷解し始めてはいるんだけど、そういうモヤモヤも今後のために書く。

Prelude> let hogeFunc x y = x + y
Prelude> hogeFunc 2 3
5

うん。

Prelude> let subHoge = hogeFunc 2
Prelude> subHoge 1
3
Prelude> subHoge 4
6

これを知ったときは驚いた。「hogeFunc x y」のxまで代入済の関数を返すのね。「すごいHaskell」を電車で読んでて、このくだりを目にした時、驚いたあまりヘンな声でて恥ずかしかった。

Prelude> :t hogeFunc
hogeFunc :: Num a => a -> a -> a
Prelude> :t subHoge
subHoge :: Integer -> Integer

はー。

で、よくわかってないのは相変わらずだけど、もしかするとHaskellさんというのは、右隣りにいる誰かにしか興味を持ってなくて、右隣りと何かをしたあと、まだ右隣に何かがいる場合、「ああ、いたの」とばかりに何かをするといったような人なのかしらと思い始めているわけです。
むろん加減乗除の優先順みたいなところで、常に右を一個ずつ食っていくわけじゃないよとは思うんですけど、

x:xs

にしろ、

Prelude> let subHoge = hogeFunc 2

にしろ、先頭(左端)の何かにしか興味を持っていないといった佇まいに見えるわけです。

で、

hogeFunc x y

コレにおけるhogeFunc氏はyさんなんぞ知らんの体であって、興味の対象はxさんだけなんであると読みますと、
hogeFunc xの結果の何か(機能的にはsubHoge)が初めてyを認識し、subHoge yを評価すると。結果、hogeFunc x y全体の評価が終わって、xとyの和が得られてよかったねという流れなんでないか。
あたくしの理解のためのこじつけですが、つまり、つまり、

((hogeFunc x) y)

という感じ?なんじゃこりゃw
まあそのなんだ、カッコあっても意味ないからカッコ書いてないんだよということなのか。hogeFunc氏にとって、yさんは興味の範囲の外にいる人だから。
こう読むとなんとなく読める気がしてくる。勘違いだった場合に突き進んで、あとで勘違いをとくハメになるのも痛いなあ。
「こういうかんじなのかもしれん」
くらいで読み進めていこうか。

Haskellコードの読み方

#Haskellコードの読み方を教える記事じゃないので、そういうのが必要な人は他のとこ行ってほしい。これはあたし用のメモだ。それも、理解したので書く記事ではなく、わかんないから書く記事なので、役に立たないす。
書きだして眺めるための記事であります。

Hakell読んでてイライラさせられるというか、他の言語をちょっとでもかじった人はたいてい苦しむんじゃないかと思う点が、「=」だ。

php:
$a = 1 + 2;

Python:
a = 1 + 2

Haskell:
a = 1 + 2

うん。ここまではいい。というかここで既に転んでたらアンインストールしとるわ。
このとき、あたしの頭は
「1+2をaに代入/バインド/ホニャララする」と読んでいます。右辺から左辺、という方向です。「←」です。
ではこれはどうか。

Haskell:
hogeFunc x y = x + y

こんくらい簡単な例なら、一瞬フリーズするものの何とかならんでもないんですが、一瞬フリーズするのは確かなんですよ。どっち方向に読むのコレ。
hogeFuncという名前の関数の定義らしきものだなと読み、えーと引数がxとyで、そんでx+yしたものを左辺にー、えっ?左辺どうすんの?……みたいなかんじです。

phpなりPythonなりJavaScriptなり、あたしの知っている言語は、関数を定義するときに関数名の定義と中身をイコールでつないだりしていない。
ブラケットやインデントで隔離された領域があって、(Haskellのように副作用を認めない、絶対に値を返すというルールでいうならば)returnで値を返す、

php:
function wee($a){
    return $a+1;
}

返された値は左辺の何かに放り込まれるか、

$b = wee(10);

その場で展開されて値になる(といった感じに読んでいる)

$b = wee(10) + 5;
→ $b = 11 + 5;
→ $b = 16;

そんなかんじであると思っているわけです。
これはHaskellであってphpでもPythonでもねえんだよということは重々承知ではあるが、10年かそこら読んできたクセが染み付いててなかなかスイッチを切り替えることができません。

Haskell:
hogeFunc x y = x + y

さっきのこれの例でいくと、x+yされた何かを左辺に返すと読みたいんだけど、左辺にあるのは関数名と引数の羅列であって、振り上げた拳を下ろす場所を見つけられないような、ぐにゃりとした、もやもやした、行き場のない感じを感じてしまうわけであります。
左辺右辺で言えば、あたしはこれを左辺を見て右辺を見て、右辺からどっかにいくはずなんだけど行き先がよくわからん、といった趣。
左辺と右辺は等しいよ、という読み方をするほうがいいのかなあ。考え方というか読解のためのコツがわかってない感じ。

何が何でも絶対に”return”するから、わざわざreturnとか書かないよ~みたいな話なのかなー。
そのうち慣れて、そういうもんだよーって思えるようになるのかなー。

ほんとに慣れるのか?

uu (x:xs) = x+1:uu xs

先日書いたコード断片。
こういう、再帰的に処理の続きが発生している記述がすごく苦手です。読み解こうとするときに、どこをどんな形で脳に教えてやったらいいのかわからず、混乱するわけです。
1. uuさんにリストを渡すんだよ~
2. リストの先頭のxに+1するんだよ~
ここで思考停止。残りの「:uu xs」部分を、脳にどう教えたらいいのかわからない。
あたしにとって右辺は左辺に値を戻すもののはずなのに、右辺の端っこでまだ処理が続こうとしている上、行き先がよくわからない。
頑張って読む。xsの先頭に対して+1する処理をして、xsの残りに対してuu xsして……。
はあ、なるほど、わからん。
その際限のなさそうな、ふかーいループというか、やたら積み上がっていくスタックというか、それどこまで覚えてたらいいの、あたしそんなたくさんのこと一度に覚えられないんだけど、ああ頭痛が眠気が。再帰こわい。つらい。
そんな酷で際限のないつらいことを強要する言語設計なんぞするわけがない。すると読解のコツがあるはずだ。
むしろあれか、際限ねえんだから今は気にするなということなのであろうか。遅延評価というのはそういうことなのだろうか。
わからぬ。

パターンマッチング

x:xs問題が脳に染み込みはじめたことで、急にHaskellが読みやすくなりました。進歩です。
読める気になり始めたという方が真実に近いかもしれない。
むろんまだまだイライラの種は尽きないので、このへん一個ずつやっつけないと進めそうにないです。

すごいHaskellのp.38に「リストのパターンマッチとリスト内包表記」という項目があります。

let xs = [(1,3),(4,3),(2,4),(5,3),(5,6),(3,1)]
[a+b | (a,b) <- xs]
[4,7,6,8,11,4]

うん、「<-」にイラっとこないわけじゃないが、ここはわかる。Pythonやってて良かった。phpだけだったらきっと未だに読めてなかった。 びっくりしたのはつぎの例文で、

[x*100+3 | (x,3) <- xs]
[103,403,503]

面妖な。いや、これはすごい。利用シーンちょっと思いつかないし、必要なときにこのやり方を思い出せる自信は全然無いけど、

[x*100+3 | (x,y) <- xs, y==3]
[103,403,503]

これと同じ結果をパターンマッチングで得るってことなわけだ。どっちがよりHaskellらしいとかは知らないし、さしあたり考えないけれど、あたしが不機嫌になり続けた「Haskellのリストとは先頭と何かである」問題と同様に、パターンマッチングというものが随所に染み込んでいるのだなということはわかってきました。

仕掛けはわかってきたが

取り扱うデータに対して、パターンマッチングを適用することで上手に利用できますね、とか、これは違うやり方が必要ですね、とか、そういった見極めの按配がわかってないんです。
「ふーん、いつつかうの?」
という、実際の利用に即した風景が見えてないもんだから、しばらくイライラし続けることにはなりそうです。

Haskellにおける、リストについての誤解

uu :: Num a => [a] -> [a]
uu [] = []
uu (x:xs) = x+1:uu xs

あたしが少ない脳みそを搾って自分ではじめて書いた、Haskellの再帰してなんかする、記念すべき関数。
ああ。
一歩進んだー。
Haskellを知る誰もが、あたしが感動している中身のアレさに引くのでありましょうがそんなことはどうでもいい。この一歩が遠い奴もいるのねって学習できてよかったな。

えーと、[1..10]みたいなリストを受け取ってインクリメントして返すだけですが、あたしはそもそもこの書式そのものに納得することができず、イライライライラしておったのであります。
そーいえばphpもpythonも初期の一歩目を踏み越えるのに、たった一歩の小さな段差を上がるのに、数週間を費やしたのでありました。これ学校の授業とかだったらカリキュラムついていけなくて落ちこぼれだよね。
#実際、あたしはサインコサインタンジェントを頭に入れることができず、まごまごしている間に授業は進み、結果なにもわからんまま高校数学ジ・エンドという憂き目を経験しています。

なんとか理解できた断片

どうもHaskellさんは、いやあのカッコだらけの変態言語もそう、リストのことを
「先頭の何か及びそれに連なっている有象無象」
という見方をしているのかなというところに気づきました。誤解かもしれん。
関数の定義とかに、

f (x:xs)

って、もうこの時点でイライラするわけです。
なんで「リスト」とすればいいものを、わざわざ「先頭(x)と何か(xs)」って書き方をするのか。headだのsndだのtailだの、一個目や二個目を取得する仕組みがわざわざ関数になってたりするブサイクさにうんざりしてたわけ。100個目の取得にも関数書くのかい?最後から2個目を取りたいときはどうするんだい?
あたしにとってリストというのは、1個目だろうと最後だろうとさくっと取りに行くことができて、前から3個目が欲しいなーと思ったら

hoge[2]

とやれば取れる、そういうもののことをさしておったわけです。x:xsとかそういう気持ち悪い何かではない。
#もっとも、これは今も納得はしていなくて、たんに3つ目ほしい時どうすんの?x:xsのxsをさらにx:xsに、さらに…ってやって、x:y:z:xs(文法的に正しいかとかは知らないけどイメージだよ)みたいな取り出し方すんの?それってあんぽんたんの所業じゃないの?とは思っています。「3個目を取得する関数」使うとかそういう意図でいってねえぞ。キリないだろそれ。

が、そういう気持ち悪いアレ自体はともかく、連中にとってのリストはそういう3個目をさくっと取ってこれるステキな何かではなく、左端から一個ずつちぎることしか許されていない数珠繋ぎの芋虫を指すのであるということが、なんとなくですが腑に落ちたというわけです。
ながかったー。

#前回の記事は、書きかけを放置していたものです。前回と今回の記事の間には、だいたい1ヶ月くらいの間が開いていたりします。やれやれ。

“Haskellにおける、リストについての誤解” の続きを読む

Haskell再入門

まえにHaskellは挫折しています。
こんな本を買いました。

すごいHaskellたのしく学ぼう!って本なんですが、だいたい読んだのは1/3くらいです。モナドというやつの概念のところまでは辿りつけていません。Haskell理解の難関らしいことは知っています。
こんな状態でございますので、レビューらしきレビューを書ける段階にございません。本に対する文句はありません。楽しげな中身でたのしいです。中に書いてあることはさっぱりわかりませんが。

あたくしの話で恐縮ですが、いまだに、Haskellを書けるようになると何がいいのか、どんな変化があるのか、実のところさっぱり見えていません。この状態でモチベーションを保つのってけっこう難儀なこと。いつ放り出してもおかしくない。
安直で嘘っぽい動機としては、再帰とか遅延評価とかの概念を染み込ませておくのはメリットである、なんかカッコいい、みたいなことが挙げられます。が、これは教養のためにラテン語を覚えておくべきだと言われるくらいピンと来ない話で、もうちっと卑近な何かがほしいところ。
だってなくても生きていけるんだもの。じゃあなんでこんな本を手にとったのか。
ぼんやりとその理由はわかってきたんですけど、難儀な話であります。

あたしまだ=の左右にあるのがなんなのかすら覚束ないレベルですんで、最初の方から再帰がどうのこうのを説明してる辺りまでを繰り返し読んでます。
意味のわからないことが書かれたページに入ると、あたくしはとたんに読解を停止し、紙の上に書かれた文字をを視線で舐めてページをめくる作業に移行してしまいます。大人になって気づきましたがあたしが勉強できなかった原因はコレ。あ、わからない、となったら入力そのものを停止してしまう。
で、そんなことでは困るので、入力停止モードになったことに気づくたびに前のページヘ戻り、繰り返し繰り返し読みながら、日に1ページとかのスピードで読み進めているわけです。
毎日続ければ来年の今頃には読み終わっているはずであります。

そのコードに至るまでの思考がそもそもわからない

文法であるとかカッコが少なくて視認できないとかいう以前に、例で挙げられているコードを眺めた時に、
「なんでそういう解決方法をしようと思えたのか」
というところが理解できなくて、膝をついています。

100個からあるデータを順番にホニャララしたい。
ループですよ。そりゃループで、一個一個をどう捌いたらいいかを考えて、クルクルやる。
Haskellはもうそのへんが、のっけから違う。
まず、最後どうする?から始める。データがないときは?最後の一個の時は?を考える。
で「最後の一個」を最小単位と捉えて、再帰とかいう変なものをかぶせてどうのこうのする。
よくわかりません。

おねーさんとHaskellの現在

行き来の電車の中でほぼ毎日、上にあげた本を読んでいます。
書き口はたいへんにやわらかい文章で読みやすい。いい本だと思います。あちこちに散りばめられたユーモアも好きです。
が、あたしには難解過ぎて本題の内容が理解できないので

だん
  だん
    ね
    む
     く
       なっ
        zzz

特に帰りはこれで乗り過ごしたことが数回。危険な本です。
睡眠導入剤としてはかなり強力な一冊です。眠らずに読めるようになりたい。