Ошибка возникает из-за того, что в вашей программе ровно два экземпляра Titles
:
instance Titles String
instance Titles (String -> String)
Они позволяют вам вызывать titleTeX
с одним и двумя аргументами соответственно, но для трех аргументов потребуется
instance Titles (String -> String -> String)
которого не существует.Или, как говорит GHC:
• No instance for (Main.Titles ([Char] -> [Char] -> String))
arising from a use of ‘titleTeX’
([Char]
- это то же самое, что String
.)
Это как если бы вы определили функцию
foo :: [Int] -> Int
foo [x] = ...
foo [x, y] = ...
но foo [x, y, z]
- ошибка.
Чтобы это работало для любого количества аргументов, нам нужно использовать рекурсию.Как и в случае со списками (где у вас обычно есть базовый регистр foo [] = ...
и рекурсивный регистр foo (x : xs) = ...
, который где-то вызывает foo xs
), нам нужно определить экземпляр Titles
в терминах других экземпляров:
instance Titles String
instance (Titles a) => Titles (String -> a)
Хитрость в том, что я не вижу способа реализовать titleTeX
, который бы соответствовал вышеуказанным объявлениям.
Мне пришлось внести другие изменения в ваш код, чтобы он работал:
{-# Language FlexibleInstances #-}
titleTeX :: (Titles a) => String -> a
titleTeX str = titleTeXAccum [str]
titleTeX
больше не метод.Это просто удобный интерфейс для фактического titleTeXAccum
метода.
В принципе, мы могли бы опустить параметр String
и определить titleTeX :: (Titles a) => a
как titleTeX = titleTexAccum []
, но тогда titleTex :: String
вылетел бы ввремя выполнения (потому что мы в конечном итоге вызываем maximum
для пустого списка).
class Titles a where
titleTeXAccum :: [String] -> a
Наш метод теперь принимает список строк, которые он (каким-то образом) превращает в значение типа a
.
instance Titles String where
titleTeXAccum acc = titleWithFrame 1 "%" "%" "%" (reverse acc)
Реализация String
проста: мы просто вызываем titleWithFrame
.Мы также передаем reverse acc
, так как порядок элементов в аккумуляторе обратный (см. Ниже).
instance (Titles a) => Titles (String -> a) where
titleTeXAccum acc str = titleTeXAccum (str : acc)
Это важная часть: общий метод titleTeXAccum
пересылает в другой метод titleTeXAccum
(другого типа / другого экземпляра Titles
).Это добавляет str
к аккумулятору.Мы могли бы написать acc ++ [str]
, чтобы добавить новый элемент в конце, но это неэффективно: вызов titleTeXAccum
с N элементами потребует O (N ^ 2) времени (из-за повторных обходов списка в ++
).Использование :
и единственный вызов reverse
один раз в конце сокращают это до O (N).