phadej
phadej

Reputation: 12133

Template haskell type list

Is there a way to construct type level lists using '[Foo, Bar, Maybe Quux] syntax?

It's possible to do:

promotedTypeList :: [Q Type] -> Q Type
promotedTypeList []     = promotedNilT
promotedTypeList (t:ts) = [t| $promotedConsT $t $(promotedTypeList ts) |]

But that will result into very ugly haddocks:

type Example = (:) [*] ((:) * Foo ((:) * Bar ((:) * (Maybe Quux) ([] *)))) ([] [*])

EDIT:

Haddock / GHC is smart enough to print types (almost) how user typed them:

{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DataKinds #-}
module T (Foo, Bar) where

type Foo = Int ': Bool ': Char ': '[]
type Bar = '[ Int, Bool, Char ]

GHC 7.8 GHC 7.10


Smallest example where issue is shown is at https://gist.github.com/phadej/f92e84a1f03ffb414ab4

Upvotes: 1

Views: 409

Answers (1)

user2407038
user2407038

Reputation: 14623

It turns out that TH will splice in the lifted type list incorrectly when you write the function promotedTypeList the way you have it written. In other words, the function generates a different representation of the list than creating and splicing a type level list directly. Here is a simple test to see this.

First define the TH functions:

{-# LANGUAGE QuasiQuotes, TemplateHaskell, TypeOperators #-} 

module TH where 
import Language.Haskell.TH 

typeList0, typeList1 :: [Q Type] -> Q Type
typeList0 = foldr (\x xs -> appT (appT promotedConsT x) xs) promotedNilT 
typeList1 = foldl (\xs x -> [t| $x ': $xs |]) promotedNilT T 

There are two variants - one does what you want and the other doesn't. To see precisely why, you can look at the splices:

{-# LANGUAGE QuasiQuotes, TemplateHaskell, DataKinds, TypeOperators #-} 
{-# OPTIONS -ddump-splices #-} 

module Test where 

import Language.Haskell.TH (stringE, Type(..))
import TH (typeList0, typeList1)

ex1 = $(typeList0 (map (return.ConT) [ ''Int, ''Bool, ''Char ]) >>= stringE . show) 
ex2 = $(typeList1 (map (return.ConT) [ ''Int, ''Bool, ''Char ]) >>= stringE . show) 

type Ex1 = $(typeList0 (map (return.ConT) [ ''Int, ''Bool, ''Char ]))
type Ex2 = $(typeList1 (map (return.ConT) [ ''Int, ''Bool, ''Char ]))

With haddock -h Test.hs TH.hs you get (the interesting bits)

Test.hs:12:9-82: Splicing expression
    typeList0 (map (return . ConT) [''Int, ''Bool, ''Char])
    >>= stringE . show
  ======>
    "AppT (AppT PromotedConsT (ConT GHC.Types.Int)) (AppT (AppT PromotedConsT (ConT GHC.Types.Bool)) (AppT (AppT PromotedConsT (ConT GHC.Types.Char)) PromotedNilT))"

Test.hs:13:9-82: Splicing expression
    typeList1 (map (return . ConT) [''Int, ''Bool, ''Char])
    >>= stringE . show
  ======>
    "AppT (AppT (PromotedT GHC.Types.:) (ConT GHC.Types.Char)) (AppT (AppT (PromotedT GHC.Types.:) (ConT GHC.Types.Bool)) (AppT (AppT (PromotedT GHC.Types.:) (ConT GHC.Types.Int)) PromotedNilT))"

As you can plainly see, the two simply have different representations. The latter representation encodes the lifted list constructors as the application of a PromotedT constructor to the normal list constructors. I guess Haddock simply can't deal with this.

Here is the result

enter image description here

Upvotes: 2

Related Questions