Skip to content

Conversation

Christopher-Chianelli
Copy link
Contributor

  • Introduces ConstraintFactory.staticData(Uni), which creates an independent NodeNetwork from the provided Uni stream.

  • Introduces TupleSourceRoot that represent top level tuple producers (the ForEach nodes now implement this, as well as a new StaticDataUniNode).

  • The StaticDataUniNode caches the results of its internal NodeNetwork and maps the tuples to new tuples compatiable with the external NodeNetwork

  • Inserts/retracts invalidate the cache and cause the output tuples to be updated.

  • Updates uses the cache and do not notify the internal NodeNetwork.

@triceo
Copy link
Collaborator

triceo commented Oct 6, 2025

This is surprisingly simple! Some high-level comments; leaving naming out of the picture for now:

Introduces ConstraintFactory.staticData(Uni), which creates an independent NodeNetwork from the provided Uni stream.

I'm not a big fan of this. The programming model doesn't look very nice IMO.

factory.staticData(factory.forEach()...)
    .filter(...)

Something like this would be more stream-y:

factory.forEach().
    ...
    .asStatic()
    .filter(...)

This brings its own problems. But both proposals share the same issue - chaining static streams. Nothing (other than run-time fail-fasts) prevents you from doing staticData(staticData(...).filter(...)). IMO we should decide (and enforce) that a static stream can only be built once, and everything after that is dynamic. And ideally, this would happen at type level, not at runtime.

This was the main idea of filterStatic() - because we can actually guarantee, at type level, that once any "non-static" method is called, a "static" method can never be called again. (The reverse doesn't work, unless we want to duplicate the entire stream API, which we don't.) IMO we shouldn't rule out filterStatic() + joinStatic() - under the hood, it can be implemented like in this PR, but the public API IMO need not offer this absolute flexibility to turn anything into static data.

@triceo
Copy link
Collaborator

triceo commented Oct 6, 2025

To bring an alternative API idea:

 factory.forEachXYZ(Something.class, filter)

This would produce a StaticUniStream which extends UniStream, and only adds one method - joinXYZ(); this join only allows to join other static streams and returns StaticBiStream - allowing possibly for a static bi join (and similarly for tri).

In essence, this brings a very simple, clear API:

  • We support static filters and joins (maybe also ifExists, which is essentially a join).
  • Static operations come first.
  • Once you go dynamic, you can never go back.
  • This is enforced at the type level, preventing impossible situations from being modelled.
  • Node-sharing is trivial; nothing changes in the status quo.

It does have a downside - it doesn't allow arbitrary static streams, such as groupBy, map, flatten etc. Right now, nobody is asking for it, and I think it is a decent trade-off to get the benefits; but we can possibly ask other people for their opinion on this.

Copy link
Collaborator

@triceo triceo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Submitting more comments as I get familiar with the changes.

Copy link
Collaborator

@triceo triceo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love this! Small comments.

Please submit a second PR with docs updates. I'll ask Tom to review that one.

Previously, forEach node sharing did not takie into account
retrival semantics, so each of these return the same stream
despite having different retrival semantics affecting joins:

- constraintFactory.forEachUnfiltered(Fact.class)
- constraintFactory.from(Fact.class)
- precomputeFactory.forEachUnfiltered(Fact.class)

Now retrival semantics are considered when node sharing.
Copy link

@triceo triceo merged commit dfd8e34 into TimefoldAI:main Oct 18, 2025
34 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add filterStatic (vs. filter) to ConstraintStreams

2 participants