Это вывод типа 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}