ta0kira / zeolite

Zeolite is a statically-typed, general-purpose programming language.
Apache License 2.0
18 stars 0 forks source link

Create a way to cache frequently-used `TypeInstance` in generated C++. #189

Closed ta0kira closed 2 years ago

ta0kira commented 2 years ago

For example, HashRoot<#k,#v> currently constructs HashNode<#k,#v> every time a new entry is added.

Caching the TypeInstance can currently be faked with an extra param #n requires HashNode<#k,#v> to HashRoot, but that's quite messy.


The two options seem to be:

  1. Create a pragma such as $CacheTypes[HashNode<#k,#v>]$, fabricate a C++ param name (e.g., Param_1, since #1 is an invalid in Zeolite param), then do a reverse substitution when expanding types in @value and @type calls.
  2. Add some sort of language syntax to give the type an alias local to the type, e.g., alias #n <- HashNode<#k,#v>, then give it implicit allows/requires filters so that type checking works.

Both will look roughly the same in C++. The main differences are in the syntax and whether or not the compiler automatically uses the alias.

ta0kira commented 2 years ago

A few problems:

  1. References to the parent type (via #self or explicit expansion) will cause infinite recursion during TypeInstance creation. (Affects both approaches, unless we use lazy init.)
  2. Reference cycles between aliases could cause C++ compiler errors or infinite recursion. (Only affects the alias approach.)

(Now that I think about it, I explicitly decided against type aliases for similar reasons back when this project was just a type system.)

ta0kira commented 2 years ago

I implemented the first approach (pragma and lazy init, didn't commit the changes), but it only improved performance of the bulk-write test below by about ~1%, caching HashNode<#k,#v> in HashRoot.

concrete HashTest {
  @type run () -> ()
}

define HashTest {
  run () {
    HashedMap<Int,Int> map <- HashedMap<Int,Int>.new()
    traverse (`Counter.zeroIndexed` 1000000 -> Int i) {
      \ map.set(i,i)
    }
  }
}

The poor improvement is probably because the cached type is only needed when allocating new objects. It might provide more improvement for @type calls that don't involve any dynamic allocation, including multiple returns.

HashedMap is the only place where this feature would be useful, so I don't think it's worth the added complexity.