通用镜头与DataKinds GHC扩展之间的关系

时间:2019-05-11 08:31:44

标签: haskell

如果我尝试使用Generic-lens软件包中的@符号访问类型的字段,GHC会抱怨启用DataKinds扩展名。您能用简单的英文解释一下generic-lens和DataKinds有什么关系吗?

谢谢

1 个答案:

答案 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