Как правильно реализовать моноид для красно-черного дерева?

Добрый день, пытаюсь написать моноид RB Tree, код примерно такой:

data Tree a = Leaf | Node a Color (Tree a) (Tree a) deriving (Show)

instance (Ord a) => Monoid (Tree a) where
    mempty = Leaf
    l1 `mappend` l2 = foldl (\x y ->insert y x) l1 l2


instance Foldable Tree where
   foldr _ z Leaf = z
   foldr f z (Node d _ l r) = foldr f (f d (foldr f z r)) l
   foldl _ z Leaf = z
   foldl f z (Node d _ l r) = foldl f (f (foldl f z l) d) r

...

insert :: (Ord a) => a -> Tree a -> Tree a
insert x s = makeBlack $ ins s
  where ins Leaf  = Node x Red Leaf Leaf
        ins (Node d c l r)
          | x < d  = balance d c (ins l) r
          | x == d = Node d c l r
          | x > d  = balance d c l (ins r)
        makeBlack (Node d _ l r) = Node d Black l r

Проблема заключается в объявлении моноида:

• Could not deduce (Semigroup (Tree a)) arising from the superclasses of an instance declaration from the context: Ord a bound by the instance declaration at src/RedBlackTree.hs:19:10-35 • 
In the instance declaration for ‘Monoid (Tree a)’ | 19 | instance (Ord a) => Monoid (Tree a) where | ^^^^^^^^^^^^^^^^^^^^^^^^^^

Я понимаю, что для использования insert значения должны быть сопоставимы, но кажется, что я как-то неправильно направил (Ord a) в экземпляре

Бонусный вопрос: Я тоже реализовал fmap, но не понял отличия от foldMap

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
89
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема не в Ord. Если вы внимательно посмотрите на полученное сообщение об ошибке, оно начинается с «Не удалось вывести (полугруппа (дерево a)) из суперклассов объявления экземпляра». Другими словами, чтобы иметь экземпляр Monoid (Tree a), вам сначала нужно иметь экземпляр для Semigroup (Tree a). К счастью, это легко добавить:

instance (Ord a) => Semigroup (Tree a) where
    (<>) = mappend

(Возможно, более идиоматический подход состоял бы в том, чтобы определить <> в экземпляре Semigroup, используя определение foldl, которое вы использовали для mappend, а затем в вашем экземпляре для Monoid просто пропустить определение mappend.)


Бонус: разница между fmap и foldMap очевидна из их типов. Для вашего дерева fmap будет иметь тип (a -> b) -> Tree a -> Tree b, что указывает на то, что оно отображает внутренние a значения в b значения. С другой стороны, foldMap будет иметь тип Monoid m => (a -> m) -> Tree a -> m. Вы можете вызывать foldMap с помощью m ~ Tree b, и в этом случае foldMap становится очень похожим на fmap (тип будет (a -> Tree b) -> Tree a -> Tree b, что не совсем совпадает с fmap), но вы также можете вызывать его с m ~ [b] или другим моноидом. (Спасибо @amalloy и @Daniel Wagner за комментарии.)

Спасибо! это очень сложно для новичка

student422 21.11.2022 02:59

Если m ~ Tree b, то foldMap имеет тот же тип, что и fmap, но я не думаю, что правильно говорить, что одно сводится к другому. Я ожидаю, что это будет означать, что это одна и та же функция, но foldMap может изменить структуру дерева, а fmap — нет.

amalloy 21.11.2022 03:34

@amalloy На самом деле даже типы не совпадают. Вы получаете foldMap :: (a -> Tree b) -> Tree a -> Tree b, но fmap :: (a -> b) -> Tree a -> Tree b.

Daniel Wagner 21.11.2022 04:23

@DanielWagner Ах, конечно.

amalloy 21.11.2022 04:59

Другие вопросы по теме