Haskell Language
모나드
수색…
소개
모나드는 조합 가능한 액션의 데이터 유형입니다. Monad
는 값이 그러한 액션을 나타내는 유형 생성자의 클래스입니다. 아마도 IO
은 가장 인식할만한 것입니다. IO a
a
값은 "현실 세계에서 값을 검색하는 a
"입니다.
우리는 타입 생성자라고 m
(예 : []
또는 Maybe
이 생길 경우) 모나드를 형성하는 instance Monad m
행동의 구성에 대한 특정 법률을 만족. 그러면 우리는 "결과가 유형 a
인 행위"라고 ma
수 있습니다.
아마 모나드
Maybe
유사 - 하늘의 값을 나타내는 데 사용됩니다 null
다른 언어를. 일반적으로 어떤 방식 으로든 실패 할 수있는 함수의 출력 유형으로 사용됩니다.
다음 기능을 고려하십시오.
halve :: Int -> Maybe Int
halve x
| even x = Just (x `div` 2)
| odd x = Nothing
생각 halve
온 따라 조치로 Int
이 홀수 인 경우 실패 정수를 반으로하려고합니다.
정수를 세 번 halve
방법은 무엇입니까?
takeOneEighth :: Int -> Maybe Int -- (after you read the 'do' sub-section:)
takeOneEighth x =
case halve x of -- do {
Nothing -> Nothing
Just oneHalf -> -- oneHalf <- halve x
case halve oneHalf of
Nothing -> Nothing
Just oneQuarter -> -- oneQuarter <- halve oneHalf
case halve oneQuarter of
Nothing -> Nothing -- oneEighth <- halve oneQuarter
Just oneEighth ->
Just oneEighth -- return oneEighth }
-
takeOneEighth
세 서열이다halve
서로 연결하는 단계. - 경우
halve
단계가 실패, 우리는 전체를 구성 할takeOneEighth
실패 할 수 있습니다. -
halve
단계가 성공하면 그 결과를 앞당기 고 싶습니다.
instance Monad Maybe where
-- (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
Nothing >>= f = Nothing -- infixl 1 >>=
Just x >>= f = Just (f x) -- also, f =<< m = m >>= f
-- return :: a -> Maybe a
return x = Just x
이제 우리는 다음과 같이 쓸 수 있습니다.
takeOneEighth :: Int -> Maybe Int
takeOneEighth x = halve x >>= halve >>= halve -- or,
-- return x >>= halve >>= halve >>= halve -- which is parsed as
-- (((return x) >>= halve) >>= halve) >>= halve -- which can also be written as
-- (halve =<<) . (halve =<<) . (halve =<<) $ return x -- or, equivalently, as
-- halve <=< halve <=< halve $ x
클라이슬리에 조성 <=<
은 (g <=< f) x = g =<< fx
또는 이와 동등하게 (f >=> g) x = fx >>= g
됩니다. 그것으로 위의 정의는
takeOneEighth :: Int -> Maybe Int
takeOneEighth = halve <=< halve <=< halve -- infixr 1 <=<
-- or, equivalently,
-- halve >=> halve >=> halve -- infixr 1 >=>
Monad
typeclass의 인스턴스 인 모든 모나드에 따라야하는 세 가지 모나드 법칙이 있습니다.
1. return x >>= f = f x
2. m >>= return = m
3. (m >>= g) >>= h = m >>= (\y -> g y >>= h)
여기서 m
은 모나드이고, f
는 a -> mb
이고 g
는 b -> mc
유형이다.
또는 이와 동등하게, 위에 정의 된 >=>
Kleisli 합성 연산자를 사용합니다.
1. return >=> g = g -- do { y <- return x ; g y } == g x
2. f >=> return = f -- do { y <- f x ; return y } == f x
3. (f >=> g) >=> h = f >=> (g >=> h) -- do { z <- do { y <- f x; g y } ; h z }
-- == do { y <- f x ; do { z <- g y; h z } }
이 법칙을 따르면 모나드에 대한 추론이 훨씬 쉬워집니다. 왜냐하면 모나드 함수를 사용하고 다른 모나드와 유사한 합리적인 방법으로 동작하도록 보장하기 때문입니다.
Maybe
모나드가 세 모나드 법칙에 복종하는지 확인합시다.
- 왼쪽 정체성 법칙 -
return x >>= f = fx
return z >>= f
= (Just z) >>= f
= f z
- 올바른 정체성 법칙 -
m >>= return = m
-
Just
데이터 생성자
Just z >>= return
= return z
= Just z
- 데이터 생성자가
Nothing
Nothing >>= return
= Nothing
- 결합 법칙 -
(m >>= f) >>= g = m >>= (\x -> fx >>= g)
-
Just
데이터 생성자
-- Left-hand side
((Just z) >>= f) >>= g
= f z >>= g
-- Right-hand side
(Just z) >>= (\x -> f x >>= g)
(\x -> f x >>= g) z
= f z >>= g
- 데이터 생성자가
Nothing
-- Left-hand side
(Nothing >>= f) >>= g
= Nothing >>= g
= Nothing
-- Right-hand side
Nothing >>= (\x -> f x >>= g)
= Nothing
IO 모나드
IO a
유형의 표현식에서 a
유형의 값을 가져올 수있는 방법이 없습니다. 이것은 실제로 모나드가 IO
를 모델링하는 데 사용되는 이유 중 상당 부분입니다.
유형 IO a
의 표현식은 실제 세계와 상호 작용할 수있는 조치를 나타내는 것으로 간주 될 수 있으며, 실행되면 유형이 a
가 될 것입니다. 예를 들어, 함수 getLine :: IO String
전주곡에서은 아래 것을 의미하지 않는다 getLine
내가 추출 할 수있는 몇 가지 특정 문자열이 -는 것을 의미 getLine
표준 입력에서 라인을 가져 오는 작업을 나타냅니다.
놀랍지 않게도, main :: IO ()
는 Haskell 프로그램이 실제 세계와 상호 작용하는 계산 / 동작을 표현하기 때문에 그렇습니다.
IO
이 모나드이기 때문에 타입 IO a
타입의 표현에 할 수 있는 일 :
첫 번째 액션을 실행하는 새 액션을 생성하기 위해
(>>)
를 사용하여 두 액션을 시퀀싱하고, 생성 한 모든 값을 삭제 한 다음 두 번째 액션을 실행합니다.-- print the lines "Hello" then "World" to stdout putStrLn "Hello" >> putStrLn "World"
때로는 첫 번째 작업에서 생성 된 값을 삭제하지 않으려는 경우가 있습니다. 실제로 두 번째 작업으로 가져 오는 것이 좋습니다. 이를 위해
>>=
합니다.IO
,(>>=) :: IO a -> (a -> IO b) -> IO b
유형을 갖습니다.-- get a line from stdin and print it back out getLine >>= putStrLn
정상적인 가치를 취하고 행동으로 변환하면 즉시 주어진 값을 반환합니다. 이 기능은
do
notation을 사용하기 전까지는 덜 분명합니다.-- make an action that just returns 5 return 5
하스켈 위키에서 IO 모나드에 대한 자세한 내용이 나와 있습니다 .
목록 모나드
목록은 모나드를 형성합니다. 그들은 이것과 같은 모나드 인스턴스화를 가지고 있습니다 :
instance Monad [] where return x = [x] xs >>= f = concat (map f xs)
계산에서 비 결정론을 에뮬레이션하기 위해 그것들을 사용할 수 있습니다. 우리가 사용하는 경우 xs >>= f
, 함수 f :: a -> [b]
리스트에 매핑된다 xs
의 각 애플리케이션의 결과리스트의 목록을 획득하는 f
의 각 요소 위에 xs
, 모든리스트 결과는 모든 결과의 하나의 목록으로 연결됩니다. 예를 들어 do notation을 사용하여 두 개의 비 결정적 숫자의 합계를 계산합니다.이 합계는 두 개의 목록에있는 모든 정수 쌍의 합계 목록으로 표현됩니다. 각 목록은 비 결정적 숫자의 가능한 모든 값을 나타냅니다.
sumnd xs ys = do
x <- xs
y <- ys
return (x + y)
또는 이와 동등하게, Control.Monad
에서 liftM2
를 사용합니다.
sumnd = liftM2 (+)
우리는 얻는다 :
> sumnd [1,2,3] [0,10]
[1,11,2,12,3,13]
적용 대상 서브 클래스로서의 모나드
GHC 7.10로, Applicative
의 슈퍼 클래스입니다 Monad
(즉,있는 모든 유형의 Monad
또한해야 Applicative
). Applicative
( pure
, <*>
)의 모든 메소드는 Monad
의 메소드 ( return
, >>=
)로 구현 될 수 있습니다.
pure
하고 return
하는 것이 동등한 목적을 제공한다는 것은 명백하므로 pure = return
. <*>
대한 정의는 너무 명확합니다.
mf <*> mx = do { f <- mf; x <- mx; return (f x) }
-- = mf >>= (\f -> mx >>= (\x -> return (f x)))
-- = [r | f <- mf, x <- mx, r <- return (f x)] -- with MonadComprehensions
-- = [f x | f <- mf, x <- mx]
이 함수는 표준 라이브러리에서 ap
로 정의됩니다.
따라서 유형에 대한 Monad
의 인스턴스를 이미 정의한 경우 효과적으로 정의를 통해 "무료"로 Applicative
의 인스턴스를 가져올 수 있습니다
instance Applicative < type > where
pure = return
(<*>) = ap
모나드 법칙과 마찬가지로 이러한 동등성은 적용되지 않지만 개발자는 항상 동등한 상태를 유지해야합니다.
모나드 계산에서 값을 추출하는 일반적인 방법이 없습니다.
값을 액션으로 래핑하고 한 계산의 결과를 다른 것으로 전달할 수 있습니다.
return :: Monad m => a -> m a
(>>=) :: Monad m => m a -> (a -> m b) -> m b
그러나 Monad의 정의는 Monad m => ma -> a
형식의 함수의 존재를 보장하지 않습니다.
즉, 일반적 으로 계산에서 값을 추출 할 방법이 없습니다 (예 : "unwrap"). 다음과 같은 경우가 많습니다.
extract :: Maybe a -> a
extract (Just x) = x -- Sure, this works, but...
extract Nothing = undefined -- We can’t extract a value from failure.
특히, IO a -> a
함수가 없기 때문에 초보자를 혼란스럽게합니다. 이 예제를 참조하십시오.
표기법
do
-notation은 모나드를위한 통사론이다. 다음은 규칙입니다.
do x <- mx do x <- mx y <- my is equivalent to do y <- my ... ...
do let a = b let a = b in ... is equivalent to do ...
do m m >> ( e is equivalent to e)
do x <- m m >>= (\x -> e is equivalent to e)
do m is equivalent to m
예를 들어, 다음 정의는 동일합니다.
example :: IO Integer
example =
putStrLn "What's your name?" >> (
getLine >>= (\name ->
putStrLn ("Hello, " ++ name ++ ".") >> (
putStrLn "What should we return?" >> (
getLine >>= (\line ->
let n = (read line :: Integer) in
return (n + n))))))
example :: IO Integer
example = do
putStrLn "What's your name?"
name <- getLine
putStrLn ("Hello, " ++ name ++ ".")
putStrLn "What should we return?"
line <- getLine
let n = (read line :: Integer)
return (n + n)
Monad의 정의
class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
모나드를 다루기위한 가장 중요한 함수는 바인드 연산자 >>=
:
(>>=) :: m a -> (a -> m b) -> m b
- 생각
ma
의 "와 행동으로 결과".a
-
a -> mb
를 "a
매개 변수에 따라b
결과가있는 동작" 이라고 생각하십시오.
>>=
첫 번째 작업의 결과를 두 번째 작업으로 파이핑하여 두 작업을 함께 처리합니다.
Monad
가 정의한 다른 함수는 다음과 같습니다.
return :: a -> m a
그 이름은 불행한 일입니다.이 return
은 명령형 프로그래밍 언어에서 발견 된 return
키워드와 아무런 관련이 없습니다.
return x
는 그 결과로 x
를 산출하는 사소한 행동입니다. (그것은 다음과 같은 의미 에서 사소합니다 :)
return x >>= f ≡ f x -- “left identity” monad law
x >>= return ≡ x -- “right identity” monad law