Haskell EDSL:s中的本原派生运算和组合子
我最近考试不及格,主要是因为EDSL问题。我没有掌握这些概念,所以我想这就是我失败的原因。我想老师的解释对我来说太抽象了,我不知道是否有人能解释得更清楚 我想知道是否有人能简单地解释一下EDSL的组成部分,以及它们的特征。在我们的课程中,我们经历了DSL的浅层和深层嵌入,并研究了DSL的以下构建块:Haskell EDSL:s中的本原派生运算和组合子,haskell,dsl,Haskell,Dsl,我最近考试不及格,主要是因为EDSL问题。我没有掌握这些概念,所以我想这就是我失败的原因。我想老师的解释对我来说太抽象了,我不知道是否有人能解释得更清楚 我想知道是否有人能简单地解释一下EDSL的组成部分,以及它们的特征。在我们的课程中,我们经历了DSL的浅层和深层嵌入,并研究了DSL的以下构建块: 构造函数 组合子(原语和派生) 运行函数 我认为constructor和run函数更具自解释性,因此我更感兴趣的是理解是什么使组合子派生或原语。如果有人解释其他的概念,那也没什么坏处。这是我们讲座中
module Signal.Shallow
( Time
-- | the 'Signal' type is abstract
, Signal
-- * Smart constructors
, constS, timeS
-- * Combinators
, ($$), mapT
-- * Derived operation
, mapS
-- * Run function
, sample
) where
-- * Smart constructors
constS :: a -> Signal a
timeS :: Signal Time
-- * Combinators
($$) :: Signal (a -> b) -> Signal a -> Signal b
mapT :: (Time -> Time) -> Signal a -> Signal a
-- * Derived operation
mapS :: (a -> b) -> Signal a -> Signal b
-- * Run function
sample :: Signal a -> Time -> a
type Time = Double
newtype Signal a = Sig {unSig :: Time -> a}
-- | The constant signal.
constS x = Sig (const x)
-- | The time signal
timeS = Sig id
-- | Function application lifted to signals.
fs $$ xs = Sig (\t -> unSig fs t (unSig xs t))
基本组合器是DSL中内置的组合器,用基本语言(即Haskell)定义。DSL通常是围绕一种抽象类型构建的,这种类型的实现对最终用户来说是隐藏的。它完全不透明。该语言提供的基本组合器需要知道抽象是如何实际实现的 另一方面,派生的组合器可以根据DSL中已经存在的其他组合器来实现。它不需要知道任何关于抽象类型的信息。换句话说,派生的组合子是一个你可以自己编写的组合子 这与Haskell中的基本类型非常相似。例如,您不能自己实现
Int
或Int
之类的操作。这些要求编译器内置的东西才能工作,因为数字是经过特殊处理的。另一方面,Bool
不是原始的;你可以把它写成图书馆
data Bool = True | False -- ... you can't do this for Int!
DSL的“原语”和“派生”是相同的概念,只是编译器实际上是您的Haskell库
在您的示例中,Signal
是一种抽象类型。它实现为一个函数Time->a
,但该信息不会从模块中导出。将来,您(作为DSL的作者)可以自由更改信号的实现方式。(事实上,你真的很想:这不是一个有效的表示,在时间上使用Double
是很挑剔的。)
像$$
这样的函数是基本函数,因为它依赖于知道信号
是时间->A
。更改信号的表示形式时
,必须重写$
。此外,您的库的用户不能自己实现$
另一方面,mapS
是一个派生操作,因为它可以完全根据您正在导出的其他内容编写。它不需要知道任何有关信号的特殊信息,甚至可以由库的某个用户编写。实现可能类似于:
mapS f signal = constS f $$ signal
请注意它如何使用常量
和$
,但从不打开信号
。如何展开信号的知识完全隐藏在这两个函数中<代码>映射
是“派生”的,因为它只在DSL中编写,不需要任何低于抽象级别的东西。当您更改信号
的实现时,映射
仍将按原样工作:您只需正确更新常量
和$$
即可免费获得映射
所以:原语组合词是直接构建到您的语言中的,需要了解其内部实现细节的组合词。派生组合符完全是根据您的语言编写的,不依赖于这些内部细节。它们只是方便的函数,您的库的最终用户也可以很容易地编写它们。很好的解释。谢谢