2012-03-29 11 views
21

나는 이것을 파악하는 데 어려움이 있습니다. do 표기법을 쓸 때 다음 두 줄은 어떻게 다른가요?do 표기법의 "<-"바인딩

1. let x = expression 
2. x <- expression 

나는 볼 수 없다. 때때로 하나는 작동하고, 다른 하나는 작동합니다. 그러나 드물게 둘 다. "Learn you a akkell"은 <-이 오른쪽에있는 기호를 왼쪽에 바인딩한다고 말합니다. 그러나 단순히 xlet으로 정의하는 것과 어떻게 다른가요?

답변

25

을하고 let 문 것 아니. 실행하면 모나드 행동 readInt "x"<- 문에 의해 실행되기 때문에

import Data.Typeable 

readInt :: String -> IO Int 
readInt s = do 
    putStrLn $ "Enter value for " ++ s ++ ": " 
    readLn 

main = do 
    x <- readInt "x" 
    let y = readInt "y" 
    putStrLn $ "x :: " ++ show (typeOf x) 
    putStrLn $ "y :: " ++ show (typeOf y) 

, 프로그램은, x의 값을 요청합니다. readInt "y"이 계산되었지만 그 결과 모나드 작업이 실행되지 않기 때문에 y의 값을 묻지 않습니다.

 
Enter value for x: 
123 
x :: Int 
y :: IO Int 

x :: Int 때문에, 당신은 그것으로 정상 Int 일을 할 수있다.

putStrLn $ "x = " ++ show x 
putStrLn $ "x * 2 = " ++ show (x * 2) 

y :: IO Int 때문에, 당신은 정기적 Int의 척 수 없습니다. let 바인딩에서

putStrLn $ "y = " ++ show y -- ERROR 
putStrLn $ "y * 2 = " ++ show (y * 2) -- ERROR 
8

형식의 경우 expression은 모나드가 아닌 값이고 <-의 오른쪽은 모나드 식입니다. 예를 들어 두 번째 바인딩에서는 I/O 연산 (IO t 유형) 만 사용할 수 있습니다. 구체적으로, 두 형태는 대략적으로 번역 될 수있다 (==>가 번역 나타내고) 다음 <- 문 모나드의 값을 추출한다

do {let x = expression; rest} ==> let x = expression in do {rest} 

do {x <- operation; rest} ==> operation >>= (\ x -> do {rest}) 
13

는 표현은 어떤 종류가 있고, 당신이 그것의 이름 (내부 구조 나 패턴 매칭을)주고하고있는 모든 수 있습니다. <- 버전에서

는 표현은 mdo 블록에 어떤 모나드이다 유형 m a가 있어야합니다. IO 모나드에서, 예를 들어,이 형태의 바인딩이 오른쪽 유형 IO a의 일부 값이 있어야합니다 그래서 손으로. a 부분 (모나드 값 내부)은 왼쪽에있는 패턴에 바인딩됩니다. 이렇게하면 do 블록의 제한된 범위 내에서 모나드의 '내용'을 추출 할 수 있습니다.

do 표기법은 모나드 바인딩 연산자 (>>=>>)에 대한 구문 설탕을 읽은 것입니다. 의 설탕은 expression >>= \x ->expression (그 자체는 <-없이)의 설탕을 expression >>으로 처리 하였다. 이것은 모나드 계산의 긴 사슬을 정의하는 데 더 편리한 구문을 제공합니다. 그렇지 않으면 중첩 된 람다의 인상적인 대량을 형성하는 경향이 있습니다.

let 바인딩은 전혀 설탕을 제거하지 않습니다. do 블록의 let 블록과 do 블록의 let 블록의 유일한 차이점은 do 버전의 경우 in 키워드를 따르지 않아도된다는 것입니다. 그것이 바인딩하는 이름은 암시 적으로 do 블록의 나머지 영역에 포함됩니다.

2

하스켈은 형태가 IO a 인 명령형을 표현하여 순수 함수형 프로그래밍을 통해 부작용 프로그래밍을 조정합니다. 타입 a의 결과를 생성하는 명령형 액션 유형입니다. 이것의 결과

하나 식의 값을 변수 바인딩 작업의 수행 결과에 바인딩 개의 상이한 것들이다 :

x <- action  -- execute action and bind x to the result; may cause effect 
let x = expression -- bind x to the value of the expression; no side effects 
그래서

getLine :: IO String이 작용되는 다음과 같이 사용되어야 의미 line1 ++ line2 :: String 반면

do line <- getLine -- side effect: read from stdin 
    -- ...do stuff with line 

순수한 표현이며 let 함께 사용되어야

,
do line1 <- getLine   -- executes an action 
    line2 <- getLine   -- executes an action 
    let joined = line1 ++ line2 -- pure calculation; no action is executed 
    return joined 
2

다음은 차이점을 보여주는 간단한 예입니다. 다음 두 가지 간단한 식을 고려 :

letExpression = 2 
bindExpression = Just 2 

검색하려는 정보는 수 2입니다. 여기 당신이 그것을하는 방법이다 :

let x = letExpression 
x <- bindExpression 

let 직접 x의 값 2을 넣습니다. <-Just에서 2 값을 추출하여 x에 넣습니다.

당신은이 두 가지 표기법이 서로 호환되지 않습니다 그 이유는 예를 들어,로 볼 수 있습니다 :

let x = bindExpression

직접 x의 값 Just 2을 둘 것입니다. x <- letExpression은 추출하여 x에 넣을 항목이 없습니다.

3

let은 임의의 값에 이름을 지정하거나 일치 패턴을 매기 만합니다.

<-를 들어, 우리가 먼저 떨어져 (정말) 신비 IO 모나드에서 단계하지만, 목록 또는 Maybe 같은 "컨테이너"의 개념이 모나드를 생각해 보자. 그런 다음 <-은 해당 컨테이너 요소를 "압축 해제"하지 않습니다. "되돌리기"의 반대 작업은 return입니다.

add m1 m2 = do 
    v1 <- m1 
    v2 <- m2 
    return (v1 + v2) 

두 컨테이너의 요소를 "압축 해제"하고 값을 함께 추가 한 다음 동일한 모나드에서 다시 래핑합니다.그것은 요소의 모든 가능한 조합을 복용, 목록에서 작동합니다

main = print $ add [1, 2, 3] [40, 50] 
--[41,51,42,52,43,53] 

사실 목록의 경우 당신은뿐만 아니라 add m1 m2 = [v1 + v2 | v1 <- m1, v2 <- m2] 작성할 수 있습니다. 그러나 우리의 버전도 Maybe의 작동 :

main = print $ add (Just 3) (Just 12) 
--Just 15 
main = print $ add (Just 3) Nothing 
--Nothing 

지금 IO 전혀 다르지하지 않습니다. 단일 값을위한 컨테이너이지만 바이러스와 같이 "위험한"불필요한 값입니다. 직접적으로 만져서는 안됩니다. do -Block은 여기 유리 용기이며, <-은 내부의 물건을 조작하는 내장 "장갑"입니다. return을 사용하면 준비가되었을 때 전체 그대로의 컨테이너 (위험한 콘텐츠가 아닌)를 제공합니다. 그런데 add 함수는 IO 값 (파일이나 명령 행 또는 임의 생성기에서 가져온 값)과 함께 작동합니다.

관련 문제