Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Haskellの型安全性の力よ〜参照透明性編〜

792 views

Published on

Haskellが表現する、型での安全性の確保について。
IO, IORef。

某所でのLT用です。
モナドはないよ!

マスコットアプリ文化祭2016エントリー作品

Published in: Software
  • Be the first to comment

Haskellの型安全性の力よ〜参照透明性編〜

  1. 1. Haskellの型安全性の力よ 〜 参照透明編 〜
  2. 2. 型の力が欲しいか? コンパイル時に バグを弾く力が欲しいか?
  3. 3. 僕 ● 名前 – あいや (aiya000) ● Haskell ● C# ● Vim
  4. 4. 本スライドについて ● 今回「モナド」は出てこないから 大丈夫だよ。
  5. 5. 前提知識 ● 参照透明性 (「純粋である」とは) ● 純粋でないことの問題点
  6. 6. 参照透明性 ● 同じ引数で関数を呼び出すと いつでもどこでも同じ結果が返ってくる 性質のこと x :: Int x = 1 f :: Int → Int f y = x + y f 10 – => 11 f 1 – => 2 f 10 – => 11
  7. 7. 参照透明性 ● 参照透明でない反例 (JavaScript) ● xへの再代入という副作用を持つ – 副作用を持つ → 参照透明でないことが多い let x = 1; const f = y => x++ + y; f(10) //=> 11 f(1) //=> 3 f(10) //=> 13
  8. 8. 参照透明性 ● 参照透明でない反例 (JavaScript) ● xへの再代入という副作用を持つ – 副作用を持つ → 参照透明でないことが多い let x = 1; const f = y => x++ + y; f(10) //=> 11 f(1) //=> 3 f(10) //=> 13 だいたい逆のこと 参照透明 <> 副作用 画面への出力や キーボードからの入力なども 副作用になる (外界へのアクセス)
  9. 9. 参照透明性 ● これも副作用 (標準出力) const f = () => console.log("ahoge");
  10. 10. 純粋でないことの問題点 ● グローバル変数やフィールドの値が どこかの関数に勝手に変更されて 期待してない値が返ってくる – => バグを生み出す
  11. 11. 純粋でないことの問題点 ● Haskellなら型レベルで参照透明性を 保証できる ● 型レベル – 副作用を扱わない型の関数で副作用を扱うと コンパイルエラーになる ● 型レベルで参照透明を保証できると 限りなくバグが少なくなる
  12. 12. 型の力 ● 様々な副作用を扱うIO型 ● 変数への再代入と加工を扱うIORef型
  13. 13. 型の力 ● 副作用を伴う関数は必ずIO型 (もしくは同系列の型)を型として持つ ● main :: IO () – HaskellのEntryPoint ● getLine :: IO String – 標準入力を受け取る関数 ● putStrLn :: String → IO () – 文字列を標準出力に出力する関数
  14. 14. 型の力 ● JavaScriptの例と似たようなやつ import Data.IORef action :: IO () action = do x <- newIORef 1 a <- f x 10 – 11 b <- f x 1 – 3 c <- f x 10 – 13 return () f :: IORef Int -> Int -> IO Int f x y = do current <- readIORef x modifyIORef x (+1) – 副作用 return $ current + y
  15. 15. import Data.IORef action :: IO () action = do x <- newIORef 1 a <- f x 10 – 11 b <- f x 1 – 3 c <- f x 10 – 13 return () f :: IORef Int -> Int -> IO Int f x y = do current <- readIORef x modifyIORef x (+1) – 副作用 return $ current + y 型の力 ● JavaScriptの例と似たようなやつf x 10 同じ呼び出しで違う戻り値 これはIO型の 副作用によるもの
  16. 16. import Data.IORef action :: () action = do x <- newIORef 1 a <- f x 10 b <- f x 1 c <- f x 10 return () f :: Int -> Int -> Int f x y = do current <- readIORef x modifyIORef x (+1) return $ current + y 型の力 ● JavaScriptの例と似たようなやつ 関数のIO …を消すと ?
  17. 17. 型の力 ● 副作用がないはずの型の関数に 副作用が含まれているので コンパイルエラー
  18. 18. 型の力 ● 副作用がないはずの型の関数に 副作用が含まれているので コンパイルエラー Program.hs:4:1: Couldn't match expected type ‘IO t3’ with actual type ‘()’ In the expression: main When checking the type of the IO action ‘main’
  19. 19. 型の力 ● 副作用がないはずの型の関数に 副作用が含まれているので コンパイルエラー Program.hs:4:1: Couldn't match expected type ‘IO t3’ with actual type ‘()’ In the expression: main When checking the type of the IO action ‘main’ Program.hs:5:3: Couldn't match type ‘IO ()’ with ‘()’ Expected type: IO (IORef Integer) -> (IORef Integer -> IO ()) -> () Actual type: IO (IORef Integer) -> (IORef Integer -> IO ()) -> IO () In a stmt of a 'do' block: x <- newIORef 1 In the expression: do { x <- newIORef 1; a <- f x 10; b <- f x 1; c <- f x 10;
  20. 20. 型の力 ● 副作用がないはずの型の関数に 副作用が含まれているので コンパイルエラー Program.hs:4:1: Couldn't match expected type ‘IO t3’ with actual type ‘()’ In the expression: main When checking the type of the IO action ‘main’ Program.hs:5:3: Couldn't match type ‘IO ()’ with ‘()’ Expected type: IO (IORef Integer) -> (IORef Integer -> IO ()) -> () Actual type: IO (IORef Integer) -> (IORef Integer -> IO ()) -> IO () In a stmt of a 'do' block: x <- newIORef 1 In the expression: do { x <- newIORef 1; a <- f x 10; b <- f x 1; c <- f x 10; Program.hs:6:8: Couldn't match expected type ‘IO t0’ with actual type ‘In In a stmt of a 'do' block: a <- f x 10 In the expression: do { x <- newIORef 1; a <- f x 10; b <- f x 1; c <- f x 10; .... }
  21. 21. 型の力 ● 副作用がないはずの型の関数に 副作用が含まれているので コンパイルエラー Program.hs:4:1: Couldn't match expected type ‘IO t3’ with actual type ‘()’ In the expression: main When checking the type of the IO action ‘main’ Program.hs:5:3: Couldn't match type ‘IO ()’ with ‘()’ Expected type: IO (IORef Integer) -> (IORef Integer -> IO ()) -> () Actual type: IO (IORef Integer) -> (IORef Integer -> IO ()) -> IO () In a stmt of a 'do' block: x <- newIORef 1 In the expression: do { x <- newIORef 1; a <- f x 10; b <- f x 1; c <- f x 10; Program.hs:6:8: Couldn't match expected type ‘IO t0’ with actual type ‘In In a stmt of a 'do' block: a <- f x 10 In the expression: do { x <- newIORef 1; a <- f x 10; b <- f x 1; c <- f x 10; .... } Program.hs:7:10: Couldn't match expected type ‘Int’ with actual type ‘IORef Integer’ In the first argument of ‘f’, namely ‘x’ In a stmt of a 'do' block: b <- f x 1 Program.hs:8:8: Couldn't match expected type ‘IO t2’ with actual type ‘Int’ In a stmt of a 'do' block: c <- f x 10 In the expression: do { x <- newIORef 1; a <- f x 10; b <- f x 1; c <- f x 10; >> Too many compile error <<
  22. 22. まとめ ● Haskellでは 手続きも値も全て 型によって制御されることになる ● さもなくば コンパイルエラーをくらえ
  23. 23. まとめ ● 今回はIOの例に限っており 型による制御能力はこの限りではない 全然この限りではない – Maybe (null可能) – Writer (ログ付き計算) – Reader (環境付き計算) – State (状態付き計算) – StateT (状態付き計算 + α)
  24. 24. End イカ、質問よろしく〜

×