tldr:根据@bame,您需要将结果绑定到getTime
或Update
执行Scenario
块中。IE。
Add_Car : CarId
with
startCoverage: Time
do
-- Check for a legal start date
now <- getTime
assert $ startCoverage > now
create this with datetime_vehicle_added = startCoverage, covered=True
要了解这里发生了什么,我们需要从 的类型开始
getTime
:
getTime : (HasTime m) => m Time
您期望的函数的类型签名是以下之一:
getTimeValue : Time
getTimeFunc : () -> Time
要了解差异,您需要考虑纯度和封装的概念。
纯度
在 DAML 中,所有函数都是纯函数。
纯函数是可以完全根据作为参数传递的值与作为结果返回的值之间的映射来描述的函数。值是具体的东西,例如时间、整数、文本等,以及值的列表、记录和变体,以及我稍后会谈到的其他一些东西。getTimeValue
是一个值,因此根据定义它是一个常数,它只是“停止时钟”意义上的“当前时间”。
getTimeFunc
是一个接受类型参数的函数Unit
,这意味着您可以传递给它的参数只有一个:()
. 因为这个函数是纯函数,这意味着它不能考虑它的参数之外的任何东西,所以这个函数也必须返回一个常量值。实际上,getTimeValue
和之间的唯一区别getTimeFunc
是您必须通过getTimeFunc
()
才能获得常量。
封装
存在一个可以查询和使用的具有“当前时间”概念的外部世界是一个“上下文”,这意味着任何使用它的函数都不能再完全按照输入->输出来描述。这被描述为“不纯”。
在 DAML 中,所有函数都是纯函数,因此如果我们想处理“杂质”,我们必须将杂质封装在一个纯值中。在 DAML 中,我们将这种封装表示为一种类型:
encapsulatedImpureValue : m a
所以在我们的例子中,值是一个Time
值:
encapsulatedImpureTimeValue : m Time
Time
您可以将其解读为依赖于m
要评估的上下文的类型的封装值。由于我们没有提到任何关于上下文的内容,m
除了它存在之外,这还不足以让我们实现它。具体来说,我们还需要说上下文必须是一个带有“当前时间”概念的上下文,这就是我们getTime
在 DAML 标准库中最终得到签名的方式:
getTime : (HasTime m) => m Time
您可以将其解读为:一个封装的时间值,Time
它取决于m
支持的上下文HasTime
(即“当前时间”的概念)。
使用封装值
我们现在可以写:
let now = getTime
并且now
将是一个纯封装值——它不会立即有用,因为在任何期望纯值的函数中使用它的任何尝试都Time
将失败,因为这将需要破坏封装,并且 DAML 严格强制违反封装作为编译错误。
要使用封装值,您必须首先指定合适的上下文,然后在该上下文中运行该值。DAML 提供了两个支持HasTime
:Update
和Scenario
. 它还提供了一种运行Scenario
包装值的方法,一种运行Update
包装值的方法,以及两种将Update
值转换为Scenario
值的方法。
DAML 模块中的每个顶级场景值都将由 DAML 解释器作为 DAML 测试运行。
每个 DAML 模板选择的主体被定义为Update
在选择执行时将运行的值。
您可以使用submit
, 和submitMustFail
函数来生成一个
Scenario
值,该值在运行时将运行一个Update
授权为指定的值Party
。
组合封装值
有许多标准 API 对几乎所有函数式语言都是通用的,用于将封装的值组合成复合值。您将听说过最著名的:“Functor”和“Monad”,它们定义了采用封装值和函数并以各种方式组合它们的函数。封装是一种基本的软件工程原则,因此大多数 FP 语言都提供语法糖以使这些更容易使用也就不足为奇了——DAML 也不例外。
作为 Functor 接口实例的封装值支持该fmap
函数,DAML 也为此提供了一个中缀运算符<$>
。
作为 Monad 接口实例的封装值(
Action
在 DAML 中调用),支持fmap
、pure
和 bind/flatMap 函数。DAML为;提供return
别名。pure
以及>>=
绑定/平面映射的运算符。它还提供了 do-notation 作为语法糖>>=
:
do
t <- getTime
a <- useTime t
combineWithTime a t
产生一个复合Update
值(当它运行时),运行getTime
,将结果值传递给,useTime
然后将两个结果传递给
combineWithTime
。这个 do-block 的结果也是封装的Update
值,所以我们不会破坏封装,因为在我们运行时,
我们已经为封闭的复合值updateA/B/C
提供了封装上下文。Update
如果(如您在示例中所做的那样)我们将此do
块设置为选项的主体,则执行该选项将运行复合更新。或者,如果我们将它传递给我们,submit
我们可以将它作为场景的一部分运行。如果您都不这样做(例如,如果您有两个 Update 值并使用 if 表达式在它们之间进行选择,则可能发生这种情况),那么它将没有可观察到的效果,因为在 DAML 中所有函数都是纯函数。