10

In the higher order function fold/reduce what is the name, if any, of the functional argument?

I am working on a monadic tabular processing library where rows are folded to produce simple analyses (like finding the minimum, maximum, average of a column). I am therefore looking for a sound name for the argument of the fold function and any name which is well-established in the ML community (or Haskell or Common Lisp as second and third candidates) would be interesting.

A name like f is common in the description of fold functions, it is however undescriptive and a noun would be better suited.

  • 10
    There isn't one. In fact, there isn't even a universally used name for catamorphisms (fold) – jozefg Dec 13 '13 at 16:26
  • 2
    If this is a question for a school assignment or exam, you need to get the answer from the teacher or textbook, not from us. He may be calling it something different. What is taught in a classroom is not always what is commonly used in the real world. – Robert Harvey Dec 13 '13 at 16:39
  • 7
    @RobertHarvey This is not an exam question(?) or anything alike. As a programmer, I think that choosing good names for programming objects (types, variables etc.) is very important. And if somethings has a well-known name, then there is no reason not to use it—aside from ignorance, and that is what questions are for. – user40989 Dec 16 '13 at 8:22
  • 9
    @Izkata No, that's incorrect. A higher order function is a function which takes a function. fold is a higher order function. The argument to fold is just a that, an argument – jozefg Dec 20 '13 at 3:21
  • 1
    @jozefg That as a response to the second part of your comment. As for the first part (and the question), see my post on Meta. They're called procedural parameters. – Izkata Dec 20 '13 at 3:34
8

I don't know if there's a single answer to this, as @jozefg mentioned. And so purely out of speculation, here's one possible explanation:

I suspect the reason is because the type -- (a -> b -> b) in Haskell's foldr, for example -- is more meaningful than any name one could come up with. Since the a and b type parameters could be anything, the function could do pretty much anything as well. So you're left with names like function, combiner, morphism, and argument, which aren't especially meaningful either. Might as well use a short, non-distracting name.

Another example is the id :: a -> a function: what should you call its argument? Again, I think id's type is more descriptive than its argument name.

However, I agree with you -- it seems like there should be a common name, perhaps in mathematics. I hope somebody can correct me on this.


Some examples of its name in real code:

In Haskell's libraries, it's mostly called f (and sometimes operator in the comments):

class Foldable t where
    -- | Map each element of the structure to a monoid,
    -- and combine the results.
    foldMap :: Monoid m => (a -> m) -> t a -> m
    foldMap f = foldr (mappend . f) mempty

    -- | Right-associative fold of a structure.
    --
    -- @'foldr' f z = 'Prelude.foldr' f z . 'toList'@
    foldr :: (a -> b -> b) -> b -> t a -> b
    foldr f z t = appEndo (foldMap (Endo . f) t) z

    -- | Right-associative fold of a structure, 
    -- but with strict application of the operator.
    foldr' :: (a -> b -> b) -> b -> t a -> b
    foldr' f z0 xs = foldl f' id xs z0
      where f' k x z = k $! f x z

    -- | Left-associative fold of a structure.
    --
    -- @'foldl' f z = 'Prelude.foldl' f z . 'toList'@
    foldl :: (a -> b -> a) -> a -> t b -> a
    foldl f z t = appEndo (getDual (foldMap (Dual . Endo . flip f) t)) z

    -- | Left-associative fold of a structure.
    -- but with strict application of the operator.
    --
    -- @'foldl' f z = 'List.foldl'' f z . 'toList'@
    foldl' :: (a -> b -> a) -> a -> t b -> a
    foldl' f z0 xs = foldr f' id xs z0
      where f' x k z = k $! f z x

    -- | A variant of 'foldr' that has no base case,
    -- and thus may only be applied to non-empty structures.
    --
    -- @'foldr1' f = 'Prelude.foldr1' f . 'toList'@
    foldr1 :: (a -> a -> a) -> t a -> a
    foldr1 f xs = fromMaybe (error "foldr1: empty structure")
                    (foldr mf Nothing xs)
      where
        mf x Nothing = Just x
        mf x (Just y) = Just (f x y)

    -- | A variant of 'foldl' that has no base case,
    -- and thus may only be applied to non-empty structures.
    --
    -- @'foldl1' f = 'Prelude.foldl1' f . 'toList'@
    foldl1 :: (a -> a -> a) -> t a -> a
    foldl1 f xs = fromMaybe (error "foldl1: empty structure")
                    (foldl mf Nothing xs)
      where
        mf Nothing y = Just y
        mf (Just x) y = Just (f x y)

-- instances for Prelude types

