user2882307
user2882307

Reputation:

Create higher order instance for a list of a type where the instance for type exists

I'm not sure this is possible. I have something like the following class:

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"]

Now I want to be able to define an instance that given an implementation for Foo f implement Foo [f].

So something like this:

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

Where the list instance just accumulates all lefts and if there no lefts it removes the outer list and moves it to the right (can this be written implemented easier?). The instance like this doesn't type check since the type isn't [f] a but [f a]. The type I expect is foo :: [f a] -> Either [String] [a]

I tried to write a type that this does for me:

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

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

But this doesn't work because I guess types do not support currying.

Is there a way to write this correctly?

Any help is appreciated!

Upvotes: 0

Views: 90

Answers (1)

user2882307
user2882307

Reputation:

I ended up changing my class to have the fully applied input and output type. It's a little bit clunky but it works.

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

Upvotes: 2

Related Questions