Skip to content

Should EdgeSpec take a list of path items, like their Path? #2

Description

@borisdev

Their edge is an ordered list of markers; ours is a typed edge with subclasses. Theirs composes, ours does not.

# theirs — measured against 2.35.1, runs and returns ['ADA','GRACE']
g.edge_from(g.start_node).map().transform(lambda ctx: ctx.inputs["name"]).to(shout)

# ours — not expressible. MapEdgeSpec and TransformEdgeSpec are separate types,
# and no edge is both.

What it would look like

EdgeSpec(source=a, target=b, carries=papers,
         path=[Map(delivers=paper), Transform(delivers=pmid, apply=take_pmid)])

One EdgeSpec, no subclasses, and delivers becomes the output of the last item.

Three costs

  1. edges stops being flat. Today it is a tuple of arrows you scan in one pass; this nests a list inside a tuple, in the most-read part of a design.
  2. check_variables becomes a small interpreter. It would thread a type through each item rather than comparing two ends. Doable — that is what their type parameter does — and it would let intermediate steps be checked, but it is no longer a two-line check.
  3. Bindable identity per item. A Transform item that a strategy binds needs its own identity for varies() and check_bindings, so path items become a second kind of bindable thing alongside nodes and transform edges.

And an argument for what we have

Their Path is partly an artefact of the fluent builder — the field is literally called working_items, an accumulator left behind by method chaining. Our declaration is a literal, and a list is the natural shape of the former, not the latter.

More substantially: MapEdgeSpec becomes a real node at build time and TransformEdgeSpec does not.

# _flatten_paths, graph_builder.py
assert not isinstance(item, MapMarker | BroadcastMarker), 'These should be removed during Graph building'

Map and broadcast are rewritten into Fork nodes before the executor ever runs; transform and label survive in the Path and are walked per completion. So the two are not the same kind of thing, and our separate types say so where a uniform list hides it until _flatten_paths.

Status

No caller. Nothing in eleven ladder rungs or in nobsmed's six arms needs map-then-transform on one wire.

Trigger to build it: the first design that wants to fan out a collection and reshape each item before it lands — e.g. carries=list[Paper], delivers=pmid — where the alternative is a step that exists only to unwrap.

parity.py now records the composition gap; the table was feature-by-feature and read as though map ✓ + transform ✓ meant both together.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions