模板 Haskell 做变量赋值说明
Template Haskell do variable assignment clarification
我正在看下面的 TH 代码,有几个问题用 --
标记:
newtype C a = C ExpQ
unC (C x) = x --what is the significance of this
clift1 :: ExpQ -> C t -> C a --why are the C's here followed by t and a
clift1 g (C x) = C $ do f <- g --is the g an ExpQ? What is the f here, what does it signify?
tx <- x --what is the tx here signify?
return $ AppE f tx
正如标题所暗示的,我特别不确定f
和tx
来自哪里以及它们在这里代表什么。我认为它们只是被赋值的变量,以便它们可以用作 AppE 的参数,这是正确的吗?但在这种情况下,为什么?为什么不简单地使用这些参数调用一个函数而不是整个 clift1 do 函数?
ExpQ
是一个 monadic 值(在 Q
monad 中),它给出了一些 Haskell 表达式的模板 Haskell 表示。底层表达式可以是任何类型,所以你可以有一个 ExpQ
代表一个布尔表达式,你可以使用 TH quotation:
{-# LANGUAGE TemplateHaskell #-}
import Language.Haskell.TH
boolExpr :: ExpQ
boolExpr = [| True && False |]
或直接从 TH 基元构建它:
-- almost the same as "boolExpr" above
boolExpr' :: ExpQ
boolExpr' = infixE (Just (conE (mkName "True")))
(varE (mkName "&&"))
(Just (conE (mkName "False")))
您还可以使用 ExpQ
表示整数表达式:
intExpr :: ExpQ
intExpr = [| 2 + length "abc" |]
请注意,boolExpr
和 intExpr
具有相同的类型 ExpQ
,即使它们表示具有不同类型的基础 Haskell 表达式(即 Bool
和 Int
)。这是因为模板 Haskell 提供类型 Haskell 表达式的 非类型化表示 。
使用 Haskell 表达式的 类型表示 会很有帮助,其中表示的表达式的类型在表示。一个简单的方法是为 ExpQ
类型定义一个 newtype
同义词,它采用所谓的“幻影”类型参数:
newtype C a = C ExpQ
这定义了一个新类型(或者实际上是整个类型集合,C Int
、C Bool
等),其值只是 ExpQ
值包裹在 C
构造函数。这意味着我们可以定义:
typedBoolExpr :: C Bool
typedBoolExpr = C [| True && False |]
typedIntExpr :: C Int
typedIntExpr = C [| 2 + length "abc" |]
其中表达式的 ExpQ
表示使用基础表达式的类型进行注释。当然,没有什么能真正阻止我们写作:
badlyTypedBoolExpr :: C Bool
badlyTypedboolExpr = C [| 2 + length "abc" |]
实际表达式类型与类型注释不匹配,因此编译器不会强制我们类型注释的正确性,但如果我们在一些关键点上使用一点自律来强制自己的正确性点,类型系统可以帮助我们保持代码库的其余部分正确。
有了这个背景,我们可以解决您的问题:
unC (C x) = x
有什么意义?
没什么,真的。它只是一个字段访问器,让您可以从带类型注释的 C SomeType
值中提取 C
构造函数,以获取底层未类型化的 ExpQ
。它本来可以更简洁地写成:
newtype C a = C { unC :: ExpQ }
您发布的代码片段中似乎甚至没有使用 unC
。
为什么 C
后面跟着 t
和 a
in:
clift1 :: ExpQ -> C t -> C a
clift1
的目的是采用函数表达式的无类型表示和参数表达式的类型表示,return表示第一个参数应用的表达式的类型表示到第二个论点。例如:
foo = clift1 (varE (mkName "not")) typedBoolExpr
请注意,在此示例中,所需的类型注释是 Bool
,因为这是基础表达式 not (True && False)
的类型。但是,一般来说,将函数应用于参数的结果将是其他类型。因此,clift1
准备采用函数表达式的无类型表示(假设它实际上具有类型 t -> a
),将其应用于任何调用者指定类型的参数表达式的类型表示 t
,并生成任何调用者指定类型的应用程序表达式的结果类型化表示 a
.
由于未在任何地方指定函数表达式的类型,因此就 clift1
而言,t
和 a
类型是任意的。我们需要希望在使用 clift1
的地方,它以类型敏感的方式使用,例如通过将类型正确的类型签名附加到使用 clift1
:
的函数
myNot :: C Bool -> C Bool
myNot = clift1 (varE (mkName "not"))
g
是ExpQ
吗? f
和tx
是什么意思?
大多数 TH 处理发生在 Q
monad 中。它允许你做一些事情,比如在编译期间报告错误,生成唯一的名字,以及一堆其他的事情。 do
语法和 clift1
中所有这些额外变量的目的是在 Q
monad 中构造应用程序表达式。显然,clift1
的作者不仅对什么构成信息丰富的函数名称有糟糕的认识,而且还是由对 TH 不熟练的人编写的。可以更简洁地写成:
clift1' :: ExpQ -> C t -> C a
clift1' f (C x) = C (appE f x)
这清楚地表明它只是在 Q monad 中构造一个函数应用程序表达式,并使用 C
类型注释构造函数进行适当的包装和解包。
综上所述,clift1
在 Q
monad 中运行,以获取实际类型 [=49] 的函数的无类型表示(即类型 ExpQ
的值) =],将其应用于实际类型 t
的参数的类型化表示(即类型 C t
的值),并构造类型化表示(即类型 C a
的值]) 将函数应用于实际类型为 a
.
的参数
我正在看下面的 TH 代码,有几个问题用 --
标记:
newtype C a = C ExpQ
unC (C x) = x --what is the significance of this
clift1 :: ExpQ -> C t -> C a --why are the C's here followed by t and a
clift1 g (C x) = C $ do f <- g --is the g an ExpQ? What is the f here, what does it signify?
tx <- x --what is the tx here signify?
return $ AppE f tx
正如标题所暗示的,我特别不确定f
和tx
来自哪里以及它们在这里代表什么。我认为它们只是被赋值的变量,以便它们可以用作 AppE 的参数,这是正确的吗?但在这种情况下,为什么?为什么不简单地使用这些参数调用一个函数而不是整个 clift1 do 函数?
ExpQ
是一个 monadic 值(在 Q
monad 中),它给出了一些 Haskell 表达式的模板 Haskell 表示。底层表达式可以是任何类型,所以你可以有一个 ExpQ
代表一个布尔表达式,你可以使用 TH quotation:
{-# LANGUAGE TemplateHaskell #-}
import Language.Haskell.TH
boolExpr :: ExpQ
boolExpr = [| True && False |]
或直接从 TH 基元构建它:
-- almost the same as "boolExpr" above
boolExpr' :: ExpQ
boolExpr' = infixE (Just (conE (mkName "True")))
(varE (mkName "&&"))
(Just (conE (mkName "False")))
您还可以使用 ExpQ
表示整数表达式:
intExpr :: ExpQ
intExpr = [| 2 + length "abc" |]
请注意,boolExpr
和 intExpr
具有相同的类型 ExpQ
,即使它们表示具有不同类型的基础 Haskell 表达式(即 Bool
和 Int
)。这是因为模板 Haskell 提供类型 Haskell 表达式的 非类型化表示 。
使用 Haskell 表达式的 类型表示 会很有帮助,其中表示的表达式的类型在表示。一个简单的方法是为 ExpQ
类型定义一个 newtype
同义词,它采用所谓的“幻影”类型参数:
newtype C a = C ExpQ
这定义了一个新类型(或者实际上是整个类型集合,C Int
、C Bool
等),其值只是 ExpQ
值包裹在 C
构造函数。这意味着我们可以定义:
typedBoolExpr :: C Bool
typedBoolExpr = C [| True && False |]
typedIntExpr :: C Int
typedIntExpr = C [| 2 + length "abc" |]
其中表达式的 ExpQ
表示使用基础表达式的类型进行注释。当然,没有什么能真正阻止我们写作:
badlyTypedBoolExpr :: C Bool
badlyTypedboolExpr = C [| 2 + length "abc" |]
实际表达式类型与类型注释不匹配,因此编译器不会强制我们类型注释的正确性,但如果我们在一些关键点上使用一点自律来强制自己的正确性点,类型系统可以帮助我们保持代码库的其余部分正确。
有了这个背景,我们可以解决您的问题:
unC (C x) = x
有什么意义?没什么,真的。它只是一个字段访问器,让您可以从带类型注释的
C SomeType
值中提取C
构造函数,以获取底层未类型化的ExpQ
。它本来可以更简洁地写成:newtype C a = C { unC :: ExpQ }
您发布的代码片段中似乎甚至没有使用
unC
。为什么
C
后面跟着t
和a
in:clift1 :: ExpQ -> C t -> C a
clift1
的目的是采用函数表达式的无类型表示和参数表达式的类型表示,return表示第一个参数应用的表达式的类型表示到第二个论点。例如:foo = clift1 (varE (mkName "not")) typedBoolExpr
请注意,在此示例中,所需的类型注释是
Bool
,因为这是基础表达式not (True && False)
的类型。但是,一般来说,将函数应用于参数的结果将是其他类型。因此,clift1
准备采用函数表达式的无类型表示(假设它实际上具有类型t -> a
),将其应用于任何调用者指定类型的参数表达式的类型表示t
,并生成任何调用者指定类型的应用程序表达式的结果类型化表示a
.由于未在任何地方指定函数表达式的类型,因此就
的函数clift1
而言,t
和a
类型是任意的。我们需要希望在使用clift1
的地方,它以类型敏感的方式使用,例如通过将类型正确的类型签名附加到使用clift1
:myNot :: C Bool -> C Bool myNot = clift1 (varE (mkName "not"))
g
是ExpQ
吗?f
和tx
是什么意思?大多数 TH 处理发生在
Q
monad 中。它允许你做一些事情,比如在编译期间报告错误,生成唯一的名字,以及一堆其他的事情。do
语法和clift1
中所有这些额外变量的目的是在Q
monad 中构造应用程序表达式。显然,clift1
的作者不仅对什么构成信息丰富的函数名称有糟糕的认识,而且还是由对 TH 不熟练的人编写的。可以更简洁地写成:clift1' :: ExpQ -> C t -> C a clift1' f (C x) = C (appE f x)
这清楚地表明它只是在 Q monad 中构造一个函数应用程序表达式,并使用
C
类型注释构造函数进行适当的包装和解包。
综上所述,clift1
在 Q
monad 中运行,以获取实际类型 [=49] 的函数的无类型表示(即类型 ExpQ
的值) =],将其应用于实际类型 t
的参数的类型化表示(即类型 C t
的值),并构造类型化表示(即类型 C a
的值]) 将函数应用于实际类型为 a
.