lang: Improve graph shape with speculative execution

Most of the time, we don't need to have a dynamic call sub graph, since
the actual function call could be represented statically as it
originally was before lambda functions were implemented. Simplifying the
graph shape has important performance benefits in terms of both keep the
graph smaller (memory, etc) and in avoiding the need to run transactions
at runtime (speed) to reshape the graph.

Co-authored-by: Samuel Gélineau <gelisam@gmail.com>
This commit is contained in:
James Shubin
2025-03-17 02:31:01 -04:00
parent 9c9f2f558a
commit 37bb67dffd
139 changed files with 1871 additions and 262 deletions

View File

@@ -56,7 +56,14 @@ const (
)
func init() {
info := &simple.Info{
Pure: true,
Memo: true,
Fast: true,
Spec: true,
}
RegisterOperator("+", &simple.Scaffold{
I: info,
T: types.NewType("func(?1, ?1) ?1"),
C: simple.TypeMatch([]string{
"func(str, str) str", // concatenation
@@ -91,6 +98,7 @@ func init() {
})
RegisterOperator("-", &simple.Scaffold{
I: info,
T: types.NewType("func(?1, ?1) ?1"),
C: simple.TypeMatch([]string{
"func(int, int) int", // subtraction
@@ -115,6 +123,7 @@ func init() {
})
RegisterOperator("*", &simple.Scaffold{
I: info,
T: types.NewType("func(?1, ?1) ?1"),
C: simple.TypeMatch([]string{
"func(int, int) int", // multiplication
@@ -141,6 +150,7 @@ func init() {
// don't add: `func(int, float) float` or: `func(float, int) float`
RegisterOperator("/", &simple.Scaffold{
I: info,
T: types.NewType("func(?1, ?1) float"),
C: simple.TypeMatch([]string{
"func(int, int) float", // division
@@ -173,6 +183,7 @@ func init() {
})
RegisterOperator("==", &simple.Scaffold{
I: info,
T: types.NewType("func(?1, ?1) bool"),
C: func(typ *types.Type) error {
//if typ == nil { // happens within iter
@@ -218,6 +229,7 @@ func init() {
})
RegisterOperator("!=", &simple.Scaffold{
I: info,
T: types.NewType("func(?1, ?1) bool"),
C: func(typ *types.Type) error {
//if typ == nil { // happens within iter
@@ -263,6 +275,7 @@ func init() {
})
RegisterOperator("<", &simple.Scaffold{
I: info,
T: types.NewType("func(?1, ?1) bool"),
C: simple.TypeMatch([]string{
"func(int, int) bool", // less-than
@@ -288,6 +301,7 @@ func init() {
})
RegisterOperator(">", &simple.Scaffold{
I: info,
T: types.NewType("func(?1, ?1) bool"),
C: simple.TypeMatch([]string{
"func(int, int) bool", // greater-than
@@ -313,6 +327,7 @@ func init() {
})
RegisterOperator("<=", &simple.Scaffold{
I: info,
T: types.NewType("func(?1, ?1) bool"),
C: simple.TypeMatch([]string{
"func(int, int) bool", // less-than-equal
@@ -338,6 +353,7 @@ func init() {
})
RegisterOperator(">=", &simple.Scaffold{
I: info,
T: types.NewType("func(?1, ?1) bool"),
C: simple.TypeMatch([]string{
"func(int, int) bool", // greater-than-equal
@@ -366,6 +382,7 @@ func init() {
// TODO: is there a way for the engine to have
// short-circuit operators, and does it matter?
RegisterOperator("and", &simple.Scaffold{
I: info,
T: types.NewType("func(bool, bool) bool"),
F: func(ctx context.Context, input []types.Value) (types.Value, error) {
return &types.BoolValue{
@@ -376,6 +393,7 @@ func init() {
// logical or
RegisterOperator("or", &simple.Scaffold{
I: info,
T: types.NewType("func(bool, bool) bool"),
F: func(ctx context.Context, input []types.Value) (types.Value, error) {
return &types.BoolValue{
@@ -386,6 +404,7 @@ func init() {
// logical not (unary operator)
RegisterOperator("not", &simple.Scaffold{
I: info,
T: types.NewType("func(bool) bool"),
F: func(ctx context.Context, input []types.Value) (types.Value, error) {
return &types.BoolValue{
@@ -396,6 +415,7 @@ func init() {
// pi operator (this is an easter egg to demo a zero arg operator)
RegisterOperator("π", &simple.Scaffold{
I: info,
T: types.NewType("func() float"),
F: func(ctx context.Context, input []types.Value) (types.Value, error) {
return &types.FloatValue{
@@ -645,8 +665,11 @@ func (obj *OperatorFunc) Info() *interfaces.Info {
// avoid an accidental return of unification variables when we should be
// getting them from FuncInfer, and not from here. (During unification!)
return &interfaces.Info{
// XXX: get these from the scaffold
Pure: true,
Memo: false,
Memo: true,
Fast: true,
Spec: true,
Sig: obj.Type, // func kind, which includes operator arg as input
Err: obj.Validate(),
}