如果我尝试使用Generic-lens软件包中的@符号访问类型的字段,GHC会抱怨启用DataKinds扩展名。您能用简单的英文解释一下generic-lens和DataKinds有什么关系吗?
谢谢
答案 0 :(得分:5)
DataKinds
是一个扩展,它允许将数据类型提升为类型级别。这恰好意味着一罐蠕虫(这是通往依赖类型的一步),所以我将尽力使这种解释集中于泛型透镜为何使用它(但请记住,我正在简化这里)。
首先,快速绕道。以下表达式的类型是什么?
mempty
mempty
来自Monoid
类。但是,与大多数方法相比,它不需要任何参数。那么Haskell如何知道将其实例化为哪种类型?以下所有条件都是正确的:
mempty == []
mempty == Sum 0
mempty == Any False
简而言之,Haskell(或称为GHC)推断表达式的类型,并使用该推断选择正确的实现。有时,推论不起作用。在下面的表达式中,例如:
print mempty
我们必须显式地指定类型,例如:
print (mempty :: [Int])
@
符号是用于应用通常会推断出的任何类型的语法。因此,在这种情况下,我们可以编写:
print (mempty @[Int])
但是,它与::
不同:@
符号专门填充了ghc试图猜测的第一个类型的孔。因此我们可以将其同样地应用于印刷品:
print @[Int] mempty
因此,您可以看到@
是我们将类型应用于表达式的一种方式。但是,它真正让我们做的是轻松地从 type 获取一个 value 。例如:
{-# LANGUAGE TypeApplications, AllowAmbiguousTypes #-}
class TypeName a where
name :: String
instance TypeName Int where
name = "Hello, I'm an Int!"
instance TypeName Bool where
name = "Bool!"
name @Int
换句话说,我们可以让类型级别的程序产生价值级别的结果。这就是通用镜头的用处。此软件包将其用于字段。如果您输入的是以下类型:
data Person
= Person
{ name :: String
, age :: Int
}
您可以生产两个镜头,一个叫_name
,另一个叫_age
(或类似物)。通用镜头的功能稍微更聪明:它具有一个像这样使用的功能(fieldLens
):
-- age lens
fieldLens @"age"
最后,这是我们需要的DataKinds
。传统上讲,在类型级别仅允许使用类型,这是@
符号所处理的。不过,以上内容是一个字符串:解除限制DataKinds
正是这样做的。
最后,从技术上讲,您不需要 DataKinds
来伪造上述行为。您可以再次使用类型。实际上,在DataKinds
之前,人们曾经做过以下事情:
data AgeField = DontConstructMe
fieldLens @AgeField