Skip to content

Atomic.fold_update#14725

Open
gasche wants to merge 6 commits intoocaml:trunkfrom
gasche:Atomic.fold_update
Open

Atomic.fold_update#14725
gasche wants to merge 6 commits intoocaml:trunkfrom
gasche:Atomic.fold_update

Conversation

@gasche
Copy link
Copy Markdown
Member

@gasche gasche commented Apr 10, 2026

val fold_update : ('a -> 'acc * 'a) -> 'a Atomic.t -> 'acc

This is a follow-up to #10798 which adds val update : ('a -> 'a) -> 'a Atomic.t -> unit. The motivation is to be able to implement pop in addition to push for the Treiber stack, more generally perform updates that also return a value.

The naming, type variable conventions and tuple order are inspired from fold_map-family functions:

val List.fold_left_map : ('acc -> 'a -> 'acc * 'b) -> 'acc -> 'a list -> 'acc * 'b list

(Edit: I initially had a claim that it is also "somehow consistent" with Seq.unfold : ('b -> ('a * 'b) option) -> 'b -> 'a t, but I don't find this convincing anymore. My best save would be to say that unfold is a dual operation, so it okay if the tuple is in the opposite order?)

Note that (unlike Haskell foldMap) the naming does not follow the pattern that foo_bar can be understood as the composition of foo and bar, with bar applied first to the input. On the other hand, update returns unit so fold ... (update ...) would not be interesting: there is no risk of conflict between two plausible interpretations of the name.

Finally, here are pointers to the comments from #10798 which discussed naming (that PR initially came with a modify_get function with exactly the type and behavior of fold_update, but it proved contentious and was dropped):

@c-cube
Copy link
Copy Markdown
Contributor

c-cube commented Apr 10, 2026

I think this is the wrong name because fold would also have an input accumulator.

modify_get is a lot more accurate I think?

@gasche
Copy link
Copy Markdown
Member Author

gasche commented Apr 10, 2026

Well, Option.fold does not have an input accumulator, or {Result,Either}.fold. You get an "iteration" feeling from List.fold because it is a recursive type, but this is not the case in general. (So one could argue that my use of 'acc is misleading, I'm happy to move to 'b if people prefer.)

I also liked Atomic.modify and Atomic.modify_get, but this ship has sailed, it is now Atomic.update :-)

@dbuenzli
Copy link
Copy Markdown
Contributor

I also liked Atomic.modify and Atomic.modify_get, but this ship has sailed, it is now Atomic.update :-)

And what is wrong with the Atomic.map_update we suggested ?

@gasche
Copy link
Copy Markdown
Member Author

gasche commented Apr 11, 2026

map is for functions that return an instance of the container, so I would expect Atomic.map* to return a _ Atomic.t. On the other hand, fold is for functions that traverse the container to compute a value, which is what is going on here.

@nojb nojb self-assigned this Apr 15, 2026
@eutro
Copy link
Copy Markdown
Contributor

eutro commented Apr 29, 2026

I find the name "fold" in "fold_update" fitting. Here is my justification for the annoying sort of category theorist such as myself: if one considers the "fold" operator for a type to be its recursion scheme, then "fold" for 'a Atomic.t would simply be ('a -> 'acc) -> 'a Atomic.t -> 'acc. In this case, "fold_update" can simply be viewed as a variant of this "fold," but with a "writer effect of type 'a" that automatically gets consumed. Basically, fold_update can be considered a foldM in the writer monad, followed (atomically) by a set operation that consumes the written result.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants