Based on the comments above, there seems to be no clean way to write the functions I need using the hofs provided Control.Newtype. However, there seems to be another option: creating instances for non-new types.
Example with the newtype package :
{-
FlexibleInstances
import Control.Newtype
instance (Newtype n o) => Newtype [n] [o] where
pack = map pack
unpack = map unpack
instance (Newtype n o) => Newtype (Maybe n) (Maybe o) where
pack = fmap pack
unpack = fmap unpack
instance (Newtype n o, Newtype n' o') => Newtype (n -> n') (o -> o') where
pack f = pack . f . unpack
unpack f = unpack . f . pack
-- a newtype wrapper for Nums
newtype NNum a = NNum {unNNum :: a}
instance Newtype (NNum a) a where
pack = NNum
unpack = unNNum
ntimes5 :: (Num a) => NNum a -> NNum a
ntimes5 = pack sum . replicate 5
foo :: a -> Maybe [a]
foo = undefined
bar :: NNum a -> Maybe [NNum a]
bar = pack foo
As mentioned in bheklilr, this is required UndecidableInstances, but it does not seem to require excessive signatures. However, we can do better using the newtype-generics package :
{-# LANGUAGE TypeFamilies, DeriveGeneric #-}
import Control.Newtype
import GHC.Generics
instance (Newtype a) => Newtype [a] where
type O [a] = [O a]
pack = map pack
unpack = map unpack
instance (Newtype a) => Newtype (Maybe a) where
type O (Maybe a) = Maybe (O a)
pack = fmap pack
unpack = fmap unpack
instance (Newtype a, Newtype b) => Newtype (a -> b) where
type O (a -> b) = (O a -> O b)
pack f = pack . f . unpack
unpack f = unpack . f . pack
newtype NNum a = NNum {unNNum :: a} deriving (Generic)
instance Newtype (NNum a)
ntimes5 :: (Num a) => NNum a -> NNum a
ntimes5 = pack sum . replicate 5
foo :: a -> Maybe [a]
foo = undefined
bar :: NNum a -> Maybe [NNum a]
bar = pack foo
(, Newtype , .) , , - UndecidableInstances FlexibleInstances, . , , , , .