The difference between continuous and point-in-time expressions

Fenl expressions are either discrete or continuous. Discrete expressions describe to values that are only meaningful at specific times. Continuous expressions describe values that are meaningful over ranges of time.

Discrete Expressions

A discrete expression produces values at a finite set of times, at all other times its value is null. Tables contain a finite set of events, and each event is associated with a specific time. A table only has a a meaningful value at the set of times for which events exist. Similarly, simple operations such as field reference produce discrete results when applied to discrete expressions.

Discrete expressions may be combined in various ways, for example by adding them together as shown.

In most cases operations produce output values any time one of their input expressions produces a value. In this example we can see that many of the values produced are null, since the expression Purchase.amount is defined at different times than Review.stars, and adding null to a number produces null.

Continuous Expressions

A continuous expression has a meaningful value at all points in time. Continuous expressions produce values at every time contributing to their value. Aggregations generally produce continuous values.

For example, at any point in time we can describe the sum of all Purchase.amount values seen so far.

Continuous expressions have a value at all times, but they produce values at specific times. These times are illustrated above as circles. A continuous expression can be thought of as producing a stream of updates; at any given time the stream takes the value of the most recent update.

When continuous values are aggregated, the aggregation is updated when a value is produced. For example, count produces the same result for a discrete expression with and without an intermediate aggregation.

Purchase.amount | count() == Purchase.amount | sum() | count()

️ Design Note

The distinction between "having" a value and "producing" a value allows aggregations to be applied in the same way to both continuous and discrete expression. Applying an operation such as sum to a continuous value (in the mathematical sense) would correspond to integrating the value.

We feel interpreting aggregations of continuous values as integration would be surprising, especially in cases such as count.

Interactions Between Continuous and Discrete Expressions

The continuity of the result of an operation involving multiple expressions depends on the continuity of the operation's inputs. In general discreteness is "greedy". Given discrete expressions defined at the set of times A and B and a continuous expression, the result of a binary operation between any pair is described in the following table.

Discrete[A]Discrete[A]Discrete[A and B]Discrete[A]
Discrete[B]Discrete[A and B]Discrete[B]Discrete[B]

When a discrete expression is combined with a continuous expression, each the value at time present in the discrete expression is combined with the value the continuous expression has at that same time to produce a result.

Filtering when Values are Produced

As mentioned, most operations between expressions produce a value when any input expression produces a value. This can lead to values being produced at undesirable times. The when operation allows filtering the times at which an expression produces values. It works by producing the value of an expression whenever a predicate expression produces the value true.

Default Values

Continuous expressions have a value at all points in time and produce a value at a fixed set of times. Thinking of continuous expressions as producing a stream of updates raises the question "what is the expression's value before the first update?". This question is answered through an expression's default value: the value of the expression before any events have been processed.

Default values follow a number of rules:

  1. The default of a literal / constant expression is it's value.
  2. The default value of a discrete expression is null, the same as any other time for which the expression isn't defined.
  3. The default value of most aggregations is also null, for example there's no meaningful value associated with Purchase.amount | max() if there have are no purchases. An exception is the aggregation count, whose default value is zero.
  4. A default value can be provided for an expression using the with_default operation. This operation has no effect on the input expression after a value is produced, but changes the default value.
  5. The default value of most aggregations when applied to an expression with a default value is the input expression's default value. The aggregation count is another exception here - its default value is always zero.
  6. The default value of an operation between expressions is generally the result of applying the operation to the default values of the input expressions.

These rules are demonstrated with examples in the following table.

ExpressionDefault ValueComment
4242Constants always have the same value
Purchase.amountnullDiscrete expressions are null by default
| sum()
nullThe sum of null is null
| count()
0The count of null is zero
| with_default(0)
0An explicit default is provided
| with_default(0)
| sum()
0The sum of 0 is 0
| with_default(0)
| count()
0The default value of count is always 0
Purchase.amount + 1nullThe sum of null and 1 is null
| count() + 1
1The sum of 0 and 1 is 1

© Copyright 2021 Kaskada, Inc. All rights reserved. Privacy Policy

Kaskada products are protected by patents in the United States, and Kaskada is currently seeking protection internationally with pending applications.