Skip to content

Document what kind of type system features are lacking for builtin functions #4410

Description

@Marwes

As discussed earlier, I have started to go through the builtin functions, documenting what kind of type information that they are currently lacking. To give an estimate on how valuable each feature might be I have grouped each function under the features that they would benefit from (one function may appear under multiple sections, as they could benefit from multiple improvements).

There may be mistakes, and omissions here as well as it isn't always clear exactly how a function transforms the data without delving deep into the code (which would take too much time IMO).

Polymorphic labels

Functions that should take column as a Label which it may then refer to in the record types. Label types are represented by 'label here. cc #4388

builtin columns : (<-tables: [A], ?column: 'label) => [{ 'label: string }] where A: Record

builtin count : (<-tables: [A], ?column: string) => [{ _value: int }] where A: Record

builtin distinct : (<-tables: [{ A with 'column: B }], ?column: 'column) => [{ _value: B }] where A: Record, B: Equatable

builtin duplicate : (<-tables: [{ A with 'column: B }], column: 'column, as: 'as) => [{ A with 'column: B, 'as: B }]
    where A: Record

builtin elapsed : (<-tables: [{ A with 'time: time }], ?unit: duration, ?timeColumn: 'time, ?columnName: 'column) => [{ A with 'time: time, 'column: duration }]
    where
    A: Record

builtin fill : (<-tables: [{ A with 'column: B }], ?column: 'column, ?value: B, ?usePrevious: bool) => [{ A with 'column: B }] where A: Record

builtin histogram : (
        <-tables: [{ A with 'column: float }],
        ?column: 'column,
        ?upperBoundColumn: 'upper,
        ?countColumn: 'count,
        bins: [float],
        ?normalize: bool,
    ) => [{ 'upper: float, 'count: float }]
    where
    A: Record

builtin histogramQuantile : (
        <-tables: [{ A with 'upper: float, 'count: float }],
        ?quantile: float,
        ?countColumn: 'count,
        ?upperBoundColumn: 'upper,
        ?valueColumn: 'value,
        ?minValue: float,
    ) => [{ 'value: float }]
    where
    A: Record

builtin holtWinters : (
        <-tables: [{ A with 'time: time, 'column: B }],
        n: int,
        interval: duration,
        ?withFit: bool,
        ?column: 'column,
        ?timeColumn: 'time,
        ?seasonality: int,
    ) => [{ _value: B }]
    where
    A: Record,
    B: Numeric

builtin hourSelection : (<-tables: [{ A with 'time: time }], start: int, stop: int, ?timeColumn: 'time) => [{ A with 'time: time }] where A: Record

builtin integral : (
        <-tables: [{ A with _start: time, _stop: time, 'column: B }],
        ?unit: duration,
        ?timeColumn: 'time,
        ?column: 'column,
        ?interpolate: string,
    ) => [{ A with _start: time, _stop: time, 'column: B }]
    where
    A: Record,
    B: Numeric

builtin kaufmansAMA : (<-tables: [{ A with 'label: B }], n: int, ?column: 'label) => [{ A with 'column: float }]
    where A: Record, B: Numeric

builtin keys : (<-tables: [A], ?column: 'label) => [{ 'label: string }] where A: Record, B: Record

builtin last : (<-tables: [{ A with 'label: B }], ?column: 'label) => [{ A with 'label: B }] where A: Record

builtin max : (<-tables: [{ A with 'label: B }], ?column: 'label) => [{ A with 'label: B }]
    where A: Record,
          B: Comparable

builtin mean : (<-tables: [{ A with 'column: B }], ?column: 'column) => [{ 'column: B }]
    where A: Record, B: Numeric

builtin min : (<-tables: [{ A with 'label: B }], ?column: 'label) => [{ A with 'label: B }]
    where A: Record,
          B: Comparable

// Could be extended to work with other types than string?
builtin set : (<-tables: [{ A with 'key: ?string }], key: 'key, value: string) => [{ A with 'key: string}] where A: Record

builtin skew : (<-tables: [{ A with 'column: B }], ?column: 'column) => [{ A with 'column: float }]
    where A: Record,
          B: Numeric

// Having the output as C is more general than necessary, however the function converts `uint => int` so we can't just use `B` there
builtin spread : (<-tables: [{ A with 'column: B }], ?column: 'column) => [{ A with 'column: C }]
    where A: Record, B: Numeric

// Seems to lack documentation, so I may be getting this wrong
builtin stateTracking : (
        <-tables: [{ A with 'count: B, 'duration: duration, 'time: time }],
        fn: (r: A) => bool,
        ?countColumn: 'count,
        ?durationColumn: 'duration,
        ?durationUnit: duration,
        ?timeColumn: 'time,
    ) => [{ A with 'count: B, 'duration: duration, 'time: time }]
    where
    A: Record

builtin stddev : (<-tables: [{ A with 'column: B }], ?column: 'column, ?mode: "sample" | "populaton") => [{ 'column: B }] where A: Record, B: Numeric

builtin sum : (<-tables: [{ A with 'column: B }], ?column: 'column) => [{ 'column: B }]
    where A: Record, B: Numeric

builtin unique : (<-tables: [{ A with 'column: B }], ?column: 'column) => [A]
    where A: Record, B: Equatable

builtin _window : (
        <-tables: [{ A with 'time: time }],
        every: duration,
        period: duration,
        offset: duration,
        location: {zone: string, offset: duration},
        timeColumn: 'time,
        startColumn: 'start,
        stopColumn: 'stop,
        createEmpty: bool,
    ) => [{ A with 'start: time, 'stop: time }]
    where
    A: Record,
    B: Record

builtin getColumn : (<-table: [{ A with 'column: B }], column: 'column) => [B] where A: Record

builtin findColumn : (<-tables: [{ A with 'column: B }], fn: (key: GroupKeyOf(A)) => bool, column: 'column) => [B]
    where A: Record

Lists of polymorphic labels

In addition to "Polymorphic labels" these need the capability to accept multiple, different labels in columns and refer to those in the record types.

builtin chandeMomentumOscillator : (<-tables: [{ A with ['label]: B }], n: int, ?columns: ['label]) => [{ A with _value: B }]
    where A: Record,

builtin covariance : (<-tables: [{ A with ['column]: B }], ?pearsonr: bool, ?valueDst: 'dst, columns: ['column]) => [{ A with 'label: B }]
    where
    A: Record

builtin cumulativeSum : (<-tables: [{ A with ['label]: B }], ?columns: ['label]) => [{ A with ['label]: B }]
    where A: Record,
          B: Addable

builtin derivative : (
        <-tables: [{ A with  ['column]: B, 'time: C }],
        ?unit: duration,
        ?nonNegative: bool,
        ?columns: ['column],
        ?timeColumn: 'time,
    ) => [{ A with ['column]: B, 'time: C }]
    where
    A: Record,
    B: Divisible + Subtractable,
    C: Time

builtin difference : (
        <-tables: [{ A with ['column]: B}],
        ?nonNegative: bool,
        ?columns: ['column],
        ?keepFirst: bool,
        ?initialZero: bool,
    ) => [{ A with ['column]: B}]
    where
    A: Record,
    B: Subtractable


// Due to the `fn` argument we can't use `A` in the returned record as `fn` makes the dropped columns dynamic
builtin drop : (<-tables: [{ A with ['column]: B }], ?fn: (column: string) => bool, ?columns: ['column]) => [{ C with  }]

builtin group : (<-tables: [{ A with ['column]: B }], ?mode: "by" | "except", ?columns: ['column]) => [{ A with ['column]: B }]
    where A: Record

builtin join : (<-tables: { 'label1: [{ A with ['label]: B }], 'label2: [{ C with ['label]: B }] }, ?method: "inner", ?on: ['label]) => [{ ["_field_" + 'label]: B }]
    where A: Record, C: Record

builtin keep : (<-tables: [{ A with ['column]: C }], ?columns: ['column], ?fn: (column: string) => bool) => [B] where A: Record, B: Record

builtin keyValues : (<-tables: [{ A with ['column]: B }], ?keyColumns: ['column]) => [{C with _key: string, _value: B}]
    where
    A: Record,
    C: Record

builtin pivot : (<-tables: [{ A with ['row]: string, ['column]: string, 'value: B }], rowKey: ['row], columnKey: ['column], valueColumn: 'value) => [C]
    where
    A: Record,
    C: Record

builtin relativeStrengthIndex : (<-tables: [{ A with ['column]: B }], n: int, ?columns: ['column]) => [{ A with ['column]: B }]
    where A: Record, B: Numeric


builtin rename : (<-tables: [{ A with ['old]: B }], ?fn: (column: string) => string, ?columns: { ['old]: 'new }) => [{ A with ['new]: B }]
    where
    A: Record

builtin sample : (<-tables: [{ A with 'column: B }], n: int, ?pos: int, ?column: 'column) => [{ A with 'column: B }]
    where A: Record

builtin timeShift : (<-tables: [{ A with ['column]: time }], duration: duration, ?columns: ['column]) => [{ A with ['column]: time }]

builtin sort : (<-tables: [{ A with ['column]: B }], ?columns: ['column], ?desc: bool) => [{ A with ['column]: B }]
    where A: Record,
          B: Comparable

Default labels

Many builtins use the _value or _time column as the default if none is specified which could be reflected either implicitily, or explicitly in the type system.

Specific label lists

Functions that take a string argument which is limited to one of several strings.

builtin group : (<-tables: [{ A with ['column]: B }], ?mode: "by" | "except", ?columns: ['column]) => [{ A with ['column]: B }]
    where A: Record

builtin stddev : (<-tables: [{ A with 'column: B }], ?column: 'column, ?mode: "sample" | "populaton") => [{ 'column: B }] where A: Record, B: Numeric

Label concatenation

Functions that not only accept a label, but also alters the label by prefixing it.

builtin join : (<-tables: { 'label1: [{ A with ['label]: B }], 'label2: [{ C with ['label]: B }] }, ?method: "inner", ?on: ['label]) => [{ ["_value_" + 'label]: B }]
    where A: Record, C: Record

Diverging type

We could have a special type for marking builtins that always diverges.

builtin die : (msg: string) => !

Group key

Some functions adds the group key of an input table to the output, but skips the other columns. Or they otherwise need to refer to the group key (incomplete).

builtin reduce : (<-tables: [A], fn: (r: A, accumulator: B) => B, identity: B) => [{ B with GroupKeyOf(A) }]
    where
    A: Record,
    B: Record

builtin tableFind : (<-tables: [A], fn: (key: GroupKeyOf(A)) => bool) => [A] where A: Record

builtin findColumn : (<-tables: [{ A with 'column: B }], fn: (key: GroupKeyOf(A)) => bool, column: 'column) => [B]
    where A: Record

builtin findRecord : (<-tables: [A], fn: (key: GroupKeyOf(A)) => bool, idx: int) => A where A: Record, B: Record

Needs Kinds to limit type conversion

Each of these only accept a limited number of types successfully so we could restrict them further by adding more kinds.

// type conversion functions
builtin bool : (v: A) => bool
builtin bytes : (v: A) => bytes
builtin duration : (v: A) => duration
builtin float : (v: A) => float
builtin int : (v: A) => int
builtin string : (v: A) => string
builtin time : (v: A) => time
builtin uint : (v: A) => uint

Needs separation of Stream and Array

Only accepts an array and not a stream (which is the case for practically all other builtins).

builtin length : (arr: Array<A>) => int

Can be updated today

// Lacks the `Timeable` bounds
builtin range : (
        <-tables: [{A with _time: time}],
        start: B,
        ?stop: C,
    ) => [{A with _time: time, _start: time, _stop: time}]
    where
    B: Timeable,
    C: Timeable,

No change needed

builtin exponentialMovingAverage : (<-tables: [{B with _value: A}], n: int) => [{B with _value: A}] where A: Numeric

builtin filter : (<-tables: [A], fn: (r: A) => bool, ?onEmpty: string) => [A] where A: Record

builtin first : (<-tables: [A], ?column: string) => [A] where A: Record

builtin limit : (<-tables: [A], n: int, ?offset: int) => [A]

builtin map : (<-tables: [A], fn: (r: A) => B, ?mergeKey: bool) => [B]

builtin movingAverage : (<-tables: [{B with _value: A}], n: int) => [{B with _value: float}]
    where A: Numeric

builtin tail : (<-tables: [A], n: int, ?offset: int) => [A]

builtin tripleExponentialDerivative : (<-tables: [{B with _value: A}], n: int) => [{B with _value: float}]
    where
    A: Numeric,
    B: Record

builtin union : (tables: [[A]]) => [A] where A: Record

builtin yield : (<-tables: [A], ?name: string) => [A] where A: Record

builtin getRecord : (<-table: [A], idx: int) => A where A: Record

// contains function
builtin contains : (value: A, set: [A]) => bool where A: Nullable

builtin inf : duration

builtin linearBins : (start: float, width: float, count: int, ?infinity: bool) => [float]

builtin logarithmicBins : (start: float, factor: float, count: int, ?infinity: bool) => [float]
All builtins in universe.flux

Builtins in universe.flux

builtin chandeMomentumOscillator : (<-tables: [{ A with ['label]: B }], n: int, ?columns: ['label]) => [{ A with _value: B }]
    where A: Record,

builtin columns : (<-tables: [A], ?column: 'label) => [{ labels: string }] where A: Record

builtin count : (<-tables: [A], ?column: string) => [{ _value: int }] where A: Record

builtin covariance : (<-tables: [{ A with ['column]: B }], ?pearsonr: bool, ?valueDst: 'dst, columns: ['column]) => [{ A with 'label: B }]
    where
    A: Record

builtin cumulativeSum : (<-tables: [{ A with ['label]: B }], ?columns: ['label]) => [{ A with ['label]: B }]
    where A: Record,
          B: Addable

builtin derivative : (
        <-tables: [{ A with  ['column]: B, 'time: C }],
        ?unit: duration,
        ?nonNegative: bool,
        ?columns: ['column],
        ?timeColumn: 'time,
    ) => [{ A with ['column]: B, 'time: C }]
    where
    A: Record,
    B: Divisible + Subtractable,
    C: Time

builtin die : (msg: string) => A

builtin difference : (
        <-tables: [{ A with ['column]: B}],
        ?nonNegative: bool,
        ?columns: ['column],
        ?keepFirst: bool,
        ?initialZero: bool,
    ) => [{ A with ['column]: B}]
    where
    A: Record,
    B: Subtractable

builtin distinct : (<-tables: [{ A with 'column: B }], ?column: 'column) => [{ _value: B }] where A: Record, B: Equatable

// Due to the `fn` argument we can't use `A` in the returned record as `fn` makes the dropped columns dynamic
builtin drop : (<-tables: [{ A with ['column]: B }], ?fn: (column: string) => bool, ?columns: ['column]) => [{ C with  }]
    where A: Record, C: Record

builtin duplicate : (<-tables: [{ A with 'column: B }], column: 'column, as: 'as) => [{ A with 'column: B, 'as: B }]
    where A: Record

builtin elapsed : (<-tables: [{ A with 'time: time }], ?unit: duration, ?timeColumn: 'time, ?columnName: 'column) => [{ A with 'time: time, 'column: duration }]
    where
    A: Record

builtin exponentialMovingAverage : (<-tables: [{B with _value: A}], n: int) => [{B with _value: A}] where A: Numeric

builtin fill : (<-tables: [{ A with 'column: B }], ?column: 'column, ?value: B, ?usePrevious: bool) => [{ A with 'column: B }] where A: Record

builtin filter : (<-tables: [A], fn: (r: A) => bool, ?onEmpty: string) => [A] where A: Record

builtin first : (<-tables: [A], ?column: string) => [A] where A: Record

builtin group : (<-tables: [{ A with ['column]: B }], ?mode: "by" | "except", ?columns: ['column]) => [{ A with ['column]: B }]
    where A: Record

// Could be generalize to work on any numbers?
builtin histogram : (
        <-tables: [{ A with 'column: float }],
        ?column: 'column,
        ?upperBoundColumn: 'upper,
        ?countColumn: 'count,
        bins: [float],
        ?normalize: bool,
    ) => [{ 'upper: float, 'count: float }]
    where
    A: Record

builtin histogramQuantile : (
        <-tables: [{ A with 'upper: float, 'count: float }],
        ?quantile: float,
        ?countColumn: 'count,
        ?upperBoundColumn: 'upper,
        ?valueColumn: 'value,
        ?minValue: float,
    ) => [{ 'value: float }]
    where
    A: Record

builtin holtWinters : (
        <-tables: [{ A with 'time: time, 'column: B }],
        n: int,
        interval: duration,
        ?withFit: bool,
        ?column: 'column,
        ?timeColumn: 'time,
        ?seasonality: int,
    ) => [{ _value: B }]
    where
    A: Record,
    B: Numeric

builtin hourSelection : (<-tables: [{ A with 'time: time }], start: int, stop: int, ?timeColumn: 'time) => [{ A with 'time: time }] where A: Record

builtin integral : (
        <-tables: [{ A with _start: time, _stop: time, 'column: B }],
        ?unit: duration,
        ?timeColumn: 'time,
        ?column: 'column,
        ?interpolate: string,
    ) => [{ A with _start: time, _stop: time, 'column: B }]
    where
    A: Record,
    B: Numeric

builtin join : (<-tables: { 'label1: [{ A with ['label]: B }], 'label2: [{ C with ['label]: B }] }, ?method: "inner", ?on: ['label]) => [{ ["_field_" + 'label]: B }]
    where A: Record, C: Record


builtin kaufmansAMA : (<-tables: [{ A with 'label: B }], n: int, ?column: 'label) => [{ A with 'column: float }]
    where A: Record, B: Numeric

// Due to the `fn` argument we can't specify the output record as it ends up being dynamic
builtin keep : (<-tables: [{ A with ['column]: C }], ?columns: ['column], ?fn: (column: string) => bool) => [B] where A: Record, B: Record

builtin keyValues : (<-tables: [{ A with ['column]: B }], ?keyColumns: ['column]) => [{C with _key: string, _value: B}]
    where
    A: Record,
    C: Record

builtin keys : (<-tables: [A], ?column: 'label) => [{ 'label: string }] where A: Record, B: Record

builtin last : (<-tables: [{ A with 'label: B }], ?column: 'label) => [{ A with 'label: B }] where A: Record

builtin limit : (<-tables: [A], n: int, ?offset: int) => [A]

builtin map : (<-tables: [A], fn: (r: A) => B, ?mergeKey: bool) => [B]

builtin max : (<-tables: [{ A with 'label: B }], ?column: 'label) => [{ A with 'label: B }]
    where A: Record,
          B: Comparable

builtin mean : (<-tables: [{ A with 'column: B }], ?column: 'column) => [{ 'column: B }]
    where A: Record, B: Numeric

builtin min : (<-tables: [{ A with 'label: B }], ?column: 'label) => [{ A with 'label: B }]
    where A: Record,
          B: Comparable

builtin mode : (<-tables: [{ A with 'column: B }], ?column: 'column) => [{ _value: B }]
    where A: Record

builtin movingAverage : (<-tables: [{B with _value: A}], n: int) => [{B with _value: float}]
    where A: Numeric

builtin quantile : (
        <-tables: [{ A with 'column: B }],
        ?column: 'column,
        q: float,
        ?compression: float,
        ?method: "estimate_tdigest" | "exact_mean" | "exact_selector",
    ) => [{ A with 'column: B }]
    where
    A: Record

builtin pivot : (<-tables: [{ A with ['row]: string, ['column]: string, 'value: B }], rowKey: ['row], columnKey: ['column], valueColumn: 'value) => [C]
    where
    A: Record,
    C: Record

builtin range : (
        <-tables: [{A with _time: time}],
        start: B,
        ?stop: C,
    ) => [{A with _time: time, _start: time, _stop: time}]
    where
    B: Timeable,
    C: Timeable,

builtin reduce : (<-tables: [A], fn: (r: A, accumulator: B) => B, identity: B) => [{ B with GroupKeyOf(A) }]
    where
    A: Record,
    B: Record

builtin relativeStrengthIndex : (<-tables: [{ A with ['column]: B }], n: int, ?columns: ['column]) => [{ A with ['column]: B }]
    where A: Record, B: Numeric

builtin rename : (<-tables: [{ A with ['old]: B }], ?fn: (column: string) => string, ?columns: { ['old]: 'new }) => [{ A with ['new]: B }]
    where
    A: Record

builtin sample : (<-tables: [{ A with 'column: B }], n: int, ?pos: int, ?column: 'column) => [{ A with 'column: B }]
    where A: Record

// Could be extended to work with other types than string?
builtin set : (<-tables: [{ A with 'key: ?string }], key: 'key, value: string) => [{ A with 'key: string}] where A: Record

builtin tail : (<-tables: [A], n: int, ?offset: int) => [A]

builtin timeShift : (<-tables: [{ A with ['column]: time }], duration: duration, ?columns: ['column]) => [{ A with ['column]: time }]

builtin skew : (<-tables: [{ A with 'column: B }], ?column: 'column) => [{ A with 'column: float }]
    where A: Record,
          B: Numeric

// Having the output as C is more general than necessary, however the function converts `uint => int` so we can't just use `B` there
builtin spread : (<-tables: [{ A with 'column: B }], ?column: 'column) => [{ A with 'column: C }]
    where A: Record, B: Numeric

builtin sort : (<-tables: [{ A with ['column]: B }], ?columns: ['column], ?desc: bool) => [{ A with ['column]: B }]
    where A: Record,
          B: Comparable

// Seems to lack documentation, so I may be getting this wrong
builtin stateTracking : (
        <-tables: [{ A with 'count: B, 'duration: duration, 'time: time }],
        fn: (r: A) => bool,
        ?countColumn: 'count,
        ?durationColumn: 'duration,
        ?durationUnit: duration,
        ?timeColumn: 'time,
    ) => [{ A with 'count: B, 'duration: duration, 'time: time }]
    where
    A: Record

builtin stddev : (<-tables: [{ A with 'column: B }], ?column: 'column, ?mode: "sample" | "populaton") => [{ 'column: B }] where A: Record, B: Numeric

builtin sum : (<-tables: [{ A with 'column: B }], ?column: 'column) => [{ 'column: B }]
    where A: Record, B: Numeric

builtin tripleExponentialDerivative : (<-tables: [{B with _value: A}], n: int) => [{B with _value: float}]
    where
    A: Numeric,
    B: Record

builtin union : (tables: [[A]]) => [A] where A: Record

builtin unique : (<-tables: [{ A with 'column: B }], ?column: 'column) => [A]
    where A: Record, B: Equatable

builtin _window : (
        <-tables: [{ A with 'time: time }],
        every: duration,
        period: duration,
        offset: duration,
        location: {zone: string, offset: duration},
        timeColumn: 'time,
        startColumn: 'start,
        stopColumn: 'stop,
        createEmpty: bool,
    ) => [{ A with 'start: time, 'stop: time }]
    where
    A: Record,
    B: Record

builtin yield : (<-tables: [A], ?name: string) => [A] where A: Record

// stream/table index functions
builtin tableFind : (<-tables: [A], fn: (key: GroupKeyOf(A)) => bool) => [A] where A: Record
builtin getColumn : (<-table: [{ A with 'column: B }], column: 'column) => [B] where A: Record
builtin getRecord : (<-table: [A], idx: int) => A where A: Record
builtin findColumn : (<-tables: [{ A with 'column: B }], fn: (key: GroupKeyOf(A)) => bool, column: 'column) => [B]
    where A: Record
builtin findRecord : (<-tables: [A], fn: (key: GroupKeyOf(A)) => bool, idx: int) => A where A: Record, B: Record

// type conversion functions
builtin bool : (v: A) => bool
builtin bytes : (v: A) => bytes
builtin duration : (v: A) => duration
builtin float : (v: A) => float
builtin int : (v: A) => int
builtin string : (v: A) => string
builtin time : (v: A) => time
builtin uint : (v: A) => uint

// contains function
builtin contains : (value: A, set: [A]) => bool where A: Nullable

// other builtins
builtin inf : duration
builtin length : (arr: Array<A>) => int
builtin linearBins : (start: float, width: float, count: int, ?infinity: bool) => [float]
builtin logarithmicBins : (start: float, factor: float, count: int, ?infinity: bool) => [float]

// die returns a fatal error from within a flux script
builtin die : (msg: string) => A

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions