Создайте экземпляр более высокого порядка для списка типа, где экземпляр для типа существует

Я не уверен, что это возможно. У меня есть что-то вроде следующего класса:

data Bar a = Bar1 a | Bar2

class Foo f where
  foo :: f a -> Either [String] a

instance Foo Bar where
  foo (Bar1 x) = Right x
  foo Bar2 = Left ["is Bar2"]

Теперь я хочу иметь возможность определить экземпляр, в котором реализована реализация Foo f для реализации Foo [f].

Что-то вроде этого:

data Bar a = Bar1 a | Bar2

class Foo f where
  foo :: f a -> Either [String] a

instance Foo Bar where
  foo (Bar1 x) = Right x
  foo Bar2 = Left ["is Bar2"]

instance Foo f => Foo [f] where
  foo as = foldr reducer (Right []) (foo <$> as)
    where
      reducer (Right n) (Right xs) = Right $ n:xs
      reducer (Left xs) (Right _)  = Left xs
      reducer (Right _) (Left xs)  = Left xs
      reducer (Left xs) (Left ys)  = Left $ xs ++ ys

Где экземпляр списка просто накапливает все левые, и если левых нет, он удаляет внешний список и перемещает его вправо (можно ли это написать проще?). Такой экземпляр не проверяет тип, поскольку тип не [f] a, а [f a]. Тип, который я ожидаю, это foo :: [f a] -> Either [String] [a]

Я попытался написать тип, который это делает для меня:

type ApplyList (f :: Type -> Type) (a :: Type) = [f a]

instance Foo f => Foo (ApplyList f) where
...

Но это не работает, потому что я предполагаю, что типы не поддерживают каррирование.

Есть ли способ написать это правильно?

Любая помощь приветствуется!


person John Smith    schedule 18.05.2020    source источник
comment
Можете ли вы добавить тип foo, который вы ожидаете для своего экземпляра? Например. что-то вроде foo :: Foo f => f [a] -> Either [String] a ? Или что? (Кроме того, я почти уверен, что вам не нужен [f], который является довольно продвинутой вещью в Haskell из-за DataKinds — и не является типом, например, [f] a недоброжелателен.)   -  person chi    schedule 18.05.2020
comment
@чи В этом проблема. [f] a плохо относится. Я ищу тип [f a].   -  person John Smith    schedule 18.05.2020
comment
Я не думаю, что вы можете иметь foo :: [f a] -> Either [String] [a] с вашим классом: ваш класс делает тип возвращаемого значения Either [String] a, который не зависит от f, поэтому его нельзя изменить в экземплярах. Вам, вероятно, потребуются некоторые изменения в определении вашего класса, возможно, добавление в класс еще одного параметра a, чтобы позже его можно было преобразовать в [a]. Может быть, с каким-нибудь фундэпом (или типом семьи?).   -  person chi    schedule 18.05.2020
comment
В экземпляре, который вы пытаетесь написать, вы можете вернуть список a, но класс говорит, что вы можете вернуть не более одного a. Поэтому вам следует переосмыслить, какую абстракцию представляет этот класс типов. Один a или много a? Это зависит от f?   -  person Li-yao Xia    schedule 18.05.2020


Ответы (1)


В итоге я изменил свой класс, чтобы иметь полностью применяемый тип ввода и вывода. Это немного неуклюже, но это работает.

class Foo f g where
  foo :: f -> Either [String] g

instance Foo (Bar a) a where
  foo (Bar1 x) = Right x
  foo Bar2 = Left ["is Bar2"]

instance Foo f g => Foo [f] [g] where
  foo as = foldr reducer (Right []) (foo <$> as)
    where
      reducer (Right n) (Right xs) = Right $ n:xs
      reducer (Left xs) (Right _)  = Left xs
      reducer (Right _) (Left xs)  = Left xs
      reducer (Left xs) (Left ys)  = Left $ xs ++ ys
person John Smith    schedule 19.05.2020