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

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

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

第1回スタートHaskell2に行ってきました。

結論

Haskell は面白い。そしてすごい。以上!

聞いてきたこと

「すごいHaskell 楽しく学ぼう」(以下「すごいH本」)の中身は、
  • イントロダクション: @Lost_dog_ さん
  • 第1章:はじめの第一歩: @Lost_dog_ さん
  • 第2章:型を信じろ!: @skoji さん
LT では、
という内容を拝聴してきました。(上に書いたLT のタイトルは僕が勝手に付けました(正しいタイトルを失念してしまったので……)。スミマセン)

聞いてきた事の詳細

  • Haskell はlazy(non-strict)
  • Haskell は純粋
    • 関数は同じ引数に対して必ず同じ結果を返す
    • 変数は無い(再代入してはならない)
  • Haskell は静的型付言語
    • コンパイル時に型チェックするからバグが減る
    • Haskell は勝手に型を変換しない(例えば、勝手にInt をFloat に変換したりしない)
    • Haskell は型を推論してくれる
  • Haskell は型クラスがある
  • 最後のお願いです。……Haskell' のこと、忘れて下さい……
  • いつからInt(Float) だと錯覚していた?
    • 1 と書いても、この時点ではInt ではなく、Num a (Int なのかFloat なのか、はたまた別の数値的な型なのかがわからない)
    • 1.0 と書いても、この時点ではFloat ではなく、Floating a (Float なのかDouble なのかがわからない)
    • 明示的に書くか、型推論をしない限り、Haskell は型がわからない!
  • Haskell をコンパイルして実行形式にすると、そのバイナリは型データを全部捨て去る
  • Haskell に於けるUML 的な存在は型クラス
  • デバッグはモジュール単位で分割して行えば良いんじゃない
  • Haskell でもユニットテストは出来る
  • Haskell を上手いこと勉強するには
  • Yesod
    • Haskell 製Web アプリケーション
    • SQL インジェクションがない
    • 他にもいろいろセキュリティが強固
    • 自リソース内でのリンク切れが起こらない(リンク切れがあるとコンパイル時にエラーが出る)
    • コンパイルするから、「実行しないとわからない」という潰すのが難しいエラーをあらかじめ減らしておくことができる
    • 詳しくはこちら。20120527yesod

感想

あらかじめすごいH本を一通り読んでから臨んだので、復習と言った体で話を聞く事が出来た為、理解が深まったように思います。
(中学・高校時代もこんな感じで授業の予習をしっかりしていれば良かったのに……俺の馬鹿馬鹿!)

独学も結構ですが、やっぱり学習対象を熟知している人から直接教えてもらえる、というのは素晴らしい!
本とにらめっこしてもいまいち理解出来なかった事が、エキスパートに教えを請うことでスッと落ちてくる快感!
今回の勉強会に参加して、Haskell みたいな難解(だと個人的には思っている)な言語は自分以外の誰かから直接教えてもらった方が良いように思いました。

LT に関しては高度な内容も含まれていた(と思います。だよね? 高度だったよね? それとも俺がただ単にノータリンなだけ?)ので、
初心者の僕にはちょっとハードルが高かったですが、面白かったです。Haskell ってこんな事もできるんだ……という印象を受けました。

まだ先の話ですが、僕が何度も、そう何度も挫折した「モナド」や「I/O」の話を聞けるのが楽しみです。
次回も参加したいです!

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!


[おまけ]僕が現地で考えた、スタートHaskell2 の練習問題の回答

1.
Prelude> 18.6 / 31 - 2.604 / 3.1 - 0.8556 / 0.31
2.
Prelude> ['z', 'v' .. 'b']
3.
Prelude> tail (take 3 aList) ++ [last (init aList)]
4.
Prelude> 1:2:3:4:5:[]
5. 欠番
6.
Prelude> head [x | x <- [1..], x `mod` 2 == 0, x `mod` 3 == 0, x `mod` 4 == 0, x `mod` 5 == 0, x `mod` 6 == 0, x `mod` 7 == 0, x `mod` 8 == 0, x `mod` 9 == 0, x `mod` 10 == 0]
なんて醜いコードなんだ……
会場で参加者の方が発表していた
Prelude> head [x | x <- [1..], all(\y -> x `mod` y == 0) [2..10]]
というコードの方が明らかにスマートだと思います。
7.
Prelude> head [(a, b, c) | a <- [1 .. 999], b <- [(a + 1)..999], c <- [(b + 1)..999], a + b + c == 1000, a^2 + b^2 == c^2]
こちらよりも、現地で@kazu_yamamoto さんが示していた
Prelude> [(a,b,c) | a <- [1..999], b <- [a..998], let c=1000-a-b, a^2 + b^2 == c^2]
の方が数倍(数百、数千倍?)高速です。(このlet の使い方はどういう意味なんだろう……)
8.
Prelude> length [y | y <- [show x | x <- [100 .. 999]], head y == last y]
9.
Prelude> length [x | x <- [a * 10000 + b * 1000 + c * 100 + d *10 + e | a <- [1..3], b <- [1..3], c <- [1..3], d <- [1..3], e <- [1..3]], x `mod` 3 == 0]

