Как работает связывание в монадах функций в Haskell? - PullRequest
0 голосов
/ 17 февраля 2019

Из изучения хакелла: http://learnyouahaskell.com/for-a-few-monads-more

Экземпляр монады для функции такой:

instance Monad ((->) r) where  
    return x = \_ -> x  
    h >>= f = \w -> f (h w) w 

У меня проблемы с пониманием вывода следующего:

import Control.Monad.Instances  

addStuff :: Int -> Int  
addStuff = do  
    a <- (*2)  
    b <- (+10)  
    return (a+b)

addStuff 3 возвращает 19. Книга говорит, что 3 передаются как параметры обоим (*2) and (+10).Как?

С h >>= f = \w -> f (h w) w кажется, что (h w) привязывается к a или b.Итак, почему 6 не передается в (+10)?

Мое понимание f заключается в том, что когда (*2) равно h, f - это последние 2 строки addStuff,Когда (+10) равно h, f является последней строкой (в данном случае оператором возврата) addStuff.

Ответы [ 3 ]

0 голосов
/ 17 февраля 2019

Давайте сначала десугар блок do [отчет Haskell'10] :

addStuff = do
    a <- (*2)
    b <- (+10)
    return (a+b)

эквивалентен:

addStuff = (*2) >>= \a -> ((+10) >>= \b -> return (a + b))

Внутреннее выражение связывания ((+10) >>= \b -> return (a + b)), таким образом, можно преобразовать с определением связывания в:

\w -> (\b -> return (a + b)) ((+10) w) w

, и если мы заменим return на const, мы получим:

\w -> (const . (a+)) ((+10) w) w

Таким образом, у нас есть функция, которая принимает в качестве ввода w, а затем вызывает const . (a+) для (w+10) и w, поэтому она игнорирует последние w.Семантически это эквивалентно:

(a+) . (+10)

Так что теперь наш addStuf эквивалентен:

addStuff = (*2) >>= \a -> ((a+) . (+10))

, и если мы теперь снова используем определение для оператора связывания, мы видим:

\w -> (\a -> ((a+) . (+10))) ((*2) w) w

или короче:

\w -> (\a -> ((a+) . (+10))) (w*2) w

Теперь мы можем заменить a на (w*2) и получить:

\w -> ((w*2)+) . (+10)) w

Так что наш addStuf равенэквивалентно:

addStuff w = (w*2) + (w+10)

или более простым:

addStuff w =  3*w + 10
0 голосов
/ 17 февраля 2019

Передача обновленного аргумента между этапами вычисления (который, таким образом, будет выполнять роль состояния) является задачей другой монады, а именно монады состояния.

Функция, известная как монада Reader, проще, чем, меньше работает:

-- (return x) w = x
-- (h >>= f)  w = f (h w) w 

(h >>= (\a ->   g >>= (\b ->   return (f a b))))   w                   
=   
       (\a ->   g >>= (\b ->   return (f a b)))  (h w)   w
= 
(g >>= (\b ->   return (f  (h w)  b)))   w
= 
       (\b ->   return (f  (h w)  b)))  (g w)   w
= 
                return (f  (h w)  (g w))        w
= 
                        f  (h w)  (g w)  

Таким образом, входной аргумент w передается без изменений в оба (по расширению, все) шага вычисления.Или в конкретном случае, о котором вы спрашиваете, функция

    liftM2 (+) ( *2) ( +10)  w 
 = 
           (+) (w*2) (w+10) 

liftM2 эквивалентна отображаемому блоку do,

liftM2 f h g =
   do {
     a <- h ;
     b <- g ;
     return (f a b) }
 =
   h >>= (\ a ->
    g >>= (\ b ->  return (f a b) ))

в любой монаде.

0 голосов
/ 17 февраля 2019

Haskell do запись является синтаксическим сахаром для серии >>= привязок;в этом случае для чего-то вроде этого:

addStuff = (*2) >>= (\a -> (+10) >>= (\b -> return (a + b)))
...