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정상적인 가치를 취하고 행동으로 변환하면 즉시 주어진 값을 반환합니다. 이 기능은
donotation을 사용하기 전까지는 덜 분명합니다.-- 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