]> Bulk Action

# Bulk Action

For any collection V, a simple bulk action on V is a relation (V: :{lists (V: :)}), which subsumes (V| v ←[v] :) and satisfies:

• whenever b has [z, a] as a right value: for any lists f = […, z] ending in z and g = [a, …] starting in a = g(0), if both f and g are right values of b, then so is f&g = […, z, a, …];
• whenever f&g is a right value of b, with f and g non-empty, both f and g are also right values of b; as is every [u, v] for whch b relates u to f and v to g; furthermore, (:b|) relates f&g to each such [u, v], i.e. every left value that b relates to such a [u, v] is also a left value that b relates to f&g. Slogan: b(f&g) = b([b(f), b(g)]); and
• if [] is a right value of b then, for each value e of b([]), whenever [e]&f or f&[e] is a right value of b, (:b|) relates f to it; i.e. every value of b([e]&f) or b(f&[e]) is a value of b(f).

Here & is the list-concatenation binary operator, pronounced and then, for which (: f |n)&g = f&unite;(: g(i) ←i+n :). More generally, a bulk action on V may be a relation (V| :{mappings (V: :)}) which subsumes a simple bulk action on V, subject to some constraints similar to these; I'll say more about that below, but first let's look at things we can say about simple bulk actions.

The preamble says everything of interest about singleton lists (and ensures (V| b :); every member of V is a left value), thereby giving us some fragments for the second rule to apply to, in so far as we have some right values of length (at least) two. The first rule tells us how to build up longer lists from shorter ones; the second how to break apart a list into fragments. The latter also tells us how to get values of the bulk action for a list from those for its fragments. The third rule ensures that [] is handled sensibly, if at all.

The second rule only requires {b(f&g)} to subsume {b([b(f),b(g)])}, so it's entirely possible for b(h) to have some values that are not realised by applying the rule to any decomposition of h as f&g, given that b can be a relation. (Indeed, to be interesting, a bulk action must have some right values of length greater than one; and nothing in the rules tells you how to get from the lists of length one to longer lists.) If b is a mapping, however, its output for any list of length greater than two can be inferred from its outputs for shorter lists, since: the second rule ensures it does have ouputs for non-empty sub-lists; these do imply at least some value for the longer list; and a mapping can only map a given list to a single output. None the less, a bulk action induced entirely from its actions on lists of length two (so every b(:h|n) for n > 2 is a value of b([b(f), b(g)]) for some decomposition of h as f&g) need not be a mapping; distinct decompositions of a list may lead to distinct values for the bulk action on it.

Nothing constrains a bulk action to accept the empty list; only the third rule can apply to it and this explicitly only applies if the bulk action does accept the empty list. It effectively says that, if [] is a right value of b, then the second rule can drop its with f and g non-empty constraint; this constrains b([])'s values to be identities for the binary operator we'll shortly be inferring from b's action on lists of length two. In particular, if b relates both e and i to [] and has [e]&[i] = [e, i] as a right value, it requires e = b([e]) = b([e]&[i]) = b([i]) = i.

Notice that various wordings here take care to condition on b having specific lists of length two as right values. Some bulk actions shall be flat and make these conditions vacuous; every list (V: |n) for positive natural n shall be a right value. Others, however, shall be categoric and constrain the order of entries in the list, just as clean composition imposes conditions on which relations it will compose on the right of each relation.

## Relation to binary operators

When we have a binary operator * closed on some collection V, we can define a simple bulk action bulk(*) for it by specifying bulk(*, [u, v]) = u*v whenever * knows how to combine u with v. The original specification already told us that bulk(*, [u]) = u for each u in V; the first rule above now tells us how to build lists that bulk(*) must have as right values and the second rule tells us what left values bulk(*) relates to them. In effect, we obtain bulk(*, [a, …, z]) = a*…*z. Every closed binary operator has a bulk action in this way, relating its operands to lists of its operands.

