Is there a name for a function that takes a piece of data and a list of functions and applies each function to the result of the last one?
Asked Answered
G

7

15

Clojure has a macro, ->, which takes a piece of data and a bunch of functions, applies the data to the first function, and then applies the result of that to the next one, the result of that to the third one, and so on, finally giving you back the result of the last application.

I quite like this because instead of having to write functions backwards from the order in which they are applied, like so: (pseudo-code follows)

floor (square.root (x))

you can write them in the order that the data flows through them:

-> x (square.root, floor)

My question is, is there a standard name for this function in functional languages, in the way that map, reduce, and filter have standard names? The Clojure docs describe it as 'threading the data through the functions', but I couldn't find anything on googling the word thread. I wrote a simple version of it in Haskell:

thread :: a -> [(a -> a)] -> a
thread x []     = x
thread x (f:fs) = thread (f x) fs

and searched for a -> [(a -> a)] -> a on Hoogle, but that didn't come up with anything either.

While researching for this question I also gleaned that you can do a very similar thing using the function composition operators from Control.Arrow in Haskell, like so:

($2) (sin >>> cos >>> tan)

whereas using the dedicated higher-order thread function you would write:

thread 2 [sin, cos, tan]

Is it perhaps the case that the first formulation suffices for practical usage?

Guan answered 3/3, 2012 at 11:37 Comment(3)
For Haskell, see also Endo a in Data.Monoid. By analogy to Sum/sum, All/all, etc, you might think there would be a endo :: [a -> a] -> a -> a that does what you want, but there is not.Tee
the desired "endo" could easily be written up, though: thread = flip $ appEndo . mconcat . map Endo . reverse. Then: thread 2 [(+1), (*3)] -> 9. (This has the argument order as in the question rather than as in the previous comment.)Tiffany
If Endo is a monoid then presumably mconcat will fold the list into a single function, which can then be applied.Piffle
B
8

The name you're looking for is function composition.

The difference between the threading macro and comp comes from the former leveraging Clojure homoiconicity, while the latter is more close to the mathematical definition of composition.

In facts, you can transform a threading macro call to a comp call if you create one function of one argument per step, and reverse the order of the functions:

(defn sin [n] (Math/sin n))
(defn cos [n] (Math/cos n))

(-> 1 sin cos sin cos cos) ; 0.6858966217219662
((comp cos cos sin cos sin) 1) ; 0.6858966217219662
Betsey answered 3/3, 2012 at 11:58 Comment(2)
FYI, your examples have different combinations of sin/cos.Luckett
If the functions that you use for composition receive more than a single argument, it becomes very cumbersome to write the composition with comp while it is still elegant with ->Mendenhall
L
4

I think you can use foldl for this, using appropriate arguments:

-- functions for testing
f1 x = x+1
f2 x = x*2
f3 x = x*x
-- here comes the line you're interested in
foldl (\x y -> y x) 2 [f1, f2, f3]

The arguments are as follows:

  • (\x y -> y x) is a function taking a function y and applying it to the argument x. y will then be substituted by each function from the list.
  • 2 is the initial argument you want to give to the functions.
  • [f1, f2, f3] is the list of functions.

Using these arguments, foldl computes the following: f3(f2(f1(2))).

Lanciform answered 3/3, 2012 at 11:55 Comment(2)
(\x y -> y x) can also be written (flip ($)) or (more cryptically) (flip id)Marvelofperu
Or perhaps even foldr (>>>) id (foldr (flip (.)) id if you don't want to use Control.Category).Aggregation
A
2

Given that >>> is similar to bash shell's pipeline operator rooted from Unix, I would call it pipeline sequence combinator.

Apterous answered 3/3, 2012 at 12:4 Comment(0)
S
2

This is not quite the function you are looking for, but there is an "idiomatic name" for this function:

foldr (.) id

An experienced Haskeller doesn't even need to think about what that means; it's common enough to be a single word.

foldr (.) id :: [a -> a] -> a -> a
foldr (.) id [abs, succ, negate] 42
    = (abs . succ . negate . id) 42
    = abs (succ (negate 42))

The function you asked for, by each function getting the output of the previous rather than the next, would be

foldr (.) id . reverse

But if you can manage it, use the former version (for one, it is lazier; i.e. it can produce a result given an infinite list of functions).

Sousa answered 3/3, 2012 at 21:48 Comment(0)
S
0

You can't have such a function in haskell because it will be limited only to functions that accept and return the same type. And that is not very useful.

But you can use either

function composition:

floor . sqrt 

or arrows:

sqrt >>> floor
Sematic answered 3/3, 2012 at 19:1 Comment(0)
G
0

Oliver Steele calls it sequence in his Functional Javascript library. pipe might be a good name too.

Guan answered 13/3, 2012 at 10:52 Comment(0)
P
0

As in Haskell, the limitation in OCaml is the need for all functions involved to take and return the same type.

h (g (f x)) can also be expressed with the |> operator as x |> f |> g |> h, so if we fold the operator over a list of functions with our x as the initial value, we accomplish the goal.

We could also use Fun.flip to achieve partial application of a list of functions. Beware the value restriction if the types involved are polymorphic.

# let apply v fs = List.fold_left (|>) v fs;;
val apply : 'a -> ('a -> 'a) list -> 'a = <fun>
# apply 5 [(fun x -> x + 1); (fun x -> x * 3)];;
- : int = 18
# apply 5 [(fun x -> x + 1); (fun x -> x * 3); (fun x -> x / 2)];;
- : int = 9
# let f = Fun.flip apply [(fun x -> x + 1); (fun x -> x * 3); (fun x -> x / 2)];;
val f : int -> int = <fun>
# f 9;;
- : int = 15
Pastoralize answered 27/11, 2022 at 2:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.