2014-10-16 5 views
3

я следующий код:Путаница с Haskell делать блоки

doSomething :: [Int] -> [Int] 
doSomething arg = arg ++ [1] 

afterThreeTurns = do 
    first <- ["test"] 
    doSomething [1] -- COMMENT THIS 
    return first 

Это возвращает:

*Main> afterThreeTurns 
["test","test"] 

Если я вынимаю линия отмечена КОММЕНТИРОВАТЬ ЭТА, она возвращает [ «тест»], как и ожидалось , Зачем? Как я его вижу, что-то не должно влиять на первое?

+0

Вы должны прочитать список монады. Также имейте в виду, что необязательное выражение в блоке do семантически такое же, как привязка к значению throwaway, например '_ <- doSomething [1]' –

+0

Ответчикам: как новичок, только что просвещенный эквивалентом вы показали, я был бы на 200% счастливее, если бы был указатель на то, почему и как они эквивалентны. Хорошие ответы и комментарий. – Yosh

+1

Обозначение @Yosh 'do' эквивалентно вызовам' >> = ', потому что именно так оно и определено. Перед компиляцией кода компилятор фактически изменяет все обозначения 'do' на' >> = 'вызовы. Итак, 'do' действительно просто удобная стенограмма для вызова' >> = '(и' >> '). 'do {v <- m1; m2 v} 'превращается в' m1 >> = (\ v -> m2 v) 'и' do {m1; m2} 'превращается в' m1 >> m2' (также 'do x' всегда совпадает с' x', для любого одиночного значения или одной связанной переменной 'x'). Я добавил эту информацию к моему ответу. –

ответ

6

С doSomething [1] является [2,1], ваш код эквивалентен:

afterThreeTurns = do 
    first <- ["test"] 
    x <- [2,1] 
    return first 

Это то же самое, что и в постижении списка [ first | first <- ["test"], x <- [2,1] ], который e xplains почему вы получаете список длины 2.

Обратите внимание, что переменная x не ссылается везде, так что это также может быть написано:

afterThreeTurns = do 
    first <- ["test"] 
    _ <- [2,1] 
    return first 

Вот аналогичный случай с использованием IO монады. Код:

thirdLine = do 
    getLine 
    getLine 
    x <- getLine 
    putStrLn $ "The third line is: " ++ x 

так же, как:

thirdLine = do 
    _ <- getLine 
    _ <- getLine 
    x <- getLine 
    putStrLn $ "The third line is: " ++ x 

Вы можете получить GHC на флаг этих видов монадического заявления с флагом в -fwarn-unused-do-bind компиляторе. В вашем примере GHC испустит предупреждение:

...: Warning: 
    A do-notation statement discarded a result of type ‘Int’ 
    Suppress this warning by saying ‘_ <- doSomething [1]’ 
    or by using the flag -fno-warn-unused-do-bind 
+0

Спасибо :) Эквивалентное понимание списка особенно полезно. – houbysoft

3

Позволяет превратить это в эквивалентные вызовы к >>=:

["test"] >>= (\first -> 
       doSomething [1] >>= (\_ -> return first)) 

Компилятор всегда делает это внутренне с do. Два способа написания - это точно то же самое.

Теперь >>= для [] такой же, как concatMap с его аргументами перевернута, так что давайте идти вперед и сделать это преобразование, а также (и применить определение return x = [x]) и уменьшить:

concatMap (\first -> concatMap (\_ -> [first]) (doSomething [1])) ["test"] 
concatMap (\first -> concatMap (\_ -> [first]) ([1] ++ [1])) ["test"] 
concatMap (\first -> concatMap (\_ -> [first]) [1, 1]) ["test"] 
concatMap (\first -> concat [[first], [first]]) ["test"] 
concatMap (\first -> [first, first]) ["test"] 
concat [["test"], ["test"]] 
["test", "test"] 

Наглядно [] Монаду можно представить как представляющую собой «недетерминированное» вычисление (т. Е. Вычисление, которое может принимать один из нескольких возможных возможных результатов). Когда вы комбинируете два недетерминированных вычисления таким образом, число результатов умножается. Это связано с различными «путями», которые можно взять, по одному для каждой возможности в каждой ветви.

Для справки, здесь является преобразование между do нотации и >>= и >> вызовов (обратите внимание, что m1 >> m2 всегда должна быть эквивалентна m1 >>= (\_ -> m2)):

do x <- m1    m1 >>= (\x -> 
    m2 x        m2 x 
    ...        ...) 

do m1      m1 
    m2      >> m2 

do x      x 
0

В List монады (или []) «эффект» возвращает несколько результатов, а не один. Таким образом, ваша функция doSomething не способствует результату, но влияет на эффект, изменяя при этом длину окончательного списка.