Take any list f that is a right value of our bulk action b: for any pair of adjacent entries in f = […, u, v, …], with i entries before the pair, we have f = (: f :i)&[u, v]&(: f(i+2+j) ←j :) to which we can apply the first rule twice to discover that [u, v] is a right value of b. We can then read b([u, v]) as a binary operator, with u and v as operands; sure enough, the rules above ensure that the bulk action derived from this binary operator is subsumed by b. (It might not recover all of b, in some cases: the second rule allows b to relate, to a list, some values that we can't build up from values b relates to parittions of that list.) Furthermore, this derived bulk action has all of the right values of b, so must in fact be equal to b when b is a mapping (since no mapping subsumes any relation but itself that has all the same right values).

### Associativity

If * is associative, its bulk action introduces no ambiguity not directly present in * itself; otherwise, the diverse possible orders of evaluation of a*…*z lead to potentially distinct values, thereby introducing ambiguity. Let's pause to prove that, if (a*b)*c and a*(b*c) always agree, then bulk(*) introduces no ambiguity beyond what * implies, in the sense that choice of how to decompose a list doesn't contribute any ambiguity. This shall be true precisely if, whenever f&g = h&k is a right value of bulk(*), with f, g, h and k all right values of b, every value of bulk(*, f)*bulk(*, g) is a value of bulk(*, h)*bulk(*, k). The third rule above ensures that this is true in the cases with any of f, g, h or k empty, provided it's true for all the cases where they're all non-empty. The case f = [a, b], g = [c] with h = [a], k = [b, c] is given, as is the matching case with f,g and h,k swapped, as these are the specification of associativity; and the other cases where f&g = h&k has length at most three, with f, g, h and k non-empty, are fatuous because they have f = h, g = k.

So write b = bulk(*) and suppose, as inductive hypothesis, that we have some natural n ≥ 3 for which, whenever f&g = h&k is a right value of bulk(*) and has length at most n, with f, g, h and k all non-empty, every value of b(f)*b(g) is a value of b(h)*b(k). When we now look at a right value of b that's f&g = h&k of length 1+n, we can decompose some of our f, g, h, k to learn what values b(f)*b(g) and b(h)*b(k) can take; as each of f, g, h and k, when concatenated with its non-empty partner, gives a list of length 1+n, each must in fact have length at most n so we can apply our inductive hypothesis to its decompositions. Given that f&g = h&k, we know that either h is a prefix of f or k is a suffix of g; both can be true, leading to the fatuous case were f = h and k = g; otherwise, either h is a proper prefix of f or k is a proper suffix of g. If h is a proper prefix of f, then we have f = h&e for some non-empty e with e&g = k; every value of b(f) is obtained from some decomposition of f which, by our inductive hypothesis, has the same values as b(h)*b(e), so every value of b(f)*b(g) is a value of (b(h)*b(e))*b(g); associativity of * tells us each such value is thus a value of b(h)*(b(e)*b(g)) which is necessarily a value of b(h)*b(e&g) = b(h)*b(k), as required. When k is a proper suffix of g we have g = e&k for some e with h&e = f; as before, any value of b(f)*b(g) is a value of b(f)*(b(e)*b(k)) because our inductive hypothesis applies to g = e&k; each such value is then a value of (b(f)*b(e))*b(k) by associativity and thus of b(h)*b(k) as required, again.

Thus our inductive hypothesis for any given length of list suffices to induce the corresponding statement for lists of length greater by one; and we know it to be true for lists of length ≤ 3; so we can induce that choice of decomposition introduces no ambiguity in associative *'s bulk action.

Naturally, this applies equally to any bulk action that's entirely determined by its action on lists of length at most two; it is the bulk action of the binary operator defined by this action on lists of lengths up to two. In particular, if a bulk action's restriction to lists of length at most three is a mapping then the bulk action itself (or, at least, its restriction to lists) is a mapping, unless it relates some values to lists of length greater than two other than those we can infer from its action on decompositions of those lists.

### Other Properties

Thus there is a natural relationship between bulk actions and closed binary operators. In particular (modulo any equivalence context treats as equality), unambiguous combiners correspond exactly with bulk actions that are mappings. We can use the definition of bulk(*) above to define a mapping ({bulk actions}: bulk |{closed binary operators}) for which ({mappings}: bulk :) = (: bulk :{unambiguous combiners}).

It thus makes sense to apply certain terminology of binary operators also to bulk actions: for example, a collection U is closed under a bulk action (V| b :{mappings (V: :)}) precisely if every (U: u :) right value of b has (U: b |{u}), i.e all left values b relates to u are in U – Slogan: every b(U:u:) is in U. I'll describe a bulk action b on V as

• abelian or commutative precisely if, whenever (: f |n) is a right value of b and s is iso (n| |n), i.e. a permutation of n, then f&on;s is also a right value of b, (:b|)-equivalent to f (i.e. b relates all the same left values to f and to f&on;s);
• flat on V precisely if it is in fact (V: |{non-empty lists (V: :)}), i.e. it accepts any non-empty list whose entries are all in V;
• left-cancellable precisely if b(f&g) = b(f&h) implies b(g) = b(h), as right-cancellable precisely if b(f&g) = b(h&g) implies b(f) = b(h) and as cancellable if both of these conditions hold true (this just restates the usual meaning of cancellability applied to b([u, v]) as binary operator); and
• complete precisely if, for every natural n, every proper subset s of n and every (V: f |s) for which some (: |n) right value of b subsumes f, we have (V| b :{lists (V: g |n): g subsumes f}); i.e. specifying (strictly) less than all of the entries in a list leaves enough freedom of choice of the remaining entries that b's outputs over such lists range over all of V.

As mentioned above, a bulk action (V| b :) can only have [] as a right value in so far as each left value, e. that b relates to [] can be removed from each right value of b that it appears in without losing any left values b relates to the given right value. Where any right value of b has two such b([]) adjacent to each other, they're necessarily equal. When * is flat, this ensures b only relates one value to []; and that one value, e, is an identity for * (by considering [e]&[v] and [v]&[e] for arbitrary v in V). In particular, when a flat binary operator does have an identity, its bulk action can be extended to (V| |{list (V: :)}), i.e. to all lists of operands, including [].

### Bestiary

Various common binary operators have standard names for their bulk actions:

sum
bulk(+), the result of adding many things together;
product
bulk(.) with . denoting multiplication; this name may also be used for the other multiplications, bulk(×) and bulk(·), when appropriate.
unite
bulk(&unite;), the union of many relations, specified for any ({relations}: r :) as input, with unite(r) relating x to y precisely if some left value of r relates x t o y; note that empty has no left values, so no left value of it relates anything to anything and unite(empty) is empty;
intersect
bulk(&intersect;), the intersection of many relations; as for unite, with any non-empty ({relations}: r :) as input, with intersect(r) relating x to y precisely if every left value of r relates x to y; may include intersect(empty) as the universal all-relation (it relates every x to every y) in contexts that tolerate such a relation;
compose
bulk(&on;), the composite of many relations.

Each of sum, product and compose may be extended (from the basic bulk action derived from its binary operator) to allow some (infinite) sequences as inputs. Contexts that are comfortable with a universal identity can use it as compose([]); those dealing with only one addition, that has an identity 0, can use 0 as sum([]); likewise those dealing with only one multiplication, with identity 1, can use 1 as product([]). Some contexts (e.g. discussing modules all over a single ringlet) may have several multiplications that all share a common identity; these too can use that identity as product([]).

Some contexts may use flatten for bulk(&), the concatenation of many lists, at least when the entries in these lists are not themselves lists; however, when the entries may be lists, flatten is apt to be used for a recursive version of this, for which flatten(({non-lists}: h :)&[L]&t) = h&flatten(L)&flatten(t) for all lists L, t. (This makes more sense in computer science than here, given that every natural is a list in my formalism, where the computer scientist would probably prefer to distinguish each natural from the monotonic list of earlier naturals.)

Particular contexts may, of course, introduce further names for bulk actions of other combiners.

## Homomorphism

For any class of mathematical structure, a homomorphism is a mapping that preserves the structure. In the case of binary operators, that would be a mapping f from the operands of one operator, *, to those of another, @, for which f(u)@f(v) = f(u*v) whenever u*v is valid. (When each binary operator involved is the composition of some category, a homomorphism of the binary operators is known as a functor between the categories.) As usual, when the homomorphism is from a structure to itself, it's an automorphism.

For the bulk actions, given a list g of *'s operands, this would imply bulk(@, f&on;g) = f(bulk(*, g)); so the proper form for a homomorphism from one bulk action, c on U, to another, b on V, would be a mapping (V: f |U) for which f&on;c = b&on;(: f&on;g ←g :(:c|)), i.e. f(c(s)) = b(f&on;s) for each right value s of c. If we write after = (: (: f&on;g ← g :) ←f :) as the mapping that encodes composition (in contrast to its bulk action, compose), this becomes f&on;c = b&on;after(f). For bulk actions or binary operators with further properties, the notion of homomorphism shall naturally be refined to respect the further properties, too.

Example: reversing relations – encoded by the mapping ({relations}| reverse |{relations}), for which reverse(r) relates x to y precisely if r relates y to x – that we then intersect gets us the same result as reverseing the intersection of the original relations, reverse(r&intersect;s) = reverse(r)&intersect;reverse(s), so reverse is an automorphism of &intersect;, as it likewise is of &unite;, with reverse&on;intersect = intersect&on;after(reverse) and likewise for unite.

For binary operators, we can also define a cohomomorphism to be a mapping for which f(u)@f(v) = f(v*u), swapping the operands compared to a homomorphism; this is a homomorphism from @ to the transpose of *, or from the transpose of @ to *. A homomorphism from a binary operator to its own transpose is likewise a coautomorphism of the binary operator. A cohomomorphism between bulk actions, as before, would thus satisfy f(c(: s |n)) = b(f&on;(: s(i) ←j; i +j +1 = n :)), for each right value s of c, reversing the order of the entries in lists passed to b or c.

Note that reversing relations, in the sense of the function reverse defined above, is a completely different operation from the reversing of lists, in the sense [a, b, c] ↔ [c, b, a], that we need to describe cohomomorphisms of bulk actions. We can define this by list-reverse = (: (: h(j) ←i; i +j +1 = n :) ←(:h|n) :{lists}) as just used; a cohomomorphism (V: f |U) from bulk action c on U to build action b on V would have f&on;c = b&on;after(f)&on;list-reverse; and, since f&on;[a, b, c] = [f(a), f(b), f(c)] has list-reverse [f(c), f(b), f(a)] = f&on;[c, b, a], after(f) commutes with list-reverse here. It is, however, usually more interesting to discuss cohomomorphisms in terms of binary operators; in particular, it's not clear there's a way to give it meaning for anything beyond finite lists or (effectively equivalent to lists) mappings from well-ordered finite sets.

Example: reverse (the mapping on relations) is a coautomorphism of &on;, since reverse(s)&on;reverse(r) relates x to z precisely if there's some y for which r relates z to y and s relates y to x, which arises precisely when reverse(r&on;s) relates x to z. Thus reverse&on;compose = compose&on;after(reverse)&on;list-reverse.

### Universality of concatenation

Now, list concatenation is a binary operator, so we can build its bulk action in the usual way. Concatenation has an identity, [], and is unambiguous, closed and associative so ({lists}| bulk(&) |{lists ({lists}: :)}) is a mapping. (Note that & also allows infinite sequences as right operands, so bulk(&) also has right values that are lists of lists with one sequence appended; it may also be possible to extend it to certain sequences of lists. This involves complications that the preceding statement carefully avoids by restricting to bulk(&)'s action on only lists.)

Repeated application of the second rule of bulk actions can thus be expressed, folding in some applications of the first rule in doing so, by saying that, whenever a bulk action b has bulk(&, f) as a right value, for some list (b: f |n) of b's right values, (: b |) relates this bulk(&, f) to every list (: g |n) that b&on;f subsumes; i.e. each such g is a right value of b and every left value that b relates to g, b also relates to bulk(&, f). Slogan: b(a&…&z) = b([b(a), …, b(z)]).

Expressed in terms of bulk(&), applied to a list f = [a, …, z] of b's right operands, this last slogan says b(bulk(&, f)) = b(b&on;f), i.e. b&on;bulk(&) = b&on;(: after(b) :{lists (b: :)}), where after(b) = (: b&on;f ←f :) as above. That's only strictly true when b is unambiguous (so that b&on;f is a list, rather than a relation whose subsumed lists we need to use as right values of b); but, in this case, b itself serves as a homomorphism from (b: bulk(&) :) to b itself. Thus bulk(&) is the model for all bulk actions that are mappings; each is a homomorphic image of some restriction of bulk(&).

When the bulk action b of a binary operator * is a mapping, we can thus construe b as a homomorphism from a restriction of & to *, since b(f&g) = b(f)*b(g); that is, b maps the action of & on lists (V: :), specifically on right values of b, onto the action of * on V, preserving the binary operator structure. Thus every unambiguous combiner is a homomorphic image of some restriction of &; in this sense, & is universal among unambiguous combiners; all others can be represented as images of its restrictions, via their own bulk actions.

## Repeating a single value

Given a bulk action b on a collection V, for every v in V and natural n there is a unique list ({v}: |n), with n entries, each of which is v. When this is a right value of b, we can form b({v}: |n), the result of combining n copies of v using b. When b is the summation associated with an addition, we can think of this as the result of multiplying v by n; when b is the product associated with some multiplication, we can think of it as taking the n-th power of v. In any case, repetition of a single value can teach us plenty about a given bulk action, or the binary operator from which one is derived. For the purposes of the present discussion, I'll refer to this operation as scaling and define

• scale = (: (: (: b({v}: |n) ←v :V) ←n :{naturals}) ←(V| b :{lists (V: :)}) :{bulk actions})

When b is flat on V, we have ({relations (V: |V)}: scale(b) |{positive naturals}); if b also has [] as a right value, it is also (: scale(b) |{naturals}), with ({b([])}: scale(b, 0) |V). When b is a mapping, so is each of its scalings. When we have a binary operator, we can naturally use its bulk action to define scalings in the same way; I'll then describe each scale(bulk(*), n) as a scaling of *.

Note that, in order to specify what a bulk action is, I needed arithmetic on the naturals; which I derived from a study of repetition that defined a mapping called repeat which can easily be seen to be just scale(bulk(&on;)), in present terms; we could, indeed, define scalings for binary operators inductively, as I do for repeat, rather than via bulk actions. Doing so was necessary for repeat, in order to build the arithmetic I need here; but there is little point in repeating all of that inductive work when I can exploit the results derived from doing it for repeat to make the present discussion more straightforward.

When we have a homomorphism (V: f |U) of bulk actions b on V and c on U, if we can scale in U (i.e. every ({u}: |n), for u in U and natural n, is a right value of c) then we can at least scale f's left values in V, with scale(b, n, f(v)) = b({f(v)}: |n) = b(f&on;({v}: |n)) = f(c({v}: |n)) = f(scale(c, n, v)); so scale(b, n)&on;f = f&on;scale(c, n). Thus scaling commutes with homomorphisms, albeit changing which bulk action is used between composing before and after the homomorphism. In particular, when b = c, its scalings truly commute with its automorphisms.

### Homomorphic when abelian

For any list f, consider scale(bulk(&), n, f) for some natural n; this is just n copies of f concatenated together; and there is some permutation of this that turns it into a list that has n repeats of each f(i), followed by the same for f(i+1) and so on; this is just bulk(&, (: ({f(i)} |n) ←i :)). When the unpermuted result is a right value of some bulk action b, we have b(bulk(&, g)) = b(b&on;g), so

b(scale(bulk(&), n, f))
= b(bulk(&, ({f}: |n)))
= b(b&on;({f}: |n))
= b({b(f)}: |n)
= scale(b, n, b(f)).

When b is commutative, the permuted version gives us

b(scale(bulk(&), n, f))
= b(bulk(&, (: ({f(i)} |n) ←i :)))
= b(b&on;(: ({f(i)}: |n) ←i :))
= b(: b({f(i)}: |n) ←i :)
= b(scale(b, n)&on;f),

so we can infer scale(b, n, b(f)) = b(scale(b, n)&on;f), which makes scale(b, n) a homomorphism from b to itself. Consequently, scalings of a commutative bulk action are homomorphisms from that bulk action to itself; and when a binary operator is abelian, its scalings are all homomorphisms from the binary operator to itself.

### Cyclic or endless

Given a bulk action b on V for which (V: scale(n) |V) for each positive natural n, for each v in V we can define a sequence (: scale(b, n, v) ←n :{naturals}); when V is finite, this can't be monic. If any two distinct naturals give the same scale(b, n, v) then they are necessarily n and n+p for some naturals n and p, with p positive; which makes scale(b, p, v) an identity for at least scale(b, n, v), under b's associated binary operator. When b is flat and cancellable, this suffices to make scale(b, p, v) an identity and thus a candidate value for b([]) = scale(b, 0, v). In any case, we can infer that scale(b, m+i.p, v) = scale(b, m, v) for every natural i and every natural m ≥ n; so, if (: scale(b, n, v) ←n :{naturals}) isn't monic it's cyclic, at least beyond some natural. It is thus – even when b isn't cancellable and regardless of whether V is finite – interesting to consider whether such sequences are monic and, if not, what their period is.

For a bulk action b on V, a value v for which (: scale(b, n, v) ←n :{naturals}) is monic is said to have order 0 (serving, in effect, as a token for infinity); otherwise, the given sequence is not monic and the order of v is intersect({positive natural p: for some natural n, scale(b, n, v) = scale(b, n+p, v)}); this is the least such p and, for each n on which it gives scale(b, n, v) = scale(b, n+p, v), we can infer scale(b, m, v) = scale(b, m+i.p, v) for every natural m ≥ n and natural i. If any v in V has order 0, then (V| b :{lists (V: :)}) is said to have characteristic 0 (again representing infinity); otherwise, its characteristic is unite(: order |V), the maximum of the orders of its values.

When b is the bulk action of a binary operator, the binary operator may equally be said to have this characteristic; the orders of values are derived via the bulk action as above. Where the binary operator or bulk action is taken as given, the given characteristic may be described as being the characteristic of V, particularly when distinguishing the characteristics of (U| b :{lists (U: :)}) for assorted U (typically closed under b), with b fixed. Where several binary operators or bulk actions (not counting a binary operator and a bulk action as distinct if they're associated with one another, of course) are involved, it may be appropriate to make clear which is implicated when talking about a value's order, e.g. the *-order or additive order of the value, and likewise for characteristics. (When discussing variants on arithmetic, such as ring theory, it is usual for order and characteristic to be tacitly additive, ignoring multiplication; but, when discussing homomorphisms it is usual for them to be tacitly associated with composition, which such contexts may treat as a multiplication, even for homomorphisms of a vector space, where addition and (other) multiplications are also involved.)

When a binary operator has an identity, its order is necessarily 1. The same holds for any value v for which v*v = v; such a value is described as idempotent (for the given binary operator). For &unite; and &intersect;, every relation is idempotent; consequently, these two binary operators have characteristic 1. For an associative binary operator – e.g. &on;, composition – every left or right identity is idempotent. When no binary operator is specified, or implicit from context, idempotence is usually with respect to composition: any mapping whose outputs are all fixed points is idempotent.

## Extended bulk actions

A simple bulk action is specified (V| b :{lists (V: :)}) and is thus inescapably also defined (V| :{mappings (V: :)}), since every list is a mapping. Let us now look at what other mappings (V: :) we can extend it to.

Whenever a finite set has a natural full order on its elements, we can represent this by simply listing its members in order; if this list is U, then the set is just (|U:) and U is iso between this set and some natural n. We can then interpret any (: f |U) as the list f&on;U, which has exactly the same outputs, repeating any of them that are repeated exactly as often as f does and preserving U's ordering of those entries. We can thus extend our bulk action to accept any mapping (V: |U) for any ordered finite set (|U:). Furthermore, for any subset of (|U:), we have a natural ordering on the sub-set induced by that on (|U:), which lets us represent the sub-set as (|S:) for some list S in the same way; which allows us to extend our bulk action to any mapping (V: :U), not just the ones (V: |U), for any ordered finite set (|U:). In particular, since each natural is an ordered finite set, we can extend the action of any simple bulk action on V to at least (V| :{mappings (V: :n): n is natural}). The extension to other inputs (V: :U) with a natural order on finite (|U:) follows naturally but contributes relatively little, precisely because it can always be represented by composition with an in-order list of the finite set's members.

When the bulk action on V is commutative, we can drop the constraint that its inputs must be (V: :U) for some ordered (|U:) and can naturally extend it to accept every mapping (V: :n) with n finite. We do this by composing such mappings after any iso (n| |m) with m natural and noting that any other choice of which such iso we use is equivalent to composing our given iso after some permutation of m (or before some permutation of n), which makes no difference to the answer. We can thus extend such a bulk action to {mappings (V: :n): n is finite}. We could also do this for any bulk action, even if it's not commutative, but it'd introduce ambiguity by relating, to each mapping, every value that any ordering of the finite collection would produce.

In some (even some non-abelian) cases, it is also possible to extend a bulk action on V to at least some of {sequences (V: :)}. Of course, if there is a b([]) in V for which b([b([]), v]) = v = b([v, b([])]) for each v in V, we can always allow sequences as right values of b as long as all but finitely many entries in each sequence is b([]); but this is seldom interesting. The interesting case of extensions to sequences comes when we have some notion of convergence in V, which is beyond the scope of the present discussion.

There are also cases, such as unite = bulk(&unite;) and intersect = bulk(&intersect;), where we can extend a bulk action on V to cover arbitrary relations (V: :) by allowing the action to combine all left values of the input relation. In both examples, V = {relations} and the feasibility of this extension is closely related to the associated binary operator being insensitive to repetition and, when one operand subsumes another, one of them makes the other irrelevant (one way round for &unite;, the other for &intersect;). These are, of necessity, treated particularly by the definitions of the relevant bulk actions, so there is little more to say about them here.

For a commutative bulk action, b, the requirement for an extension can be stated as: for every right value (V: f |U) of b and every partition U = unite(h) for some ({non-empty subsets of U}: h |n) for which every (V: |n) is a right value of b; each restriction (: f :h(i)) is a right value of b, as is H = b&on;(: (: f :h(i)) ←i |n), and (:b|) relates f to H (i.e. every left value that b relates to H, b also relates to f). Slogan: b(: f :A&unite;…&unite;Z) = b([b(:f:A), …, b(:f:Z)]) whenever (: f :A), …, (: f :Z) are non-empty and disjoint.

Consequently, integration over a continuum, e.g. the reals, doesn't naturally fit into the bulk action framework, despite large similarities, because it would be bulk(+) = sum that we're extending and, when we partition a real interval U into open intervals that abut one another and the discrete values that make up the rest of U, say as U = {a}&unite;P&unite;{b}&unite;Q, we get sum(:f|U) = sum([sum(:f|{a}), sum(:f|P), sum(:f|{b}), sum(:f|Q)]) = sum([f(a), sum(:f|P), f(b), sum(:f|Q)]) = f(a)+sum(:f|P)+f(b)+sum(:f|Q). The requirement that sum subsumes (: v ←[v] |{lists (V: |1)}) has, via the natural extension to allow mappings from any finite set (not just lists), turned into a requirement that sum(: f |{a}) = f(a) and likewise for b, which forced us to not ignore f(a) and f(b), where integral(:f|U) would ignore f's values on any countable subset of reals, including {a, b}. Integration ignores the zero-width single points in such a partition, but an extension of summation cannot, so it is necessary to formalise integration separately from any extension to sum.

For the non-commutative case, applied to a (V: f |U), we need an ordering on U, the partition U = unite(h) needs an ordering on (h: x←x :) and we need h, understood via this, to respect the ordering on U – something like: right values i < j of h must have p < q for each p in h(i) and q in h(j) – all of which gets rather complicated, which makes this case far less interesting to explore. If I ever have cause to extend such a bulk action, I'll deal with each such extension individually !

Written by Eddy.