instance Foldable Maybe where
    foldr _ z Nothing = z
    foldr f z (Just x) = f x z

    foldl _ z Nothing = z
    foldl f z (Just x) = f z x

instance Ix i => Foldable (Array i) where
    foldr f z = Prelude.foldr f z . elems
    foldl f z = Prelude.foldl f z . elems
    foldr1 f = Prelude.foldr1 f . elems
    foldl1 f = Prelude.foldl1 f . elems

-- | Monadic fold over the elements of a structure,
-- associating to the right, i.e. from right to left.
foldrM :: (Foldable t, Monad m) => (a -> b -> m b) -> b -> t a -> m b
foldrM f z0 xs = foldl f' return xs z0
  where f' k x z = f x z >>= k

-- | Monadic fold over the elements of a structure,
-- associating to the left, i.e. from left to right.
foldlM :: (Foldable t, Monad m) => (a -> b -> m a) -> a -> t b -> m a
foldlM f z0 xs = foldr f' return xs z0
  where f' x k z = f z x >>= k

-- | Map each element of a structure to an action, evaluate
-- these actions from left to right, and ignore the results.
traverse_ :: (Foldable t, Applicative f) => (a -> f b) -> t a -> f ()
traverse_ f = foldr ((*>) . f) (pure ())

-- | Map each element of a structure to a monadic action, evaluate
-- these actions from left to right, and ignore the results.
mapM_ :: (Foldable t, Monad m) => (a -> m b) -> t a -> m ()
mapM_ f = foldr ((>>) . f) (return ())

It's also called f in Clojure:

(def
    ^{:arglists '([f coll] [f val coll])
      :doc "f should be a function of 2 arguments. If val is not supplied,
  returns the result of applying f to the first 2 items in coll, then
  applying f to that result and the 3rd item, etc. If coll contains no
  items, f must accept no arguments as well, and reduce returns the
  result of calling f with no arguments.  If coll has only 1 item, it
  is returned and f is not called.  If val is supplied, returns the
  result of applying f to val and the first item in coll, then
  applying f to that result and the 2nd item, etc. If coll contains no
  items, returns val and f is not called."
      :added "1.0"}    
    reduce
     (fn r
       ([f coll]
             (let [s (seq coll)]
               (if s
                 (r f (first s) (next s))
                 (f))))
       ([f val coll]
          (let [s (seq coll)]
            (if s
              (if (chunked-seq? s)
                (recur f 
                       (.reduce (chunk-first s) f val)
                       (chunk-next s))
                (recur f (f val (first s)) (next s)))
              val)))))
6

I disagree with the idea that f is a bad name for the function argument of fold. The reason we want descriptive names in programming is so we know what the name describes, and the name f is commonly used in mathematics (and functional programming languages) for a function (as are g and h).

We know almost nothing about f (by design, since if we could be more specific about it, we could not use fold on as many things), and the name f tells us everything we need to know, except that it is a function of two arguments. The name f is a lot like the pronoun 'it' in English -- it's a placeholder which could mean almost anything. We don't know what 'it' is until we use it in a sentence, and we don't know what f is until we call fold.

When naming things, we want to get as much information out of the name as possible, and we prefer the name to be short (if we can get that without sacrificing important information). Here, we have a very short name that tells us nearly everything we know about it. I don't think it can be improved on.

In imperative programming, i is used as a loop counter (as are j and k, if we need more); in functional programming, f is used for a function argument in higher-order functions (as are g and h, if we need more). I don't have enough experience with functional languages to be sure that the f/g/h convention is as well established as the i/j/k convention, but if it isn't, I think it should be (it definitely is established in mathematics).

3

The most common name for this that I've heard other than the pronoun f would be binary operation or binary function - the problem with being more specific than that is it could do literally anything, and that's why people stick to the type signature a -> b -> a (or a -> b -> b if it's right fold).

So I propose that you do exactly that, stick to the type signature, luckily that specific type signature does have a name: binary function, just like a -> a is a unary operation, and a -> b is a unary function, you can call a -> a -> a a binary operation and a -> b -> c a binary function.

  • 1
    To me binary operation would more mean a -> a -> a and binary function would stand for a -> b -> c… the truth surely lies in between. :-) – user40989 Jan 31 '14 at 17:42
  • @user40989 good call, corrected. – Jimmy Hoffa Jan 31 '14 at 19:47
1

The function you ask for is sometimes referred to as the "combining function" (see e.g. the HaskellWiki page on Fold), but this is not sufficiently common to call it standard terminology.

protected by gnat Oct 5 '16 at 10:50

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.