Closed nrdxp closed 2 months ago
When two atoms interact or "wechselwirken" (phys. German for "interact"), we encounter two primary paradigms:
Do multiple atoms simply behave harmoniously as in a choreography, or are they (more passively) orchestrated by a single atom?
A typical pattern I've observed is the inference of "glue-atoms" in the presence of a set of atoms. For example:
These glue-atoms often take on the role of the conductor and exhibit the following characteristics:
This approach allows for more flexible and modular system design, enabling better separation of concerns and easier maintenance of complex atom interactions.
Sounds interesting, but I'm not sure I fully follow, could you maybe give an concrete example or something.
Could you give an example of the choreography-based atoms? from how you were explaining it, that seems the "more modular" design, but I believe you were referring to the orchestration by other atoms as the "more flexible and more modular" paradigm.
I'm having in mind the following scenario:
pkg
(or X)dev
(or Y)dev.pkg
and maintained by the dev
team.Now when Atom 1 & Atom 2 are present, the glue-atom could be brought in scope automatically.
Maybe one could say "feature-flagged" on dev
by the presence of pkg
, or in general on Y by the presence of X.
That's interesting, and should be independently possible to do with the feature flags, since they allow depending on optional dependencies, you should already be able to accomplish that in our model (just mark an atom as optional, and refer to it in a feature flag).
More generally though, I was thinking atoms can naturally compose within a mono-repo when "contained" within one another, but not downwards, to avoid implicit dependencies. Here's how it might look:
1. Directory structure example:
.
├── atom
│ ├── dev
│ │ └── mod.nix
│ ├── dev.toml
│ ├── pkg
│ │ ├── atoms
│ │ │ ├── pkg-lib
│ │ │ │ └── mod.nix
│ │ │ ├── pkg-lib.toml
│ │ │ ├── wrapper
│ │ │ │ └── mod.nix
│ │ │ └── wrapper.toml
│ │ ├── mod.nix
│ │ ├── other
│ │ │ └── mod.nix
│ │ └── some
│ │ └── mod.nix
│ └── pkg.toml
└── atom.toml
Composition rules:
Scope examples:
# Top-level atom
{
# Explicitly declared atom dependencies
dev;
pkg;
# Modules
pkg.some;
pkg.other;
# Note: pkg's subatoms (pkg-lib, wrapper) are not visible here
# i.e. they were not declared as explicit dependencies in atom.toml
}
# Child atoms (pkg-lib & wrapper)
{
# atom.pre refers to parent's (pkg) public interface
foo = atom.pre.some;
}
# pkg atom
{
# Can refer to public children of its parent
value = atom.pre.dev;
}
Key points:
This structure provides clear guidelines for code organization based on scoping relationships and dependencies.
Some open questions:
atom.pre.pre.pre
, etc), or just the immediate parent? The latter would probably be easier for vendoring, but perhaps the former is more semantically appropriate?Could you give an example of the choreography-based atoms?
I don't think that's a desirable scenario. But it would look like a bit of scattered conditional logic all over the place that is then triggered by the presence of X or Y, on either side.
It's probably hard to maintain.
After some brainstorming I have this high-level draft:
[atom]
name = "my-awesome-atom"
version = "0.1.0"
[atom.source]
url = "github:this/repo:path/to/this/atom" # canonical url for the source repo, either a full url with schema, or a shorthand, e.g. `github:`
[dependencies]
other-atom = { source = "github:user/repo", tag = "v1.0.0" }
mono-repo-atom = { source = "github:org/mono-repo:path/to/atom" }
local-atom = { path = "../sibling-atom" }
[dev-dependencies]
test-atom = { source = "github:user/test-repo", branch = "main" }
lock format (in toml for conciseness, but would probably be json, or maybe toml is good for human readability?):
version = 1
[[atoms]]
name = "other-atom"
version = "1.0.0"
source = "github:user/repo"
rev = "abcdef123456789"
hash = "sha256-..."
[[atoms]]
name = "mono-repo-atom"
version = "0.2.0"
source = "github:org/mono-repo"
path = "path/to/atom"
rev = "fedcba987654321"
hash = "sha256-..."
[[atoms]]
name = "local-atom"
version = "0.1.0"
source = "github:this/repo"
path = "path/to/this/sibling-atom"
hash = "sha256-..."
[[atoms]]
name = "test-atom"
version = "0.3.0"
source = "github:user/test-repo"
rev = "123abc456def789"
hash = "sha256-..."
Which achieves these high-level goals:
Perhaps we can also have something like:
[atom.registry]
url = "https://my-company-registry.com"
And atom will first search the registry for atom's matching the spec before pulling from source. Also there would be an eka vendor
or similar command to bring all the dependencies into the current repo in a vendor directory.
this issue would be moving to the cli boundary via ekala-project/eka#4, which defines a powerful type system for atom's that is flexible enough to define arbitrary collection types. For clarity, we might call these special higher-level types something besides atoms to make clear that we have left the boundary of singular atomic units, and have moved on to collections of them. For that, maybe we keep the lattice venacular, or something similar
TL;DR
Propose a semantic structure in the manifest for composing atoms, both within mono-repos and remotely, while maintaining isolation and providing an intuitive user experience.
Background
While the low-level wiring for composing atoms is in place, we need to define a clear and intuitive semantic structure in the manifest. This structure should allow for seamless composition of atoms while preserving isolation between them.
Proposed Solution
Introduce the concept of a "lattice" - a higher-order structure that represents a collection of atoms. This lattice structure would provide a framework for composing atoms and managing their interactions.
Key features of the lattice structure:
Implementation Approach
Leverage the existing test directory as a starting point for developing the lattice structure. This approach offers practical benefits:
Benefits
Potential Challenges
Next Steps
Questions for Consideration
Request for Feedback
We welcome input on: