
不久前,Lua语言的作者耶路撒冷的罗伯托访问了我们的莫斯科办事处。 我们采访了他,在此期间,我们问了哈勃(Habr)读者一些问题。 最后,我们可以与您分享整个对话记录。
-让我们开始思考。 如果您要从头开始重新构建Lua,那么用此语言您将改变三件事?-哇! 艰难的问题。 语言的创建和发展背后有一个完整的故事。 这不是一个重大决定。 有些决定令我后悔,多年来我能够解决。 程序员一直抱怨它,因为兼容性遭到破坏。 我们已经做过几次了。 我只考虑一些小任务。
-全局默认值(默认为全局)? 您认为这是正确的方法吗?-也许吧。 但是对于动态语言来说,这条路非常困难。 也许您应该完全放弃“默认”的概念,但是使用变量将很困难。
例如,您必须以某种方式声明所有标准库。 您需要
one-liner
,
print(sin(x))
,然后需要声明“ print”和另一个“ sin”。 拥有如此简短的脚本的广告很奇怪。
在我看来,默认情况下,任何较大的内容都不应该具有任何内容。 默认本地不是解决方案,它根本不存在。 它仅用于分配,不用于使用。 我们分配一些东西,然后再次使用并分配,就会发生完全莫名其妙的错误。
缺省的全局性也许不是完美的,但是缺省的局部性绝对不是一个选择。 我认为是某种公告,也许是可选的……我们已经打算进行多次全球公告。 但是,如果我们盲目介绍我们所要求的所有内容,那么它就不会有任何好处。
(讽刺地)是的,我们将要引入一个全球公告,并补充说,第五,第十名,再拔出另一个,因此,我们意识到最终的解决方案不能满足大多数程序员的需求,因此我们不会添加要求的所有功能,因此我们什么都不做。 毕竟,严格模式是一种合理的折衷方案。
有一个问题:例如,我们经常使用模块内部的字段,然后又遇到了同样的问题。 这只是错误的一种非常特殊的情况,在一般解决方案中应考虑在内。 因此,如果您确实需要它,则最好使用静态类型的语言。
-对于小配置文件,默认全局性仍然很方便。“是的,是的,对于小的脚本之类的东西。”
-在这种情况下没有妥协吗?-总会有妥协。 在这种情况下,小脚本和实际程序之间会折衷,类似的事情。
-在这里,我们回到第一个大问题:如果有这样的机会,您将改变三件事? 在我看来,您对目前的状况非常满意,对吗?“好吧,我不会说这是一个大的变化,但是...变成大变化的一个不成功的决定之一就是表中没有零。” 对此我感到抱歉。 我做了这个骇客...您知道我在说什么吗? 在半年或一年前的C中,我发布了一个Lua版本,其中表中没有零。
-没有值?没错 我认为这在表中称为nil-即null。 我们进行了语法修改,以确保兼容性。
-为什么需要?“我真的坚信这是整个问题……我认为,如果我们可以在表中使用nil,那么数组中nil的大多数问题就会消失……更确切地说,问题不在于数组中的nil。” 他们告诉我数组不能为nil,因此应将数组与表分开。 真正的问题是我们不能在表中保持零! 因此,问题不在于我们如何表示数组,而在于表。 如果我们在表中可以包含nil,那么在没有其他所有内容的情况下,我们可以在数组中具有nil。 因此,对此我感到非常抱歉,许多人不了解如果Lua允许将零放在表中,情况可能会发生变化。
-我能告诉您一个有关Tarantool的故事吗? 我们有自己的null实现,它是指向null指针的CDATA。 如果您需要在内存中创建空片段以插入用于远程调用的定位参数等,我们将使用它。 但是通常这是一个问题,因为CDATA总是转换为true。 而零数组将解决很多问题。-是的,我知道。 这就是我要说的-它可以解决许多问题,但这会导致更大的兼容性问题。 我们没有勇气发布不兼容的版本,然后销毁社区并发布Lua 5,Lua 6等的其他文档。 但是也许有一天我们会发布这样的版本。 这些将是非常大的变化。 我认为应该从一开始就做到这一点,然后我们会谈论该语言的一个小变化,除了兼容性。 今天,它将破坏许多程序。
-除了兼容性问题,您还看到哪些缺点?-将需要两个新的原始函数。 类似于“删除键”,因为分配nil不会删除键,因此您将需要一个基本操作将其从表中实际删除。 您将需要“测试”以准确检查零和缺少数据之间的区别。
-您是否分析了这些创新对实际实施的影响?-是的,我们发布了这样的Lua版本。 正如我所说,这悄悄地破坏了代码。 有些使用
table.insert(f(x))
函数调用。 并有意这样做,以便如果该函数不想插入任何内容,则返回nil。 因此,代替单独检查,“我要嵌入吗?” 我调用
table.insert
,如果得到nil,那么我知道它不会被插入。 与其他任何语言一样,该错误已变成一种功能,人们开始使用它。 但是,如果您更改功能,它将破坏代码。
-空类型呢? 像零一样,只有虚无?“好吧,不,这是一场噩梦。” 您只是推迟解决问题。 如果输入一个“拐杖”,那么您将需要一个或更多。 这不是解决方案。 问题之一是nil已在语言的许多地方扎根。 这是一个典型的例子:我们说“避免在数组中使用nil,即空洞”。 然后,我们编写一个返回nil及其后的内容的函数,然后得到一个错误代码。 这样的构造本身已经暗示了nil是什么。例如,如果我要列出该函数返回的结果的列表,只需将它们全部捕获即可。
-为此,您有一个技巧:)“是的,但是您不需要使用黑客来解决如此简单而明显的任务。” 但是,库确实这样做了……我想了一下:也许库应该返回false而不是nil,尽管这是一个粗略的选择,但只能解决一小部分问题。 正如我所说,真正的问题是我们需要在表中添加零。 否则,我们不应该像我想的那样频繁使用nil。 一般来说,还不清楚。 因此,如果您创建了void,这些函数仍将返回nil,并且直到创建新类型并且这些函数将返回void而不是nil时,我们才能解决问题。
-使用void时,您可以明确地说必须将键存储在表中,键的值应为void。 而且nil可以像以前一样工作。“是的,这就是我的意思。” 库中的所有这些函数应返回void或nil。
“他们也可以返回零,为什么不呢?”-因为这样在某些情况下,我们将无法解决无法捕获函数返回的所有值的问题。
“但是我们没有第一把钥匙,只有第二把钥匙。”-不,没有秒,因为计算将不正确,并且阵列中会出现孔。
-所以您想说您需要一种伪造的元方法?-是的 我梦到这个:
{f(x)}
您需要截获
f(x)
返回的所有结果。 然后我可以说
%x
或
#x
,这样我就知道返回结果的数量。 这就是正常语言应如何工作。 因此,除非有一个非常严格的规则,即函数永不返回nil,否则创建一个void不会解决问题。 但是,为什么我们甚至需要零? 也许值得放弃。
-Roberto,Lua中对静态分析的支持会更大吗? 像Lua检查类固醇吗? 当然,我知道这并不能解决所有问题。 您说过,此功能将在6.0版中出现,对吧? 如果在5.x中将有一个强大的静态分析工具-如果在其中投入了工时,这会有帮助吗?“不,我相信一个真正强大的静态分析工具被称为……类型系统!” 如果您需要真正强大的工具,请使用静态类型的语言(例如Haskell)或某种具有依赖类型的语言。 这样,您肯定会拥有一个强大的分析工具。
“但是那时候我就没有Lua了。”-是的,Lua需要...
-不确定性? 我喜欢长颈鹿关于静态和动态类型的照片。-是的,这是我从演示文稿中获得的最后一张幻灯片。
罗伯托·耶路撒冷(Roberto Jerusalem)演讲“为什么要露娜(Lua Lua? (以及为什么不参加)“在Lua in Moscow 2019会议上。-要问下一个问题,让我们回到这张照片。 如果我理解正确,您会认为Lua是解决不太大任务的便捷工具。-不,我相信您可以解决一些大问题,但不涉及静态分析。 我全心全意地进行测试。 顺便说一句,我不同意覆盖率,我们不应该追逐覆盖率。我想说,我完全同意以下观点:覆盖率无法提供完整的测试,但是缺乏覆盖率根本无法进行测试。 我在斯德哥尔摩谈到测试室,您当时在那儿。 我开始使用[几个]错误进行测试-这是最奇怪的事情-其中一个众所周知,而其余的则完全未知。 Microsoft,C和C ++头文件中的某些内容完全损坏。 我搜索了网络,但没人担心它,甚至没有注意到它。
例如,有一个数学函数
modf() ,需要将其传递给double值的指针,因为它返回了两个double。 我们转换数字的整数或小数部分。 长期以来,此功能一直是标准库的一部分。 然后是C 99,浮点数现在需要此功能。 Microsoft头文件只是保存了此函数,并声明了另一个宏。 也就是说,第一个函数进行了隐式类型转换。 Double被转换为浮点数,然后double的指针被转换为浮点数的指针!
-这张图片有问题。-这是Visual C ++和Visual C 2007的头文件。如果使用任何参数一次调用此函数,然后检查结果,则除非是0,否则它将是错误的。其他任何值都不是true。 您永远不能使用此功能。 零覆盖。 然后,关于测试的讨论很多。只需调用一次函数并检查结果! 这个问题已经存在了很长时间,多年来一直没有困扰任何人。 苹果有一个非常著名的错误,
类似于 “
if… what… goto… ok
”。 有人在这里插入了另一个表达,一切都变得正常了。 然后,他们就是否需要规则,是否应在代码样式中使用花括号等问题争论不休。 没有人提到还有许多其他“如果”。 没有人做过...
-我记得还有一个安全问题。-是的 毕竟,他们只测试已确认的情况。 他们不会连续测试所有内容,因为所有内容都会得到确认。 这意味着在检查安全性的应用程序中,没有一个测试可以检查任何连接是否失败或应该拒绝哪些连接。 每个人都在争论是否需要括号...我们需要测试,至少需要测试! 毕竟,没有人甚至测试过我所说的“涂层”的含义。 这简直令人难以置信,人们甚至没有进行基本测试。 当然,要提供基本测试,运行所有代码行将非常困难。 人们甚至避免基本测试,因此覆盖范围很小。 我想敦促您注意您忘记的程序部分。 有点像提示如何稍微改善您的测试。
-您知道Tarantool中的测试范围吗? 83%! Lua的覆盖范围是什么?-大约99.6%。 您有几行代码? 一百万,几十万? 这是一个巨大的数目。 十万分之一的代码是一千行未经测试的代码。 您根本没有执行它们。 您的用户没有进行任何测试。
-也就是说,现在不使用大约17%的Tarantool函数?-您不太可能希望回到我们刚才讨论的内容……我认为动态语言(以及静态语言)的问题之一是人们没有测试他们的代码。 即使使用静态语言-不是像Haskell,而是像Coq-直到拥有验证系统,您都将其更改为该语言。 而且没有静态分析工具可以捕获这些错误,因此您需要进行测试。 如果有它们,则可以识别全局问题,名称中的错别字等各种错误。 您肯定需要测试。 有时很难调试,否则很简单-这取决于语言和错误类型。 但最重要的是,没有静态分析工具可以替代测试。 另一方面,它们不能保证没有错误,但是有了它们,我感到更加自信。
-我们有一个关于测试Lua模块的问题。 作为开发人员,我想测试一些以后可以使用的局部功能。 问题:您需要99%的覆盖率,但是API模块应该生成的功能情况数量远远少于其内部支持的功能数量。-对不起,为什么?
-有些功能不适用于公共接口。“她不应该在那里,只需删除此代码即可。”
-删除它?-是的,有时我会在Lua这样做。 涉及到某种代码,我无法到达这里,那里或那里,我认为这是不可能的,因此删除了代码。 很少,但是它确实发生了。 如果某些情况是不可能的,只需在注释中写出为什么这是不可能的。 如果您不能通过公共API进入该函数,则不应这样做。 必须使用错误的输入数据编写公共API,这对于测试很重要。
-删除代码是好的,它降低了复杂性。 复杂性更低-更高的稳定性和易于维护。 不要复杂化。-是的,在极限编程中有这样的规则。 如果无法测试,则不存在。
-您启发了哪些语言来创建Lua? 您喜欢什么范例,功能或部分语言?-我设计Lua的目的很狭窄,这不是一个学术项目。 因此,当您问我关于重新创建语言的问题时,我回答说它背后有一个完整的故事。 我并不是以“我将创建一种我喜欢或想要使用的语言,或者每个人都需要的语言”来开发Lua。 我有一个问题:这里我需要地质学家和工程师的配置语言,它必须小巧且具有简单的界面,以便他们可以使用它。 因此,API一直是该语言的组成部分。 那是任务。 至于灵感,那时候我熟悉大约十种不同的语言。
“我想知道您想在Lua中加入哪些语言。”-我从许多语言中借来了想法,所有适合解决我的问题的想法。 尽管很难说,Modula语言的语法影响最大,因为存在多种语言。 AWK采取了一些措施。 当然,从Scheme和Lisp出发……自从我开始编程以来,我对Lisp并不陌生。
-而且Lua中仍然没有宏!-是的,语法完全不同。 可能第一语言是Fortran ...不,我的第一语言是汇编语言,然后是Fortran。 我学习了,但从未使用过CLU。 在Smalltalk和SNOBOL上进行了大量编程。 也曾研究过,但并未使用Icon,这也是一种非常有趣的语言。 我从Pascal和C那里借了很多东西。当我创建Lua时,C ++对我来说已经太复杂了,这早于模板和其他东西。 我从1991年开始工作,并于1993年发布Lua。
-苏联解体,您开始创建Lua :)启动Lua时,您不喜欢分号和对象吗? 在我看来,它的语法类似于C,因为Lua已集成到其中。 但是...-我认为语法应该有所不同,因此您不会感到困惑,因为这是两种不同的语言。
这真的很有趣,并且与关于以1开头的数组的答案有关,您没有允许我在会议上表达它。 我的回答太长了。
当Lua发行时,世界就不同了。 并非所有语言都类似于C。仍然没有Java和JavaScript,Python是婴儿,并且没有超过1.0版。 因此,并非所有语言都类似于C;它的语法是许多语言中的一种。
数组也一样。 有趣的是,其中大多数都不知道。 以0和1开头的数组各有优点。
多亏了C语言,当今大多数流行语言都使用以0开头的数组。其创建者受到C语言的启发。而且很有趣的是C语言中没有索引。 , , . , — , (offset). , , , , .
, , . Java, JavaScript — . 0, . , « ».
— , , . -, , , - , , . , Lua ? ?— ?
— .— . , , . . ? , , . . .
— Lua C.— , , . , , … , , - . : , … .
, . , ? , - … ?
— .— . ? C , , … ?
— 16 ?— , .
— , , .— , . , , … , , . — , . , , . , … : ?
— C++ .— , C++ .
— ? ptrdiff_t
?—
ptrdiff_t
— (signed type). , , . ?
, diff , . . , ? 没办法 , , . 2 , .
, . , . diff , .
, ++.
— Lua ?— , C++, - , . , - ++, … .
— , ?— . , . , , . , .
— JVM?— , JVM. , … — , . JVM , .NET, . JVM , Lua. , . JVM, . JVM . Java-, 10 . , JVM , .
— JVM, , Mobile JVM?— , JVM. - Java, Java.
— , Go Oberon? Lua, ?— Oberon… … Go , runtime- Lua. Oberon , . , , . , , const Pascal Oberon. , .
.
, 1994- Oberon Self. Self? JIT- . Self , , . - , — ! — , - . , …
Oberon, , 10 Self, . Oberon , , .
, .
— Haskell?— Haskell, , Lua.
— Python, R Julia Lua?— , .
R . , .
Python , . , . , .
Python , , . , , - . API… , Python, . , , « ».
, -: , , , -. . , API , - . , . , , …
- , (pattern matching). , , , . , , .
: Perl. Lua, . , Python . , , . - .
— ?— Python. Perl : $1, $2, $3. Perl, …
— Python, , ( Tarantool).— , , , API, . Python , .
Julia, LuaJIT, , . , , . , . , , , . , , . : , - . , , - , .
Julia, : , , . , - . , double, []… . .
() «, , , , , . , ». , , .
. R, Python.
— Erlang?— . , . , , , - . Erlang , , . , .
, . , . ? , . .
— , Erlang , Python . Lua?— , . Lua , , Lua , .
— ?— Forth, .
— Lua?— , . , . - , . Lua, Lua, .
Java. Java, ? 不行 (reflection)? 不行 ? Java, , Java. , , Java, .
, Lua, … , , FFI.
— Lua?— , .
— Lua ? ?— , . , Haskell. , , … Lua, , , , , .
— , Lua.— , , . . , .
— , . Lua ?— , , …
— « »? , ?— , . , , , . , .
— Lua?— . , , LaTex DocBook. , … LaTex, . @, . Gsub , , - - . , , , .
— LaTeX?— LaTeX? -, , . , , LaTex. , inline-. /, . — . , , , . , . , .
— LaTeX?— , . , , . , 3+1, . , , . , . , , . , 1 «and», . . , .
— ?— ,
git .
2html . HTML… , . , , . , , . , TeX. TeX- TeX.
— ?— , . , TeX. , . DocBook, . , .
— 2html DocBook?— , DocBook.
— , , !— .
- ,
Lua Mailing List .