1.
  • null 関数
    • 任意の型のリストを受け取ってBool 値を返す関数
  • abs 関数
    • Num 型クラスに属する変数を受け取って、その型の結果を返す関数
  • take 関数
    • take :: Int -> [a] -> [a] (Int 型の変数を受け取り、任意の型のリストを受け取って、その型のリストを返す関数)
2.
2-1
  • 'a'
    • 'a' :: Char
  • ['a', 'b', 'c']
    • ['a', 'b', 'c'] :: [Char]
  • ('a', 'b', 'c')
    • ('a', 'b', 'c') :: (Char, Char, Char)
  • 'a' == 'a'
    • 'a' == 'a' :: Bool
  • ("Hello", "world!")
    • ("Hello", "world!") :: ([Char], [Char])
  • 100
    • 100 :: Num a => a
  • show
    • show :: a -> [Char] (←誤り。正しくは show :: Show a => a -> String)
  • take 3 [1..]
    • take 3 [1..] :: Num a => [a] (←誤り。正しくは (Enum a, Num a) => [a])
  • [(False, 'o'), (True, '1')]
    • [(False, 'o'), (True, '1')] :: [(Bool, Char)]
  • ([False, True], ['0', '1'])
    • ([False, True], ['0', '1']) :: ([Bool], [Char])
  • [init , tail , reverse]
    • [init , tail , reverse] :: [ [a] -> [a] ]
2-2
  • second xs = head (tail xs)
    • second :: [a] -> a
  • swap (x, y) = (y, x)
    • swap :: (a, b) -> (b, a) (←正しくは swap :: (t1, t) -> (t, t1))
  • pair x y = (x, y)
    • pair :: a -> b -> (a, b) (←正しくは pair :: t -> t1 -> (t, t1))
  • double x = x * 2
    • double :: Num a => a -> a
  • palindrome xs = reverse xs == xs
    • palindrome :: Eq a => [a] -> Bool
  • twice f x = f (f x)
    • twice :: (a -> a) -> a -> a (←正しくは twice :: (t -> t) -> t -> t)
3.
(*) は Num a => a -> a -> a
(/) は Fractional a => a -> a -> a
型クラスが異なるので、両式は異なる。
4.
型だけから想像するの難しくないですか……? 慣れれば分かるようになるんでしょうか。
と言うわけでHoogle で検索書けた奴を貼るという暴挙に。
5.
pred
6.
Prelude> [(42, last)]
[(42, last)] :: Num t => [(t, [a] -> a)]
7.
1."hoge" :: [Char] 2.let foo = 1 :: Integer :t foo foo :: Integer 3.let bar = [12.3, 23.4] :t bar bar :: [Double] 4.? 5.id 3 6.replicate 3 2 7.? |
タプルの奴が分かりませんでした
8.
Eq: http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.html#t:Eq
Eq とShow 両方に属する型: Char とか
9.
Prelude> maxBound :: Int
2147483647
10.
pi はFloating a => a だから、Int と演算させようとするとエラーになる
11.
Prelude> (1 :: Int) + (1.0 :: Double) は、Int とDouble を混ぜて演算しているからエラーになる。
Prelude> 1 + 1.0 は、Haskell 側で型推論してくれるからエラーが起きない。(1と1.0 が両方ともFractionalとして解釈されるはず……)
12.
Prelude> fromIntegral(1 :: Int) + (1.0 :: Double)
これで良いんでしょうか? 問題の意図が若干読み取れませんでした。
13.
N = a 'div' length xs
  where
    a = 10
   xs = [1, 2, 3, 4, 5]
largeN = a `div` length xs
    where
        a = 10
        xs = [1, 2, 3, 4, 5]
  • 関数名の先頭は小文字から始まらなければならない
  • 関数を中置関数として扱う場合は、シングルクォートではなくバッククォートで括らなければならな
  • where 節以下のインデンテーションは同じ深さでなければならない