对于uni分配,我们已获得一行Haskell代码,其中显示:
newtype TC a = TC ([Id] -> Either TypeError ([Id], a))
首先,TypeError
是需要我们实现的赋值语句,所以我不能在这里发布数据声明,但我的问题是,我如何阅读上面的代码?newtype TC
后面的a
是什么?我也不明白等号右边的TC
是如何被重用的。
我认为这里的a
是一个类型变量,因为newtype
的工作方式与数据类似。我不知道知道知道这一点对我的理解有什么帮助。
2条答案
按热度按时间7rtdyuoh1#
newtype TC
后面的a
是什么?新类型声明中的
a
在函数声明中的表达与
x
非常相似a
是一个类型参数,所以你可以使用TC
,例如TC Int
或TC Bool
,类似于你可以使用f
,例如f 1
或f "bla bla"
(取决于它的类型)。案例
TC Int
等效于以下备选项:我也不明白
TC
是如何被重用到等号的右边的。这在Haskell中有点令人困惑。实际上
TC
* 没有 * 被重用,而是声明了 * 两个单独的 * 实体,它们都被称为TC
。你也可以用不同的方式来调用它们:TC_T
是一个 * 类型构造函数 *。这是将出现在类型签名中的内容,即TC_T Int
或TC_T Bool
。TC_V
是一个 * 值构造函数 *。当生成TC_T Int
或TC_T Bool
类型的值时,可以使用它。例如,您可以编写以下代码:
使用原始版本时,其外观如下所示:
...但它仍然是两个独立的东西,在这里都被称为
TV
。Haskell中的大多数newtypes调用类型和值构造函数是一样的,但通常只有类型构造函数从模块中导出,而值构造函数作为实现细节留下。data
和newtype
的大部分内容是相同的。...与newtype版本的唯一区别是一个微妙的间接:如果是
data
,那么编译器会插入一个间接值,它允许更多的惰性,但在这种情况下,这样做几乎没有意义。在实践中,通常只有在需要 * 多个 * 构造函数和/或具有多个字段的构造函数时才使用
data
,而newtype
不支持这些构造函数。p8h8hvxi2#
与 * data * 相比,* newtype * 的好处是可以通过其基础类型进行派生:
我不得不交换元组,但由于有三个 * newtype *,所以这是可行的:
TC a
、StateT s m a
和ExceptT
。编译器为每个 * newtype * 生成一个示例,证明它与基础类型具有相同的运行时表示。这被称为 * 代表性相等 *,由
Coercible
证明。这使得
TC a
可以强制为StateT [Id] (Except TypeError) a
,因此我们可以使用基于coerce
的派生策略在它们之间携带示例(如GeneralizedNewtypeDeriving
或DerivingVia
)。GeneralizedNewtypeDeriving
仅适用于 * newtype *,但这并不意味着DerivingVia
仅适用于 * newtype *。您可以为 * data * 派生许多示例,例如通过Applicative
提升方法对TC
(Ap TC a
)进行提升。将其变为 * newtype * 确实简化了事情,
Applicative
可以通过状态转换器派生,因此我们不需要手动编写任何示例。TC
名称在代码中有两个用途("双关语")。我将它们分成两个不同的名称,分别是TC
和transformers。左边是型别建构函式:
右边是值构造函数,我添加了一个
Mk
以消除歧义:a :: Type
是型别建构函式的参数:如果没有类型参数,这些示例都不会进行类型检查,因为所有这些示例都需要一个"一元类型构造函数"(
Type -> Type
)作为参数。也就是说,由类型参数化的类型: