英国电信

促进专业软件开发中知识和创新的传播

为InfoQ撰写

话题

选择您的语言

InfoQ主页 访谈 Miles Sabin谈带Scala、Shapeless和Scala宏的依赖类型

Miles Sabin谈带Scala、Shapeless和Scala宏的依赖类型

书签
   

1 我们在伦敦参加2012年Scala Days,我和Miles Sabin坐在这里,那么Miles你是谁?

我是Miles Sabin,我是Undercore Consulting的创始合伙人之一,之前我是Chuusai,这是一家Scala Consulting公司,我从2008年开始经营。我曾多次负责Eclipse项目的Scala IDE,我相信自2004年以来我一直在以某种方式使用Scala。所以我有点像那些你偶尔会看到广告中宣传的拥有30年Java经验的人,我几乎是Scala的一员,不是EPFL之外的一些人,也不是Martin之外的几个人,他们使用Scala的时间比我长一点,但不是很多。

   

2 那么在Undercore,你的职责是什么,你做什么?

嗯,在某种程度上,作为Undercore产品的一部分,我正在继承我之前的业务。目前主要是培训和咨询,在培训中,我专门从事更高级的领域,尤其是Scala类型系统,这就是我要对人们说的非常有趣的事情,我想我能提供一个很好的切入点,让人们了解类型系统可以做的事情,这既有用又实用,但可能不像你预期的那样为人们所熟悉。

   

三。 所以你基本上是在训练人们理解类型系统,还是在教人们这些先进的想法?

我认为这两者兼而有之,所以既要理解类型可以为您做什么,也要理解它是如何在Scala类型系统的上下文中具体表现出来的。Scala类型系统具有一些不同寻常的创新功能;在Scala的类型系统中,你会在其他编程语言中找到很多东西,但它肯定有一些你在其他任何地方都找不到的特性,甚至在任何一种微弱的主流编程语言中也找不到。我认为Scala包含了一种特殊形式的依赖类型,我认为这非常重要,这是学术编程语言以外的任何地方都找不到的。

这听起来可能有点吓人,但我认为依赖类型带来的关键点是,它能够以编译器可以静态检查和验证的方式表达越来越多的程序属性,因此,除了简单形式的类型检查之外,您还希望任何现代的静态类型编程语言都能够强制执行,例如,String列表中的每个元素都是String,这是您希望能够用Java或C#表示的内容,或多或少,任何静态类型的编程语言,以及表达列表至少有一个成员这一事实的能力是一种更有趣的属性,并且能够以编译器可以静态验证的方式这样做,这样,您就可以在编译时预先知道这样的事情永远不会发生,而不是冒着运行时错误的风险,例如试图获取空列表的头部。

我认为这是下一代和下一代编程语言的一个极为重要的特性,这些语言以直接或间接的形式合并为依赖类型的某些方面。

   

4 那么依赖类型在Scala中是如何表达的,那么你指定它的例子是一个必须有三个元素的列表,你是如何做到的?

某种程度上间接地,Scala具有的依赖类型的形式,它以成员类型的形式出现,因此Scala中的嵌套类型与Java或C#中的不同,它不属于封闭类型,它属于封闭实例,因此您可以将嵌套类型视为它所属实例的函数,这样,在编程语言中就有了足够的素材来表达更有趣、更复杂的属性,这是一种实际的机制,你可以用它来编码列表中有特定数量元素的事实,这是编码,但可以这样做,对于一种使用依赖于这些属性的代码的最终用户来说,在所涉及的机制方面看不到太多,寻找如何实现这一点的示例的地方是OpenSource项目,我已经工作了一年多了,但它最近表现为一个图书馆,Github项目,Mailing List,“无形”。在过去的五六个月里,我想,这将是一个很好的规范化的地方,寻找依赖大小的集合类型的实现。

   

5 所以Shapeless基本上是一个创建集合或类型的项目?

这是《Shapeless》所涉及的内容之一,主要的标题是,我试图探索Scala的类型系统能够表达比我们熟悉的更普遍的多态性的方式,因此,它比OOP语言中的子类型多态性更普遍,或者比典型函数式编程语言中的参数多态性更一般,常用于它的术语是泛型编程或多类型编程。你可以在一些有趣的新的依赖类型编程语言(如Idris或Agda)中看到这类东西的表现,你也可以看到,例如,在Haskell中可以做很多事情,捕获一些相同类型的想法和一些非常通用的方法,这些方法也可以在Template Haskell之类的东西中捕获。所以Shapeless是对数据类型的形状进行抽象的,所以Shapeles的目的是传达这样一种思想,即我不打算处理任何一组特定的形状,我试图对形状进行抽象。

关于你在《无形状》中会发现什么样的东西,我应该强调一下,这是一个完全实验性的项目,它是一个图书馆,你可以使用它,Maven中心现在有二进制工件,但它只是一个经过精心设计的实验集合,尽可能地扩展Scala类型的系统。在你会发现的东西中,有一个集合的概念,它对大小进行编码,你会发现异构列表,例如列表,它能够以类型系统可以跟踪的方式捕获列表中每个元素的潜在不同类型的想法,在某种程度上,对该列表进行索引的操作将保留类型。

因此,如果我有一个包含Integer、String和Boolean的异构列表,如果我选择它的第二个元素,编译器就会知道它是String,但某些内容在编译时可以强制执行,而等效的单类型列表无法捕获,它必须找到所有元素的一些常见超类型,如果以这种方式索引到列表中,就会失去精度,因此这些东西被称为HLists,这有点像是在Haskell中指向它们起源的方向,H是表示异构的,而不是Haskell,但这些东西的最初设计来自于哈斯克尔的工作。所以Hlists;异质地图是另一个有趣的东西;使这些东西正常工作所涉及的通用技术是相关的,异类映射将允许您表达这样的想法-类似于每个人都熟悉的映射-但其中值的类型由键的类型决定。

