openlang

Empty reductions return the identity

Reduce over an empty vector. APL answers.

      +/⍬
0
      ×/⍬
1
      ∧/⍬
1

Each primitive carries its identity. Zero for plus. One for times and for and. ⌈/⍬ gives the most negative number the interpreter has. No special case in the caller. No length check before the reduction.

A dfn gets nothing of the sort. {⍺+⍵}/⍬ is a DOMAIN ERROR in Dyalog. Same arithmetic, no identity, because the interpreter can't know one for an arbitrary function.

Most languages I read decide this per function. Sum returns zero, max throws or returns an empty option, product sometimes isn't there at all. In APL it is one rule. Anyone else bothered by how many languages turn it into a table?

The conversation continues 3

Follow the reasoning
  1. Permalink

    ⌈/⍬ is the one I'd argue with. The most negative float is a real value, so a caller that forgets the empty case gets a plausible maximum and keeps going. Rust splits it the other way.

    let empty: [i64; 0] = [];
    let s: i64 = empty.iter().sum();
    let p: i64 = empty.iter().product();
    println!("{s} {p} {:?}", empty.iter().max());

    That prints 0 1 None. Sum and product have identities and return them. i64::MIN would work as an identity for max, and Rust still returns an Option, so the caller has to say what empty means. Floats don't even get max on the iterator, since f64 isn't Ord. You write fold(f64::NEG_INFINITY, f64::max) and pick the APL answer by hand, which at least puts the choice at the call site.

  2. Permalink

    It's the identity. ⌈/x,⌈/⍬ equals ⌈/x for any x. Concatenate, reduce, same answer. That is what lets a reduction compose over pieces. An Option breaks it. Every partial result needs unwrapping before the next step.

    Forgetting the empty case is real. It comes from a caller who never thought empty could happen. There a None turns into an unwrap, which crashes instead of lying, and I grant that beats a quiet wrong maximum.

Other sides of the same question

  1. age>=18=>allow and the Racket reader

    Rule authors want to write age>=18=>allow, no spaces, no parentheses. Racket's default reader may hand that over as one symbo…

    Replies 8racket
  2. Returning from a Common Lisp handler means declining

    Neat abbreviation. I'd still like to see what it's agreeing to. The wrapper macro installs a handler around a parser, and the…

  3. foldl composes fine and leaks anyway

    foldl (+) 0 over a long list is the smallest space leak I know, and it's a composition problem more than an arithmetic one. E…

    Replies 5haskell