facebook / starlark-rust

A Rust implementation of the Starlark language

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

"Strict" equality testing of Values from the API?

spinda opened this issue · comments

Hi! Thanks for picking up the maintenance of Starlark's Rust implementation. I'm excited to see where this tech goes!

Is it possible to do "strict" equality testing of Values from the starlark-rust API, or would it be possible to expose that capability? I'm referring to something akin to JavaScript's === operator, or the default behavior of == in Python. I'd like to test primitive values (bool, int, string...) by value, but reference values (tuple, list, dict, record...) by reference, i.e., checking their identity only. Currently Value::equals tests the latter by value (except for functions), for example, walking over all elements in a list.

My use case is a system I'm fiddling with which will contain a large number of small, interconnected Starlark programs, where the outputs from one will be fed in as inputs to another. This graph of programs will be executed multiple times with changing sets of initial inputs. I'd like to take advantage of Starlark's determinism to memoize the execution of individual programs in the graph by their inputs, returning cached output values if the inputs given are unchanged from the previous execution. This requires equality-testing the inputs against previous values, which could become unintentionally expensive if it involves recursively descending into lists, dicts, etc.

I should mention that I can contribute code for this myself if it would be accepted!

Hi @spinda, thanks for the questions, sounds like an interesting use case. For Starlark, the behaviour of == is defined by the spec, https://github.com/bazelbuild/starlark/blob/master/spec.md#data-types. That spec also defines all the operators that are available.

With Starlark you can define your own functions in Rust though, e.g.

#[starlark_module]
fn custom_eq(builder: &mut GlobalsBuilder) {
    fn eq(x: Value, y: Value) -> bool {
        if let Some(x) = x.unpack_int() {
            Ok(Some(x) == y.unpack_int())
        }
        ... special cases for all the types you care about ...
        Ok(x.ptr_eq(y))
    }
}

The function ptr_eq on Value isn't actually exposed. But we could do so, albeit with some health warnings (it lets you observe things like whether ints are stored on the heap, or values have been collapsed, which is an implementation detail that might change). In the short term, just transmuting a Value to a usize is probably sufficient.

If you did your own eq function, would that be enough?

Yep, exposing ptr_eq in some form would be great! I just need some way to observe identity equality of Values, and then everything else falls into place.

Exposed in 59b57ea. Let me know if you need anything else, or if a release would make it easier.

Fantastic, thanks very much!