Как объявить класс типов с двумя параметрами так, чтобы компилятор понимал тип первого по типу второго?

Есть класс типов, объявленный следующим образом:
newtype Fix f = Fix { unFix :: f (Fix f) }

class Functor f => Fixable t f where
    toFix :: t -> Fix f
    fromFix :: Fix f -> t


Следующие типы данных являются его экземплярами:
-- Сам тип Fix
instance Functor f => Fixable (Fix f) f where
    toFix = id
    fromFix = id

-- И список
data ListF a b = Nil | Cons a b deriving (Eq, Functor)

instance Fixable [a] (ListF a) where
    toFix = apo go
      where go (x:xs) = Cons x (Right xs)
            go _ = Nil
    fromFix = cata go
      where go (Cons x xs) = x:xs
            go _ = []


В данном куске кода возникает ошибка:
main :: IO ()
main = putStrLn $ someFunc [\a b -> a ++ b, \a b -> a ++ b ++ a] "str1" "str2"
  where someFunc cs = para go cs


Из контекста понятно, что cs - список функций типа:
String -> String -> String

Но компилятор выводит слишком общий тип, а именно:
someFunc :: Fixable t (ListF (String -> String -> String)) -> (String -> String -> String)

В связи с чем возникает ошибка типизации, и компилятор требует использование расширения FlexibleContexts. Если же указать тип функции явно:
someFunc :: [String -> String -> String] -> (String -> String -> String)

код компилируется без ошибок. Как сделать так, чтобы компилятор сам выводил нужный тип?
  • Вопрос задан
  • 112 просмотров
Решения вопроса 1
wiz
@wiz
Ортодоксальный хаскелит
Есть два способа - классический через FunctionalDependencies и неоклассический - через TypeFamilies.

Функциональные зависимости сделаны специально для классов с несколькими параметрами:

{-# LANGUAGE FunctionalDependencies #-}
class Collects e ce | ce -> e where
  empty  :: ce
  insert :: e -> ce -> ce
  member :: e -> ce -> Bool
  toList :: ce -> [e]

usage :: Collects e ce => ce -> Maybe e


Семейства типов более общие и могут работать отдельно от классов. Но внутри классов можно определять так называемые "ассоциированные типы". Тогда у класса не будет этого параметра, но экземпляр должен будет предоставить функцию из себя в ассоциированный тип:

{-# LANGUAGE TypeFamilies #-}
class Collects ce where
  type Elem ce
  empty  :: ce
  insert :: Elem ce -> ce -> ce
  member :: Elem ce -> ce -> Bool
  toList :: ce -> [Elem ce]

usage :: Collects ce => ce -> Maybe (Elem ce)


Больше подробностей есть на wiki.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы