2016-02-29 3 views

ответ

2

<- является >>= (bind), где let является fmap в do блоке.

Кража пример из here:

do x1 <- action1 x0 
    x2 <- action2 x1 
    action3 x1 x2 

-- is equivalent to: 
action1 x0 >>= \ x1 -> action2 x1 >>= \ x2 -> action3 x1 x2 

action1, action2 & action3 возвращают какое-то монады, говорят:

action1 :: (Monad m) => a -> m b 
action2 :: (Monad m) => b -> m c 
action3 :: (Monad m) => b -> c -> m d 

Вы можете переписать пусть привязок, как например:

-- assume action1 & action3 are the same 
-- but action2 is thus: 
action2 :: b -> c 

do 
    x1 <- action1 x0 
    let x2 = action2 x1 
    action3 x1 x2 

do 
    x1 <- action1 x0 
    x2 <- return & action2 x1 
    action3 x1 x2 

-- of course it doesn't make sense to call action2 
-- an action anymore, it's just a pure function. 
3

let дает имя для вызова функции.

<- связывает результат монадической операции в текущей монаде с именем.

Они совсем разные. Используйте let для результатов функций вне монады, то есть нормальных чистых функций. Используйте <- для любого монадического, так как он «разворачивает» результат монады и позволяет вам получить значение внутри него.

Например:

предположить функцию IO со следующей подписью

frobnicate :: String -> IO Bool 

и чистой функции

dothing :: Bool -> Bool 

мы можем сделать это

main :: IO() 
main = do 
    x <- frobnicate "Hello" 
    let y = frobnicate "Hello" 
    -- z <- dothing x 
    let z = dothing x 
    return() 

и мы знать т hat x :: Bool, потому что Bool был извлечен из результата операции IO для нас (операция выполняется, и результат называется x, поэтому мы можем использовать его позже).

Мы также знаем, что y :: IO Bool - операция не была выполнена, это потенциал для операции ввода-вывода в будущем. Таким образом, единственная полезная вещь, которую мы можем сделать с y, запускается позже, связывает результат и получается с Bool внутри этого пути, но после let, что Bool еще не существует.

Третья строка закомментирована, потому что она не будет компилироваться - вы не можете выполнить монадическое связывание с операцией, которая не находится в соответствующей монаде. dothing не возвращает IO ничего, поэтому вы не можете связать его внутри функции IO().

Четвертая линия проста - z сделан, чтобы быть результатом dothing x, где x было значение распакованного запуска frobnicate "Hello" ранее.

Все этот просто синтаксический сахар для операций «реальные» монадной снизу, так что расширяется (без закомментированных частей) на что-то вроде

main = frobnicate "Hello" >>= (\x -> let y = frobnicate "Hello" 
             z = dothing x 
             in return()) 

В примере, конечно, не имеет никакого смысла, но, надеюсь, он иллюстрирует, где let и <- отличаются в пределах do обозначение.

TL; DR: использовать <- для присвоения имен результатам монодических операций, let для присвоения имен всем остальным.

1

Хороший пример визуализировать то, что <- делает:

do 
    a <- ['a'..'z'] 
    b <- [1..3] 
    pure (a,b) 

Вы можете попробовать это в онлайн РЕПЛ на try.frege-lang.org (Вы можете ввести это в одну строку:

do { a <- ['a'..'z']; b <- [1..3]; pure (a,b) } 
Смежные вопросы