読者です 読者をやめる 読者になる 読者になる

その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

スタートHaskell2 #3 に参加してきました

Haskell

Long long ago... そう、それは先週の日曜日の話でした……

第3回スタートHaskell2に参加してきました。

聞いてきた事


くわしく

  • 第6章
    • 資料はこちらにあるようです。 http://www.slideshare.net/aomoriringo/haskell-6
    • import Foo () は何の役に立つのか。何もインポートされないじゃないですか。
      • 何もインポートされない訳ではなく、「インスタンス宣言だけ」がインポートされる。そういう使い方をしたいときにどうぞ
    • Haskell のChar はUnicode で処理される
      • つまり1文字4Byte。C のように1Byte ではない。
      • 比較的リッチなデータ構造なので低速。
      • C言語ライクなChar を使いたい場合は、Data.Word8 を使うと良い。
    • やっぱり(!)foldl はいらない子
      • 基本的に、正格なfoldl' 使えば良い。
      • とは言うものの、foldl' に遅延評価を要求するような関数を渡すとオーバーフローしてしまう
      • なので遅延評価しない書き方で関数を渡してやる必要がある
    • Map.map について
      • Map: 要素が重複しない辞書的なもののこと
      • map: 写像する
      • 同じスペルであれど、両者は全くの別モノなので理解しておくと良いかも。
    • import qualified Data.Map as Map
      • リストライクなモジュールをインポートする時にはこのような書き方をする
      • なぜか: 関数名が重複してしまうから。
      • 型名は被らないようにつくられている(はず)だから、まずは型だけをインポートして、その後にqualified なりas なりすると良い。
  • 第7章
    • 資料はこちらにあるようです。 http://www.slideshare.net/a-hisame/start-haskell7
    • なるべくエクスポートリストは書いた方が良い
      • エクスポートリストを書くと、どれをエクスポートしてどれをエクスポートしないかをコンパイラに伝えられる
      • その結果、GHC が最適化してくれるから高速になる
    • 抽象データ型としてエクスポートすると?
      • 型の名前は公開するけど、データコンストラクタは公開しない(直接触らせない)
      • パターンマッチに引っかからなくなる
      • 内部実装をこっそり変えても影響が出にくくなるからハッピー
    • 全てのソースは"module" から始めるべき。そっちの方がお行儀が良い
      • ファイル名とモジュール名は一致していなければならない
      • 但し、モジュール名Main だけはファイル名とモジュール名が一致していなくても良い
      • 言語仕様的に、"module" が省略された時はモジュール名が強制的にMain になる
    • 各ファイル(モジュール)でたくさんimport を書くのは面倒臭い。import.hs みたいなファイルで必要なものを一括インポートして、各ファイルでimport.hs をインポートすれば楽じゃない?
      • そのやりかたはOK
      • けれども、1つのファイルに大量のimport 文が存在しているのは何かあやしい。分割等した方が良いんじゃ?
    • パッケージ単位で「エクスポートしたい、したくない」のようなアクセス制限は出来るのか
      • パッケージ内に何個かモジュールがあって、それらをエクスポートするかしないかの記述はCabal 5 に書く
    • レコード構文のフィールドを秘匿し、ヘルパ関数を提供する事により値を生成する事は可能か
      • できる
      • また、差分だけを記述して別の型(たとえばPerson型)を提供することもできる
      • その場合、デフォルトのPerson型を提供しておく必要がある
    • コンストラクタと型コンストラクタの違い
    • Either 型で、Left が失敗でRight が成功と決められているのはなぜか
      • ダブルミーニング(右側 & 正しい)、つまり洒落
      • 本来は左右どちらに成功の値を入れようが構わない
      • ただ、モナドで便利になる
    • Either 型はBool のなれの果て
      • Bool --> Maybe --> Either というふうに出世している
      • Bool : False | True
      • Maybe : Nothing | Maybe a
      • Either: Left a | Right a
      • というように、これらの系譜は全て左側が失敗している
    • データを宣言する時に、値の中身まで制約することは可能か? (例えば非負の値のみ受理する、のような)
      • 基本的に無理。依存型になってしまうから。
    • Haskell でInvalid な値(制約条件を満たさない値)が渡されたときはどうするの?
      • 基本的に、データコンストラクタは公開されておらず、API を通じてしか触れないようにされている
      • だから、データ型が正しく実装されていると信じるしかない。なので特別にエラー処理等はしない。
    • Data.Map にはその昔バグがあった。
      • Delete()時にバランスが取れなくなって低速になる事が。10年くらい放置されていた
    • データコンストラクタが複数ある時に、レコード構文を利用する場合は制約がある
      • 引数の個数は異なっても良いが、型・フィールドラベルは同じ順序でなければならない
    • Haskell正規表現ライブラリ
      • POSIX : デフォルトで入ってる
      • Perl的: PCRE なんかのライブラリを使えばよろし。ただ、インストールするのが難しいかも。32/64bit 問題とか色々あって。
  • 再起の鳥瞰図
    • 資料はこちらにあるようです。 http://mew.org/~kazu/material/2012-recursion.pdf
    • 末尾再起
      • 場合分けの末端で自分自身を呼び出す
      • Haskellだと'='のすぐ右が自分自身
    • 一般的な再起
      • 場合分けの末端で他の関数を呼び出す
    • 末尾再起とは、すなわちループである
    • ループで記述可能なものは末尾再起に書き換え可能である。逆も然り。
    • 末尾再起はスタックを消費しない
    • 正格評価でも末尾再起呼び出しはジャンプに変換する事が出来る。
      • コンパイラがサポートしていれば、スタックは消費されない
      • 正格評価ではなく、遅延評価の場合はいつでもジャンプに変換する事ができる
    • ループで記述可能なコードなのに、一般的な再起で記述されている場合
      • 引数を増やしてやる事によって末尾再起に書き換える事ができる
    • 末尾再起では書けない一般的な再起 ==> 末尾再起よりも強力(なはず)
    • 再起の考え方
      • 「ひとつ前のステップができているとすれば、次はどうするか」
    • Haskell における力の強さ
      • 一般的な再起、末尾再起 : 強い
      • 畳み込み : やや強い
      • 単純な高階関数(map, filter, etc...): 弱い
    • なるべく力の弱いものを使う
      • 力の強いものは何でも出来てしまうがゆえに危険
    • MapReduce でやっつけましょう

感想

Haskell のコアとも言える型の話を聞けて良かったです(小学生並みの感想)
残念なことに次回のスタートHaskell は物理的に参加不可能なので、ust 等で楽しみたいと思います。

演習問題

こちらにあるようです https://github.com/yuzutechnology/Community-StartHaskell2011/tree/master/exercises/recursion
今回は色々と多いので省略と言うことで。(気が向いたら上げるかもわかりませんが)