{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE QuantifiedConstraints #-} {-# LANGUAGE TypeApplications #-} import Data.Proxy data Bar t where BarInt :: (Int, Int) -> Bar Int BarChar :: [Char] -> Bar Char data FooCharTag class Mapper tag f (t :: *) where type MapperRes tag t mapper :: Proxy tag -> f t -> f (MapperRes tag t) type family Foo (t :: *) where Foo Int = Char Foo Char = Char fooChar :: Bar t -> Bar (Foo t) fooChar (BarInt (i,j) ) = BarChar (show i ++ show j) fooChar (BarChar string) = BarChar string instance Mapper FooCharTag Bar t where type MapperRes FooCharTag t = Foo t mapper _ = fooChar -- headF :: forall f y . (forall x . x -> f x) -> [y] -> f y -- headF f xs = f (head xs) headF :: forall tag f y . (forall t. Mapper tag f t) => Proxy tag -> [f y] -> f (MapperRes tag y) headF p xs = mapper p (head xs) unBarChar :: Bar Char -> String unBarChar (BarChar string) = string test1 :: String test1 = unBarChar $ headF (Proxy @FooCharTag) ([BarInt (3,4), BarInt (5,6)] :: [Bar Int]) test2 :: String test2 = concatMap unBarChar $ map fooChar [BarChar "fff"] ++ map fooChar [BarInt (3,4), BarInt (5,6)] main = do putStrLn test1 putStrLn test2