有很多方法可以向计算机发出指令:电气工程师可能会编写 MATLAB 程序;数据库管理员可能会编写 SQL 脚本;硬件工程师可能会使用 Verilog 编写代码;会计师可能会编写带有嵌入式公式的电子表格。除了这些示例中使用的语言不同之外,在形式和习语方面也存在重要差异。每种语言都针对手头的工作进行了定制,并且每种语言都以程序员熟悉且高效的形式构建计算请求(尽管会计师可能不认为自己是程序员)。简而言之,这些示例中的每一个都使用了 DSL(领域特定语言)。
DSL 是一种专用语言,旨在封装特定领域中可能的计算。在 MATLAB、SQL、Verilog 和电子表格的示例中,领域分别是科学建模、数据库查询和更新、硬件电路和财务计算。具体考虑 SQL,它所做的任何事情都可以在 Java 或 C 或任何其他通用编程语言中完成。SQL 只是将与数据库交互所需的动作捆绑到一个可用且高效的包中,并且该语言成为与数据库引擎通信请求的接口。
DSL 有两种基本类型。第一种是一流语言,如图 1(1) 所示,它有自己的编译器或解释器,并且通常在其自己的生态系统中使用。到目前为止提到的所有示例都属于这一类。SQL DSL 与 Java(例如)之间的主要区别在于范围和焦点,尽管有时 DSL 会发展得像通用语言一样庞大。
另一种 DSL 类别是嵌入在宿主语言中的语言,如图 1(2) 所示。此类语言可以具有其自身语言的外观和感觉,但它们利用宿主语言现有的生态系统和初始语义。本文关注的是第二类 DSL。
EDSL(嵌入式 DSL)是一种语言内部的语言。Haskell17,首屈一指的纯函数式编程语言,是 EDSL 的绝佳宿主,因为它具有灵活的重载、强大的类型系统和惰性语义。本节提供了 Haskell 的简洁介绍,足以使本文自成一体。它是作者在 2011 年于可重构系统和算法工程国际会议上给出的 Haskell 入门教程的扩展版本。10
Haskell 完全是关于类型的。Haskell 中的类型,就像其他语言中的类型一样,是对结构值的约束性摘要。例如,在 Haskell 中Bool是值的类型True和False; Int是机器字大小的类型;Double是双精度 IEEE 浮点值的类型;此列表以与 C、C++、Java 和其他传统语言相同的方式继续下去。Haskell 中的所有这些类型名称都以大写字母开头。
在这些基本类型之上,Haskell 有两种用于表达复合类型的语法形式。首先,可以使用元组语法、括号内用逗号分隔的类型来编写对、三元组和更大的结构。因此,(Int,Bool)是一个既有Int又有Bool组件的结构。其次,列表有一个语法快捷方式,使用方括号。因此,[Int]是一个Int.
列表。Haskell 还有其他容器类型。一个可能包含一个Int的容器具有类型Maybe Int,读作Int 的 Maybe。这些容器名称也以大写字母开头。
类型可以嵌套到任何深度。例如,你可以有一个[(Maybe (Int,Bool))],读作(Int 和 Bool) 的 Maybe 列表。
多态值使用小写字母表示,并且与 C 中的void*指针和 Java 泛型工具中的多态参数起着类似的作用。这些多态值可以具有对其表达的约束,使用 Haskell 等效于对象层次结构的东西。
最后,函数使用从参数类型到结果类型的箭头编写。因此,在 Haskell 中,一个接受列表并返回列表的函数被写为[a] -> [a]。
这是一个 Haskell 函数的例子
sort :: (Ord a) => [a] -> [a]
sort [] = []
sort (x:xs) = sort before ++ [x] ++ sort after
where
before = filter (<= x) xs
after = filter (> x) xs
此函数使用快速排序的变体对列表进行排序,其中枢轴是列表的第一个元素
* 第一行是sort的类型。这是∀a,使得a可以排序(允许像<=<=a这样的比较);该函数接受并返回此类
的列表。
* 第二行表示空列表已经排序。* 剩余行声明,可以通过获取列表的第一个元素和其余元素(分别称为和x和
xs),对该枢轴之前和之后的值进行排序,并将这些中间值连接在一起来对非空列表进行排序。* 最后,可以使用where和语法命名中间值;在本例中,是.
beforeMaybe和after和的值。:
Haskell 是一种简洁而直接的语言。Haskell 中的结构使用类型表示,构造和解构,但从不更新。例如,
Maybe
类型可以使用两个构造函数定义,
afterNothingMaybe和的值。JustMaybedata Maybe a where
Nothing :: Maybe aMaybe Just :: a -> Maybe a
Nothing
是一个
Maybe
的任何东西;Just
,带有一个参数,是一个MaybeMaybeMaybe,其类型与参数的类型相同。这些构造函数可用于构造和解构结构,但永远不会进行任何更新;Haskell 中的所有结构都是不可变的。的值。可以使用基于类的重载系统为特定类型赋予额外的能力,例如相等性和比较。
Maybe类型,例如,可以使用实例赋予测试相等性的能力instance Eq a => Eq (Maybe a) where
Just a == Just b = a == b
Nothing == Nothing = True
_ == _ = False
这表明,对于任何可以测试相等性的类型,你也可以检查
Maybe a
的相同类型。你使用Maybe上的模式匹配来分解类型,例如,可以使用实例赋予测试相等性的能力Maybe类型,例如,可以使用实例赋予测试相等性的能力,以检查内部值。类型,例如,可以使用实例赋予测试相等性的能力在 Haskell 中,诸如写入屏幕或读取键盘之类的副作用使用类型,例如,可以使用实例赋予测试相等性的能力do
main :: IO ()
putStrLn "Hello"
xs <- getLine
print xs
在此示例中,名为main的值使用maindo符号来描述与用户的交互。实际上,do符号将其捕获为名为monad的结构;纯洁性并未受到损害。关于:
do
符号和 monad 如何在 Haskell 等纯语言中提供有效接口的更多详细信息是可用的。18 对于本文的目的,
do符号将其捕获为名为monad的结构;纯洁性并未受到损害。关于符号是一种提供看起来像交互的语法和结构的方式。有很多关于 Haskell 的教程;Haskell 网站 (http://haskell.org) 是进一步阅读的良好起点。符号将其捕获为名为monad的结构;纯洁性并未受到损害。关于嵌入式 DSL符号将其捕获为名为monad的结构;纯洁性并未受到损害。关于EDSL 是宿主语言中的一个库,它具有其自身语言的外观、感觉和语义,并针对特定问题领域进行了定制。通过重用宿主语言的工具和设施,EDSL 大大降低了开发和维护 DSL 的成本。受益于 Haskell 简洁的语法,Haskell 社区——以及一般的函数式编程社区——采纳了 EDSL 的思想,并开发了大量 DSL,为充分理解的系统提供更高级别的接口和抽象。接下来是 EDSL 的两个示例:一个用于自动生成软件测试的测试用例;第二个用于指定硬件电路行为。
示例 EDSL:QuickCheck 属性考虑编写测试用例的挑战——或者更具体地说,编写测试用例需要满足的属性.
-- 反转列表的反转是列表本身
prop_reverse_twice (xs :: [Int]) = reverse (reverse xs) == xs
在此示例中,
prop_reverse_twice
[0]
[2,-2]
是一个常规的 Haskell 函数,它接受一个 Int 列表并返回一个布尔值,该布尔值基于所提议内容的有效性——具体来说,两个反转相互抵消。这是巧妙的部分
prop_reverse_twice
也是一个领域特定的语句,因此可以被认为是 Haskell 内部的子语言。这种使用函数(在本例中,名称以prop_
为前缀的函数,接受多个类型化参数并返回条件)的风格是一种小型语言。在 Haskell 中编写的属性也是属性的 EDSL,称为 QuickCheck。4 这个 EDSL 可以使用也称为
quickCheck
的函数运行
Prelude Test.QuickCheck> quickCheck prop_reverse_twice
+++ OK, passed 100 tests.
通过运行quickCheck prop_reverse_twice
和这个明确且特定的属性,EDSL 在 Haskell 内部执行。quickCheck
函数为该属性生成 100 个测试用例,并即时执行它们。如果它们都成立,则系统会打印一条消息反映这一点。测试用例是使用类型类系统生成的——QuickCheck 赋予特定类型测试用例生成的能力——并且quickCheck
函数使用它来生成随机测试。作为不正确属性的示例,考虑以下reverse的属性prop_reverse xs ys = reverse xs ++ reverse ys == reverse (xs ++ ys)
这表明,两个不同列表的反转与两个列表连接在一起的反转相同,但此属性是错误的。
Prelude Test.QuickCheck> quickCheck prop_reverse
Falsifiable, after 5 tests
事实证明,这种迷你语言在实践中非常有用。尽管 Haskell 的使用方式很简单,但 QuickCheck EDSL 提供了一种思考和直接表达属性的方式。它具有额外的功能,包括生成随机函数参数、控制随机测试用例的分布以及声明属性的前提条件的能力。从这个 DSL 开始,已经构建了许多其他这些思想的实现。甚至有一家瑞典公司 QuviQ,销售用于并发编程语言 Erlang 的 QuickCheck。
1 : 1 : 1 : 2 : 2 : 2 : 3 : 3 : 3 : ...
示例 EDSL:堪萨斯 Lava再举一个例子,考虑描述硬件。硬件描述语言和函数式语言长期以来一直享有富有成效的合作伙伴关系。Lava 是赋予一类 Haskell DSL 的名称,这些 DSL 实现了基于函数的硬件描述语言 Ruby 的版本。12,13 不要与同名的现代编程语言 Ruby 混淆,Ruby 基于关系,而关系又受到μFP 中的开创性工作的启发。21堪萨斯 Lava11 是一个 Haskell 托管的 DSL,它遵循 Lava 的研究路线。它是一种用于表达门级电路的语言。Haskell 抽象允许程序员在稍微更高的抽象级别上工作,其中模型是通过同步流通信的递归组件模型。堪萨斯 Lava 已用于生成用于遥测解码器的高性能电路,尽管使用的模型是通用的。作为堪萨斯 Lava 的一个例子,考虑counter :: (Rep a, Num a) => Signal Bool -> Signal Bool -> Signal a
counter restart inc = loop
where reg = register 0 loop
reg' = mux2 restart (0,reg)
loop = mux2 inc (reg' + 1, reg')
此电路连接了两个多路复用器 (mux2)、一个加法器和一个register,以提供一个电路,该电路计算信号
inc
上时钟脉冲的数量。该电路接受两个时钟信号,并返回一个使用相同时钟显式运行的时钟信号,因为它们共享相同的类型。算术的使用被低估了,但只是简单地使用(通过重载)加法的标准语法;
0 : 1 : 4 : 9 : 16 : 25 : 36 : 49 : 64 : 81 : 100 : 121 : 144 : 169 : 196 : 225 : 0 : ...
Num
约束允许这样做。图 2 说明了为此描述设计的电路。
GHCi> toSeq (cycle [True,False,False])quickCheck
True : False : False : True : False : False : True : False : False : ...quickCheck
GHCi> counter low (toSeq (cycle [True,False,False]))
除了基本信号类型之外,你还可以构建直接对 Haskell 函数进行操作的电路,前提是函数的域是有限的。
Rep
能力用于表示你可以枚举类型中所有可能的表示值,从而给出
函数
funMap :: (Rep a, Rep b) => (a -> Maybe b) -> Signal a -> Signal b
生成的电路是使用 ROM 实现的,你可以直接根据 Haskell 函数和数据结构生成控制逻辑。例如,考虑一个存储值平方的小型 ROM
squareROM :: (Num a, Rep a) => Signal a -> Signal a
squareROM = funMap (\ x -> return (x * x))
通过这种方式,可以直接将 Haskell 函数提升到
Signal
世界中。请注意,的属性squareROM
函数没有具体说明大小,而是完全通用的,仅要求参数流的类型可以表示为数字。
时钟平方 ROM 现在可以在特定类型中使用。例如,在八位时,你可以生成以下内容
GHCi> squareROM (toSeq [0,1..] :: Signal Word8)
这种级别的电路规范已在许多 Lava 和类 Lava 语言中得到有效应用。一个值得注意的例子是 Hawk15,一种类 Lava EDSL,用于指定 Pentium Pro 的整个微架构,包括超标量设计和寄存器旁路功能。
现在,如果 DSL 作为库设计的习语如此强大,那么为什么它们没有取代一切?作为表达可以模拟的事物的一种手段,EDSL 是一个非常宝贵的设计模式;但是,并非所有事物都是模拟。如果你想使用 EDSL 来表达你想在其他地方运行而不是在 Haskell 系统内部运行的东西怎么办?Lava 可以用于生成在 FPGA(现场可编程门阵列)上运行的电路吗?EDSL 可以用于为嵌入式处理器或 GPU 生成代码吗?这种合成外部解决方案的能力将非常有用。EDSL 习语可以扩展到这样做,但存在重大警告。本文的其余部分是关于如何从 EDSL 内部捕获和外包工作;这种能力可以用于什么;以及有哪些限制。
深度嵌入式领域特定语言
EDSL 仅仅是一种思考提供的函数库的方式,通常称为组合器,因为它们将它们的参数组合成 DSL 内部的项。在之前的 Lava 示例中,
register
组合器接受一个初始值和一个传入的值流,并提供新的流,延迟一个周期,初始值占据初始周期。至关重要的是,register是可组合的;它组合 DSL 的较小部分以构成更大的解决方案。如果 DSL 在设计中仔细遵循这种可组合性,则可以实现一种重要的替代实现方式。
最常见的 EDSL 风格是使用所谓的浅层嵌入,如图 1(2a) 所示,其中值是直接计算的。浅层 EDSL 中计算的结果是一个值。到目前为止的所有示例都是浅层的。然而,还有另一类 EDSL:特别是那些使用深度嵌入来构建抽象语法树的 EDSL,如图 1(2b) 所示。深度嵌入式 DSL(深度 EDSL)中计算的结果是一个结构,而不是一个值,并且该结构可以用于计算值或在评估之前进行交叉编译。7 这种深度 EDSL 在设计和要求上都严格遵循可组合性原则。
从历史上看,EDSL 一直是浅层的——仅仅是一种为库构建 API 的方式。然而,深度 EDSL 具有暂存代码的能力——也就是说,执行一个程序可以生成另一个程序,很像众所周知的 yacc DSL,但这以显着限制 DSL 的哪些形式可以生成有效输出为代价。越来越多的深度 EDSL,以及围绕其形式和局限性的研究。统一的主题是深度 EDSL 可以是务实的、高效的和有用的。
本节研究了深度 EDSL 与浅层 EDSL 相比的基本结构,并研究了改进深度 EDSL 的实用性的三个实用技巧。
构建深度 EDSL
深度嵌入式 DSL 公开了自己的组合和结构。深度 DSL 不是使用直接对值进行操作的函数(浅层 DSL),而是构建一个结构,然后允许某个辅助代理提供对该结构的解释。为了使这个想法具体化,考虑一个用于算术的 DSL,带有加法、减法、乘法和常量。对于浅层嵌入,运行此 DSL 是微不足道的;你只需使用内置的算术即可。深度嵌入是事情变得有趣的地方。考虑我们算术的数据类型
data Expr where
Lit :: Integer -> ExprregisterAdd :: Expr -> Expr -> ExprregisterSub :: Expr -> Expr -> Expr
Mul :: Expr -> Expr -> Expr
现在重载算术以使用此 E 数据类型;在 Haskell 中,
Num
是整数算术的重载
instance Num Expr where fromInteger n = Lit n e1 + e2 = Add e1 e2 fromInteger n = Lit n e1 - e2 = Sub e1 e2 fromInteger n = Lit n e1 * e2 = Mul e1 e2通过构建 E 类型的表达式,你可以观察计算的结构:
funMap :: (Rep a, Rep b) => (a -> Maybe b) -> Signal a -> Signal b
GHCi> 1 + 2 * 3 :: Expr
Add (Lit 1) (Mul (Lit 2) (Lit 3))
这很深刻,它是使深度嵌入工作的关键思想。你可以编写一个表达式并提取一个做什么的树,而不是直接结果。对于深度嵌入,通常还会编写一个
run
函数,该函数计算捕获的计算的结果
run :: Expr -> Integerrun (Lit n)= n
run (Add a b) = run a + run b
run (Sub a b) = run a - run b
run (Mul a b) = run a * run b
图 3 说明了浅层和深度 DSL 之间的差异,以及深度嵌入与特定
run
函数如何给出相同的结果。对于深度嵌入式 DSL,
run
函数恢复了浅层嵌入的能力,但另一个函数采用嵌入式结构并以某种创造性的方式使用它。为了使深度 DSL 实用,DSL 领域中有两个额外的技巧几乎总是被使用。第一个技巧允许通过虚拟参数捕获函数。第二个技巧可以通过某种形式的可观察共享来观察循环。:
如何从函数中提取深度嵌入
用构造函数表达函数调用并构建表达式树是有用的,但就其本身而言,这只是一个噱头。然而,通过仔细的构造,你还可以直接从深度嵌入中捕获函数定义以及其他语法结构。正是在这一点上,捕获代码,然后使用捕获的代码在不同的目标上执行代码的想法成为可能。考虑一个简单的函数,将 1 加到其参数
f :: Expr -> Expr为了使深度 DSL 实用,DSL 领域中有两个额外的技巧几乎总是被使用。第一个技巧允许通过虚拟参数捕获函数。第二个技巧可以通过某种形式的可观察共享来观察循环。f x = x + 1
这是一个在新类型
Expr
上操作并返回新
的函数。你如何捕获此函数?诀窍是发明一个唯一的
Exprmux2:
并将其作为(虚拟)参数传递给
f
Lit :: Integer -> Expr
Add :: Expr -> Expr -> Expr
Sub :: Expr -> Expr -> Expr
Mul :: Expr -> Expr -> Expr
Var :: String -> Expr -- 新构造函数
你现在可以直接运行该函数,并在深度嵌入中看到结果,或者传入
Var
参数并查看实际函数
-- 仅运行函数
GHCi> f 4
Add (Lit 4) (Lit 1)-- 具体化函数,使用我们唯一的 Var。GHCi> f (Var "x")
Add (Var "x") (Lit 1) -- 函数的具体化版本
这真是太棒了!你使用虚拟参数(称为原型参数)运行了一个函数,并提取了函数的主体。
这个想法可以扩展到多参数函数。考虑-- 具体化函数,使用我们唯一的 Var。gg :: Expr -> Expr -> Exprg x y = x * x + y + 2
两个原型参数到-- 具体化函数,使用我们唯一的 Var。g
将捕获该函数
GHCi> g (Var "x") (Var "y")
Add (Add (Mul (Var "x") (Var "x")) (Var "y")) (Lit 2)
这种设计模式有很多用途。一个例子是将表面纹理指定为函数;可以将这些纹理导出到在 GPU 上可执行的代码中,同时提升用于编写纹理的抽象,并加快相同操作的实现速度。这里没有任何特定于 Haskell 甚至函数式语言的东西。事实上,相同的思想已在 Java 中用于 VHDL(超高速集成电路硬件描述语言)生成器。2 Haskell 凭借其强大的抽象能力,使深度 DSL 几乎感觉像是直接的浅层嵌入。
如何发现循环
Lava 程序被编写为递归绑定的方程。直接构建 Lava 的深度嵌入的尝试将导致结构无限循环。为了说明挑战,让我们构建 Lava 的深度嵌入,看看哪里出了问题,并使用称为可观察共享的技术来修复它。
首先,Lava 语言需要一个结构。我们定义了之前使用的函数,但为它们提供了一个深度嵌入,称为
data Signal a where
Register :: a -> Signal a -> Signal a
Mux2 :: Signal Bool -> (Signal a,Signal a) -> Signal a
Lit :: a -> Signal a
Var :: String -> Signal a -- Var 技巧quickCheck
instance Num a => Num (Signal a) where
a + b = Add a b
mux2 :: Signal Bool -> (Signal a,Signal a) -> Signal a
mux2 c (a,b) = Mux2 c (a,b)
register :: a -> Signal a -> Signal a
register d s = Register d s
现在,当尝试提取
counter
时,事情变得非常糟糕类型,例如,可以使用实例赋予测试相等性的能力GHCi> counter (Var "restart") (Var "inc")类型,例如,可以使用实例赋予测试相等性的能力Mux2 (Var "inc") (Add (Mux2 (Var "restart") (Lit 0,Register 0 (Mux2 (Var "inc") ...
输出树是无限的。发生的事情是,在尝试具体化函数时,或者更具体地说,quickCheck
loop
的主体正在循环。在这一点上,EDSL 社区陷入了困境。人们努力使用 monad 结构,其中循环使用
do-notation
深度 DSL 是一种值级别的方式来提取表达式,但也存在其他方式。准引用是一种提取表达式的机制,但在语法层面。Haskell 自带一个名为 Template Haskell20 的广泛模板系统,该系统通常用于 DSL。人们对这些解决方案感到一丝不安;然而,在很大程度上,C 预处理器也被使用,即使它被认为不够优雅。主要问题是 Haskell 的语法非常庞大,由大约 100 个语法术语组成。基于表达式的解决方案,例如深度嵌入,可以避免重写前端转换的需要。准引用有一个重要的优势:具体来说,它可以处理控制流和值的解构。也许深度 DSL 的未来是表达式生成和准引用之间的一种混合,结合两种系统的优点。
本文基于美国国家科学基金会在 Grant No. CCF-1117569 下资助的工作,最初于 2013 年 11 月在苏格兰信息学与计算机科学联盟访问学者项目下作为大师班讲授。堪萨斯 Lava 示例和描述改编自作者早期撰写的一篇关于 Lava 的文章。10
1. Axelsson, E., Claessen, K., Sheeran, M., Svenningsson, J., Engdal, D., Persson, A. 2011. Feldspar 的设计与实现:一种用于数字信号处理的嵌入式语言。载于第 22 届函数式语言实现与应用国际会议论文集。Springer-Verlag: 121-136.
2. Bellows, P., Hutchings, B. 1998. JHDL—用于可重构系统的 HDL。年度 IEEE 现场可编程定制计算机器研讨会。
3. Bracker, J., Gill, A. 2014. Sunroof:用于生成 JavaScript 的单子 DSL。声明式语言的实际方面。Matthew Flatt 和 Hai-Feng Guo 编辑。第 8324 卷,计算机科学讲义:65-80。Springer International Publishing.
4. Claessen, K., Hughes, J. 2000. Quickcheck:用于 Haskell 程序随机测试的轻量级工具。载于第五届 SIGPLAN 函数式编程国际会议论文集:268-279.
5. Claessen, K., Sands, D. 1999. 用于函数式电路描述的可观察共享。载于第五届亚洲计算机科学会议论文集,计算机科学讲义。Springer Verlag.
6. Elliott, C. 布尔包;hackage.haskell.org/package/Boolean.
7. Elliott, C., Finne, S., de Moor, O. 2003. 编译嵌入式语言。函数式编程杂志 13(2).
8. Erkök, L., Launchbury, J. 2000. 递归单子绑定。载于第五届 SIGPLAN 函数式编程国际会议论文集:174-185.
9. Gill, A. 2009. Haskell 中类型安全的可观察共享。载于第二届 SIGPLAN Haskell 研讨会论文集:117-128.
10. Gill, A. 2011. 使用堪萨斯 Lava 的声明式 FPGA 电路综合。可重构系统与算法工程国际会议。
11. Gill, A., Bull, T., Farmer, A., Kimmell, G., Komp, E. 2013. 用于硬件仿真和综合的类型和关联类型族:堪萨斯 Lava 的内部和外部。高阶和符号计算:1-20.
12. Hutton, G. 1993. Ruby 解释器。研究报告 72,查尔姆斯理工大学。
13. Jones, G., Sheeran, M. 1990. Ruby 中的电路设计。VLSI 设计的形式化方法。Jorgen Staunstrup 编辑。Elsevier Science Publications.
14. Mainland, G., Morrisett, G. 2010. Nikola:在 Haskell 中嵌入编译的 GPU 函数。载于第三届 SIGPLAN Haskell 研讨会论文集:67-78.
15. Matthews, J., Cook, B., Launchbury, J. 1998. Hawk 中的微处理器规范。载于国际计算机语言会议论文集:90-101.
16. Persson, A., Axelsson, E., Svenningsson, J. 2012. 用于嵌入式语言的通用单子构造。载于函数式语言的实现与应用:85-99。Springer.
17. Peyton Jones, S. L., ed. 2003. Haskell 98 语言和库—修订报告。英国剑桥:剑桥大学出版社。
18. Peyton Jones, S. L., Wadler, P. 1993. 命令式函数式编程。载于第 20 届 SIGPLAN-SIGACT 编程语言原理研讨会论文集:71-84.
19. Sculthorpe, N., Bracker, J., Giorgidze, G., Gill, A. 2013. 受约束的单子问题。载于第 18 届 SIGPLAN 函数式编程国际会议论文集:287-298.
20. Tim Sheard 和 Simon Peyton Jones。Haskell 的模板元编程。载于 Manuel M. T. Chakravarty 编辑,《 SIGPLAN Haskell Workshop 02》,第 1-16 页。 Press,2002 年 10 月。
21. Sheeran, M. 1984. μFP,一种用于 VLSI 设计的语言。载于 LISP 和函数式编程研讨会论文集:104-112.
22. Svenningsson, J., Svensson, B. J. 2013. 单子嵌入式语言的简单且可组合的物化。载于函数式编程国际会议论文集:299-304.
喜欢它,讨厌它?请告诉我们
Andy Gill ([email protected]) 是堪萨斯大学电气工程与计算机科学系的助理教授。他的研究兴趣包括优化、语言设计、调试和可靠性。他的研究的长期目标是为工程师和从业人员提供编写清晰且高水平可执行规范的机会,这些规范可以实际编译成高效的实现。
© 2014 1542-7730/14/0400 $10.00
最初发表于 Queue vol. 12, no. 4—
在 数字图书馆 中评论本文
Matt Godbolt - C++ 编译器中的优化
在向编译器提供更多信息方面需要权衡:这可能会使编译速度变慢。链接时优化等技术可以为您提供两全其美的效果。编译器中的优化不断改进,即将到来的间接调用和虚函数分派方面的改进可能很快就会带来更快的多态性。
Ulan Degenbaev, Michael Lippautz, Hannes Payer - 作为合资企业的垃圾回收
跨组件跟踪是解决跨组件边界引用循环问题的一种方法。只要组件可以形成具有跨 API 边界的非平凡所有权的任意对象图,就会出现此问题。CCT 的增量版本在 V8 和 Blink 中实现,从而能够以安全的方式有效且高效地回收内存。
David Chisnall - C 并非低级语言
在最近的 Meltdown 和 Spectre 漏洞之后,值得花一些时间来研究根本原因。这两种漏洞都涉及处理器推测性地执行超出某种访问检查的指令,并允许攻击者通过侧信道观察结果。导致这些漏洞以及其他几个漏洞的功能被添加到 C 程序员身上,让他们继续相信他们正在使用低级语言进行编程,但这在几十年内一直不是这种情况。
Tobias Lauinger, Abdelberi Chaabane, Christo Wilson - 你不应该依赖我
大多数网站都使用 JavaScript 库,其中许多库已知存在漏洞。了解问题的范围以及包含库的许多意外方式只是改进情况的第一步。这里的目标是本文中包含的信息将有助于为社区提供更好的工具、开发实践和教育工作。