2

我在关于合同选择的断言语句中使用 getTime,如下所示:

Add_Car : CarId
        with
            startCoverage: Time
        do
          -- Check for a legal start date
          assert (
            startCoverage > getTime
            )
          create this with datetime_vehicle_added = startCoverage, covered=True

它会产生一个错误:

error:
    * Couldn't match expected type `Time' with actual type `m0 Time'
    * In the second argument of `(>)', namely `getTime'
      In the first argument of `assert', namely
        `(startCoverage > getTime)'
      In a stmt of a 'do' block: assert (startCoverage > getTime)

getTime 不返回“时间”类型的值吗?什么是“莫时间”?

4

2 回答 2

2

tldr:根据@bame,您需要将结果绑定到getTimeUpdate 执行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:UpdateScenario. 它还提供了一种运行Scenario包装值的方法,一种运行Update包装值的方法,以及两种将Update值转换为Scenario值的方法。

  1. DAML 模块中的每个顶级场景值都将由 DAML 解释器作为 DAML 测试运行。

  2. 每个 DAML 模板选择的主体被定义为Update在选择执行时将运行的值。

  3. 您可以使用submit, 和submitMustFail函数来生成一个 Scenario值,该值在运行时将运行一个Update授权为指定的值Party

组合封装值

有许多标准 API 对几乎所有函数式语言都是通用的,用于将封装的值组合成复合值。您将听说过最著名的:“Functor”和“Monad”,它们定义了采用封装值和函数并以各种方式组合它们的函数。封装是一种基本的软件工程原则,因此大多数 FP 语言都提供语法糖以使这些更容易使用也就不足为奇了——DAML 也不例外。

作为 Functor 接口实例的封装值支持该fmap函数,DAML 也为此提供了一个中缀运算符<$>

作为 Monad 接口实例的封装值( Action在 DAML 中调用),支持fmappure和 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 中所有函数都是纯函数。

于 2019-02-21T02:04:50.330 回答
2

getTime只有作为有账本时间概念的交易的一部分才有意义。Them0是一个类型变量,它引用 anUpdateScenario,具体取决于您的上下文。实际上,这仅仅意味着您需要将结果绑定getTime到块内的变量do

do
  currentTime <- getTime
  assert ( startCoverage > currentTime )
于 2019-02-20T18:56:58.753 回答