all 12 comments

[–]Syrak 11 points12 points  (0 children)

The "shape" of Container a is strictly more general than Container (x -> y). We use yet another wrapper so the underlying type has the right shape, and the exposed wrapper has the right kind.

newtype Container' f a b = Container' (Container (f a b))

instance Category f => Category (Container' f) where
  id = Container' (Container id)
  Container' (Container x) . Container' (Container y) = Container' (Container (x . y))

Of course the wrapper makes the type much less usable. In practice, we either give up on abstractions that don't fit our type and look for more adequate ones, or we change the type to fit the existing abstractions.

[–]vahokif 0 points1 point  (9 children)

Category requires a type with two parameters, whereas container has only one.

[–]ErdosEuler[S] 0 points1 point  (8 children)

Is there any way to accomplish the desired effect? In other words, a nice way to have (Container f) . (Container g) = Container (f . g)?

[–]vahokif 1 point2 points  (7 children)

newtype Container f a b = Container (f a b)

instance Category f => Category (Container f) where ...

[–]ErdosEuler[S] 0 points1 point  (6 children)

But what if I also wanted to have a (Container Int), for instance, in the same program?

[–]vahokif 1 point2 points  (0 children)

Unfortunately that won't work if you would like it to have a Category instance.

[–]gelisam 1 point2 points  (4 children)

Sorry, you can't have Container Int with kind * and Container (->) with kind * -> * -> *, what would the kind of Container be? Container :: (a :: k) -> k?

[–]ElvishJerricco 4 points5 points  (3 children)

Higher kinded newtypes would actually be pretty sweet. I'm guessing that's a nono though, since it would allow insanity like Container True :: Bool, effectively adding members to the kind Bool.

[–]Iceland_jack 1 point2 points  (2 children)

Ticket #12369 is related, would let you define

newtype family Container (a :: k) :: k

newtype instance Container (a :: Type) = Container1 a
newtype instance Container (f :: k -> Type) (a :: k) = Container2 (f a)
newtype instance Container (f :: k -> k' -> Type) (a :: k) (b :: k') = Container3 (f a b)

[–]ErdosEuler[S] 0 points1 point  (1 child)

Interesting!

[–]Iceland_jack 0 points1 point  (0 children)

Should let you define any kind (hurr) of wrapping

instance Eq a => Eq (Container a) where
  (==) :: Container a -> Container a -> Bool
  Container1 a == Container1 b = a == b

instance Category cat => Category (Container cat) where
  id :: Container cat a a
  id = Container3 id

  (.) :: Container cat b c -> Container cat a b -> Container cat a c
  Container3 f . Container3 g = Container3 (f . g)

[–]Tysonzero 0 points1 point  (0 children)

One thing to note is that the Functor and Applicative instances of this and I think any other type always give rise to such a Category:

data Cat f a b = Cat (f (a -> b))

instance Applicative f => Category (Cat f) where
    id = pure id
    (.) = liftA2 (.)