Это вывод типа Haskell в действии или что-то еще?

Я работаю через интернет Лях книга (ссылка приведет вас непосредственно к разделу, что мой вопрос).

автор определяет тип данных двоичного дерева и показывает, как его можно сделать экземпляром типа Foldable (определенного в Data.Складная) путем реализации функции foldMap:

import Data.Monoid
import qualified Data.Foldable as F

data Tree a = Empty | Node a (Tree a) (Tree a) deriving (Show, Read, Eq)

instance F.Foldable Tree where  
  foldMap f Empty = mempty  
  foldMap f (Node x l r) = F.foldMap f l `mappend`  
                           f x           `mappend`  
                           F.foldMap f r 

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

F.foldMap :: (Monoid m, F.Foldable t) => (a -> m) -> t a -> m

таким образом, он принимает функцию, которая принимает экземпляр типа " a " и возвращает моноид.

теперь в качестве примера, автор создает экземпляр дерева

    testTree = Node 5  
                 (Node 3  
                    (Node 1 Empty Empty)  
                    (Node 6 Empty Empty)  
                 )  
                 (Node 9  
                    (Node 8 Empty Empty)  
                    (Node 10 Empty Empty)  
                 )  

и выполняет следующую створку (определенную для складных типов):

F.foldl (+) 0 testTree -- the answer is 42 (sum of the Node Integers)

мой вопрос в том, как Haskell выясняет, что добавление по целочисленному типу - запрос Haskell для типа testTree дает дерево [Integer] - можно рассматривать как моноидную операцию (если моя терминология верна)?

(моя собственная попытка ответа: автор нескольких абзацев перед этим разделом описывается, как Num тип можно интерпретировать как моноидом введите двумя разными способами; обернув их в Sum и продукт тип С (+) и (*) в качестве mappend функции и 0 и 1 Как mempty элемент, соответственно. Тип "а" (дерево a) каким-то образом выводится как принадлежащий Sum type (путь Haskell по-разному интерпретирует числовые значения в соответствии с контекстом) или это что-то совсем другое? ]

2 ответов


мой вопрос в том, как Haskell выясняет, что добавление по целочисленному типу - запрос Haskell для типа testTree дает дерево [Integer] - можно рассматривать как моноидную операцию (если моя терминология верна)?

не может! На самом деле, нет Monoid экземпляр Integer.

теперь, не поймите меня неправильно--целые числа are моноидом по сложению. Однако они также являются моноидом при умножении, и Хаскелл имеет нет способа узнать, что использовать, следовательно,newtype фантики.

но... ничего из этого здесь не происходит. Двигаться дальше...

(моя собственная попытка ответа: автор нескольких абзацев перед этим разделом описывает, как тип Num может быть интерпретирован как Моноидный тип двумя различными способами; обернув их в сумму и тип продукта с (+) и (*) как функции mappend и 0 и 1 как элемент mempty, соответственно. Тип "а" (дерево а) каким-то образом выводится как принадлежность к типу суммы (как Хаскелл по-разному интерпретирует числовые значения в соответствии с контекстом) или это что-то совсем другое? ]

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

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

второй момент-это то, что я подозреваю, что вам действительно нужно-как он создает общий foldl это не нужно Monoid, когда все, что вы определили, это foldMap, что ли? Чтобы ответить на это, мы можем просто посмотрите на реализацию по умолчанию of foldl:

foldl :: (a -> b -> a) -> a -> t b -> a
foldl f z t = appEndo (getDual (foldMap (Dual . Endo . flip f) t)) z

здесь Endo is другой newtype фантик, специально для функции a -> a дав Monoid композиции, с id как личность, в то время как Dual - это оболочка это меняет направление a Monoid. Так что Monoid на самом деле он использует здесь, поэтому он может клеить использование (+) вместе с составом функции, после этого приложите результат к значению семени.


моноид на самом деле здесь не используется. Последняя строка использует F.foldl в котором имеется подпись F.Foldable t => (a -> b -> a) -> a -> t b -> a. В основном вы "вручную" используете моноид, поставляя (+) и 0.

если вы хотите использовать моноид "неявно", вы можете использовать F.fold (который имеет подпись (F.Foldable t, Monoid m) -> t m -> m). В этом случае, если вы попробуете, вы получите:

*Main> F.fold testTree

<interactive>:1:1:
    No instance for (Monoid Integer)
      arising from a use of `F.fold'
    Possible fix: add an instance declaration for (Monoid Integer)
    In the expression: F.fold testTree
    In an equation for `it': it = F.fold testTree
*Main> :t F.foldl

теперь GHCI жалуется, что для Integer нет Моноидного экземпляра, как и должно быть. Вы должны выбрать сумму или продукт, обернув Целое число вверх. Для этого мы можем использовать F.foldMap (подпись (F.Foldable t, Monoid m) => (a -> m) -> t a -> m):

*Main> F.foldMap Sum testTree
Sum {getSum = 42}