Подтвердить что ты не робот

Как работает функция Haskell `tell`

Я изучаю do expression и Monad, используя УЧИТЬ ВАС ХАСКЕЛЛ ДЛЯ БОЛЬШОГО ХОРОШЕГО. Там реализация gcd с использованием функции tell меня смущает.

gcd :: Int -> Int -> Writer [String] Int
gcd a b
  | b == 0 = tell ["Finished with " ++ show a ] >>= (\_ -> return a)
  | otherwise = tell [show a ++ " mod " ++ show b ++ " = " ++ show (a `mod` b)] >>= (\_ -> gcd b (a `mod` b))

gcdResult = gcd 8 3

-- result: WriterT (Identity (1,["8 mod 3 = 2","3 mod 2 = 1","2 mod 1 = 0","Finished with 1"]))

Но меня смущает функция tell. При использовании >>= (\_ -> ...) или >> результат до этого будет проигнорирован, так почему результат результата tell может быть передан окончательному результату? По моей мысли, результат tell может быть проигнорирован, а конечным результатом будет WriterT (Identity (1,[])).

4b9b3361

Ответ 1

Вы смешиваете результат с контекстом. Вы правы, что при применении >> или >>= \_ -> результат левой стороны игнорируется. Однако, если бы вся ценность была проигнорирована, это было бы совершенно бессмысленно; монадический контекст может быть передан вперед.

a >> b

Это означает "взять контекст из a и объединить его с контекстом b, сохраняя значение результата b". В случае Writer, монадический контекст заключается в том, что передаются некоторые данные только для записи.

tell :: Monoid w => w -> Writer w ()

Это (несколько упрощенный) тип tell. Он принимает значение для записи и возвращает экземпляр Writer, значение результата которого незначителен (()), но в контексте которого имеется только значение для записи, содержащее аргумент w. Когда вы применяете >>, значение результата игнорируется (что не имеет значения, потому что tell не возвращает ничего из значения через свой результат), но контекст сохраняется.