例如,您可以指定一个异构映射,该映射将String键映射为Integer值,将Boolean键映射为Double值或沿着这些行的其他值,并且可以由编译器以静态方式强制执行,这样您就可以知道如果使用String键查找值,那么您肯定会得到Integer的值,同样,以一种永远不会出错的方式,这样您就可以避免进行任何类型的模式匹配或类型测试,最终得到一个可以使用的实际具体类型。

   

6 所以它是在键上索引的,键的类型不是在键的值上吗?

因此,它在两者上都建立了索引,因此键类型和值类型之间的关系完全在类型级别实施,但实际的单个值将被映射为单个值,单个键实例将被映射到单个值。

   

7 但在运行时,我可以说:“如果我有不同类型的键,键‘Foo’映射到String,而键‘Bar’映射到Integer”吗?

实际上,这是可以做到的,在某种程度上,你在那里所做的是,如果你愿意,你实际上是在编码一类记录的概念,在这里,你实际上使用字符串的方式类似于一类类型成员的名称,实际上,可以在Scala中对其进行编码,Shapeless中有一个可扩展的记录实验证明了这一点,而在Scala上实现这一点的一种方法是利用Scala的另一种类型系统功能,即单例类型,这种系统功能并不独特,但肯定有些不常见,所以Singleton类型是非常非常精确的类型,它是特定实例的类型。

因此,如果你可以把编码键想象成非常具体的值,那么你可以将该键的最具体类型映射到一个特定类型,这样你就可以映射,你现在不能严格地说“Foo”或“Bar”,但你可以做一些非常类似的事情,这样你可以有一个Foo键或Bar键的代表,然后将该映射映射到String或Integer或值端的其他值。这与您在Scala中设置记录或case类的方式完全相同,其中包含一个命名成员,该成员指定了特定时间的值。你可以问这类东西有什么用处,通常很明显,当你真正拥有这些作为语言第一级组成部分的可用工具时,你不会使用这类东西,但在有些情况下,你可能需要,你可能想保持静态类型安全,但是有一些更大的灵活性,这种灵活性只有通过利用代码生成或宏才能获得。例如,您可能希望以编译器可以捕获的方式将数据库模式或XML模式或沿着这些行的内容与记录进行匹配。

这是你可以使用这种技术来做的事情,我也不一定会建议人们立即投入到新的对象关系映射方案的生产中,但我认为这其中涉及的技术以及它们嵌入Scala的方式非常重要的是,我认为人们会根据这里涉及的各种想法来构建生产质量工具。我再次认为这是另一个,涉及的技术,这个特定的例子是在Scala中使用Singleton类型和在Scala中使用依赖类型的组合。你的工具箱里有一套非常强大的工具;用这种方法可以做很多事情。

   

8 你基本上解释了类型系统现在有多强大,但是你想在Scala中使用什么特性,或者没有?你现在最大的障碍是什么?

迈尔斯·萨宾(Miles Sabin):这并不完全是一个障碍,但它肯定会使Shapeless的使用不如其他方式实用。库的第一部分需要验证的属性是由编译器实现的,它们涉及使用Scala隐式值对属性进行编码,粗略地说,如果您试图证明异类列表的第n个元素是String,你必须递归地构造这种证明项,这个见证,编译器可以在编译时完成。因此,它将进行类型检查,以确保能够生成一个隐式对象,该对象验证只有在异构列表的第n个元素具有正确的类型时才能生成。然而,编译器在编译时这样做的计算成本非常高,因为它利用了Scala的类型推断和隐式搜索机制的工作方式。如果你把异类列表作为语言的第一类部分,你会以一种更有效的方式做不同的事情,这并不是最优的。

也许比这还要糟糕,如果你用一种语言来工作,这种语言本身就做了这种事情,比如Agda或Idris,你会认为这些证明词只存在于编译时,它们会被删除,这种情况下类型删除是一件非常非常好的事情,您不希望这些证明术语必须在编译过程中存活下来,并且在运行时仍然存在。它们目前仍然存在于运行时,因此没有很大的开销,但在生成这些基本上无用的运行时工件时会涉及到开销,这些工件之所以存在是因为它们需要在编译时存在。

如果你愿意的话,Scala宏将使编译器的正常编译过程有一个小小的转义通道,它们基本上是在编译时将这些证明词变为现实,这意味着应该可以完全消除这些东西的任何运行时残留物。所以我想可能是因为这个原因,还有更多的原因,我非常希望看到Scala中出现宏。有趣的是,宏,一些人把它们看作是一种从类型系统中解脱出来的方式,人们常说,你知道,当语言发现自己的类型系统表达能力不够时,它们就会发展出宏系统,我认为这是有道理的,但我认为在这种特殊情况下,有很多,我认为我们可以用Scala中的宏做很多有用的事情,但我实际上不属于这一类,我认为在Scala程序中使用宏作为一种擦除证明术语的机制的想法,我认为这确实非常有趣。是的,宏,这也是我要找的。

沃纳·舒斯特:似乎现在每个人都在谈论宏,所以它似乎是未来的一个有趣功能。

迈尔斯·萨宾(Miles Sabin):当然。

   

9 最后,我们的特殊Scala Days问题。迈尔斯·萨宾(Miles Sabin)将自己描述为一名蒙娜德人。

迈尔斯·萨宾(Miles Sabin):我想我会成为延续单子,因为我会继续做下去,一直做下去,直到我达到某种类型级别的涅盘和。。。

沃纳·舒斯特:。。。你变成了一个例外单子。

2012年7月13日

英国电信