empirical-soft / empirical-lang

A language for time-series analysis
https://www.empirical-soft.com/
Other
168 stars 13 forks source link

Function Traits and Computation Mode #26

Closed chrisaycock closed 4 years ago

chrisaycock commented 5 years ago

TL;DR We want to add "function traits" that track an expression's capabilities and "computation modes" that indicate the mechanism of evaluation.

# pure, transform, linear
func inc(x: Int64):
  return x + 1
end

Goal

Beyond type information, there are certain function characteristics that are vital for the compiler to know. For example, we may wish to know that an expression is to be evaluated at compile time, or that an expression represents a streaming computation.

Function traits and computation modes are a proposed solution. By explicitly listing a function's capabilities, we have a way of allowing new mechanisms of evaluation.

The traits proposed here are an inversion of effect systems. (If types describe the kind of data, then effects describe the kind of computation.) Effects generally describe "bad" things that can happen, like IO, state changes, and exceptions. A compound expression's effect is the set-wise union of all subexpressions' effects.

The function traits proposed here describe something "good" that can happen, and compound expression traits are the intersection of subexpression traits. The resulting computation mode is then determined from the traits.

Traits and Modes

Traits describe the qualities of a computation:

Modes indicate how a computation is performed:

A function can have any number of traits, but only one mode. The resulting traits of a function call are simply the set-wise intersection of the the function and all parameters. The mode is determined by specific rules:

Most VVM ops will be pure, transform, and linear with the following exceptions:

Regarding mode, literals are comptime. Constant assignment (let) can be comptime, but variables (var) are not.

Implementation

The traits will be stored as an integer bitmap; subexpressions need only be &'ed and |'ed together.

The traits and mode will exists for all expressions and declarations. As mentioned in the final remarks of issue #23, we need to know when a value is available at compile time:

# both of these values are comptime:
let x = 7
let y = add(3, 4)

A function's traits and mode are inferred at compile time based on the function body's return statements, similar to how the function's return type is inferred.

Thinking Ahead

It is conceivable that users could define their own traits as part of a metaprogramming regimen. I don't yet know what this would look like, so I am putting that on hold for now.

Users might need to explicitly list the traits for a recursive function. (Users already have to explicitly list the return type for recursive functions.) That implies some sort of syntax like

func foo(x: Int64) pure, transform, linear -> Int64:

The primary purpose for creating function traits is to support both compile-time function evaluation and streaming computation. We may want to distinguish chunked from continuous, but we will punt that for now.