【问题标题】:Template haskell type list模板 haskell 类型列表
【发布时间】:2016-03-31 01:52:11
【问题描述】:

有没有办法使用'[Foo, Bar, Maybe Quux] 语法构造类型级列表?

可以这样做:

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

但这会导致非常难看的黑线鳕:

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

编辑:

Haddock / GHC 足够聪明,可以(几乎)打印出用户输入的类型:

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

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


显示问题的最小示例是https://gist.github.com/phadej/f92e84a1f03ffb414ab4

【问题讨论】:

  • 该语法与该类型完全对应,即'[X, Y]X ': Y ': '[] 完全相同。前者只是语法糖。列表的打印方式不取决于模板 haskell,而是取决于 GHC 使用的漂亮打印机(或者在本例中为 Haddock)。旧版本的 GHC 会将类型运算符打印为前缀函数,并将包含 kind 参数到类型函数。我不知道您使用的是哪个版本的 Haddock,但我认为这是一个错误 - 不久前它已在 GHC 中修复。
  • @user2407038 GHC 将类型打印为程序员(在本例中为 template-haskell)编写的类型。 IE。我想输入'[X, Y],但不能使用template-haskell 因此GHC 只能看到X ': Y ': []。没有种类的黑线鳕中的中缀版本已经是一个改进,所以如果有人可以确认它会是这样,比如在 GHC 8.0 中,我会很高兴。
  • "GHC 打印程序员编写的类型" 虽然这通常是正确的,但对于类型级别的列表来说并非如此。定义type X = '[ Int, Bool, Char ]type Y = Int ': Bool ': Char ': '[] 并询问它们的种类或信息(:kind!:i),它将使用特殊的列表语法打印它们。事实上你可以写:kind! forall x . (x ': x ': x ': '[]) 和 ghc 7.8 和 7.10 给我forall (k :: BOX) (x :: k). '[x, x, x]
  • @user2407038 我不太关心 ghci,但关心 haddock 文档。请参阅问题中的编辑。
  • 这似乎是一个黑线鳕问题,在黑线鳕跟踪器中打开了问题:github.com/haskell/haddock/issues/466

标签: haskell template-haskell


【解决方案1】:

事实证明,当你编写函数promotedTypeList 时,TH 会错误地拼接到提升的类型列表中。换句话说,该函数生成的列表表示形式与直接创建和拼接类型级别列表不同。这是一个简单的测试来看看这个。

首先定义TH函数:

{-# 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 

有两种变体 - 一种满足您的需求,另一种则不满足。要确切了解原因,您可以查看接头:

{-# 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 ]))

有了haddock -h Test.hs TH.hs,你会得到(有趣的部分)

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

如您所见,两者只是具有不同的表示。后一种表示将提升的列表构造函数编码为PromotedT 构造函数对普通列表构造函数的应用。我猜黑线鳕根本无法处理这个问题。

这是结果

【讨论】:

  • 如果我将您的版本修改为` [t| $promotedConsT $x $xs |])promotedNil` 我会让Ex1Ex2 打印相同的内容。现在我想知道为什么它不能使用generics-sop
  • gist.github.com/phadej/f92e84a1f03ffb414ab4 是我可以重现它的最小示例。 tySynInstD 发生了一些奇怪的事情,tySynD 不会发生这种情况。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-05
  • 1970-01-01
  • 2011-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多