8

If we have two functions, f and g, then in Haskell h = f . g is equivalent to h x = f(g x). I.e. the functions are applied from right to left to the input. Is there any fundamental reason why it goes from right to left, and not from left to right? I.e. why didn't they make h = f . g equivalent to h x = g(f x) instead?

EDIT: as others pointed out my equivalent functions where the wrong way around, so I fixed those.

Tiddo
  • 6,331
  • 6
  • 52
  • 85
  • 3
    I think you're a bit confused, `.` is right associative, but `h = f . g` is indeed equivalent to `h x = f (g x)`: `h = (*3) . (+2)`. `h 2` yields `12`, as you'd expect if `(+2)` would be evaluated before – berdario Apr 26 '15 at 14:02
  • Also, the associative property is meaningful when you have more than 1 operation (and thus more than 2 operands): https://en.wikipedia.org/wiki/Associative_property – berdario Apr 26 '15 at 14:04
  • 2
    Why `h x = f (g x)` and not `h x = g (f x)` isn't a question of the associativity of composition, but rather how the operands to `.` are treated. A question of associativity would be why `f . g . h` is parsed as `f . (g . h)` and not `(f . g) . h`, but in fact, composition is fully associative and both parenthesizations are correct. Composition is the same as addition in this sense. – chepner Apr 26 '15 at 14:23
  • See http://stackoverflow.com/q/20342860/1126841 – chepner Apr 26 '15 at 14:25

1 Answers1

16

First of all, there's a mistake in your [original, unedited] question:

h = f . g is equivalent to h x = g(f x)

— that's not true: h = f . g is equivalent to h x = f (g x).

However, as to why it's that way and not the other way around, it's most likely because that's how it works and has worked in math; see http://en.wikipedia.org/wiki/Function_composition:

[...] composite function is denoted g ∘ f : X → Z, defined by (g ∘ f )(x) = g(f(x)) for all x in X.

It's also intuitive because of the equality (f . g) x == f (g x) — as you can see, the order of f and g is the same on both sides.


Moreover, it's trivial to create your own "reverse composition" operator if you desire one for reasons of e.g. readability:

(.>) = flip (.)

so that

Prelude> ((+1) .> (*2)) 3
8
Prelude> ((+1) . (*2)) 3
7

In fact, you can just use Control.Arrow.(>>>) which does the same for functions but is more general and works for other things as well:

Prelude Control.Arrow> ((+1) >>> (*2)) 3
8
Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
  • 8
    Traditional it is; intuitive it is not. It makes the order of the text the opposite of the order of the diagram, which is extremely annoying. – dfeuer Apr 26 '15 at 14:34
  • which diagram? and couldn't you then also say that `f x` is unintuitive to start with? compared to `x.f()`, that is. – Erik Kaplun Apr 26 '15 at 14:52
  • 3
    "diagram" in the serse of category theory. That is: if you have a function `f:a->b` and another `g:b->c`, it feels unnatural to write `g.f` composing arrows/functions/morphisms in the opposite order. – chi Apr 26 '15 at 14:54
  • 3
    yes, but perhaps it should be `f:b<-a` in the first place, because we also apply the argument from right to left (`f x` not `x f`)... therefore, I'd say that `(f . g) x == f (g x)` is at least consistent with `f x` because the argument "flows through" the function from right to left. – Erik Kaplun Apr 26 '15 at 14:55
  • 4
    in other words, if the types flow left to right (like in `f:a->b` and `g:b->c`) then we should also have `(x)f` (vs `f(x)`), and `(x)(f . g) : a -> b`; then everything would be truly consistent and intuitive. – Erik Kaplun Apr 26 '15 at 14:58
  • Indeed, we should reverse application notation as well to be consistent. – chi Apr 26 '15 at 14:59
  • 3
    ...which is probably why so many people like the OO style: `x.f().g()`. – Erik Kaplun Apr 26 '15 at 14:59
  • 2
    @ErikAllik Richard Bird's _The Algebra of Programming_ uses flipped composition and flipped arrows in type signatures, so there is some precedent. – Rein Henrichs Apr 26 '15 at 17:28
  • 1
    @chi I don't think reversing one or the other notation can make everything consistent. If you have `f :: a -> b -> c -> d`, then you can have `f x y z`, and read the types of the arguments `x :: a`, `y :: b`, `z :: c` in left-to-right order. If you reverse application order, you'd end up with `z y x f`; now we're writing arguments in the opposite order to the order their types appear in the signature. Reversing the arrows instead gives you the same problem. You can only line up 2 of the 3 (directions of arrows, order of arguments in function type, order of arguments in function application). – Ben Jun 27 '17 at 01:14
  • @Ben you could go `f :: c <- b <- a ` (or even `c <- b <- a :: f`) – Erik Kaplun Jun 27 '17 at 08:49
  • @ErikAllik Sorry, it's not the direction of arrows I meant to write (that's basically the same thing as the order of types in the function signature), but the direction in which arguments "flow" through functions (the thing that many people like being left-to-right in OO style method invocation). – Ben Jun 27 '17 at 23:12
  • 1
    @ErikAllik But thinking about it now, I realise my point is less interesting than I thought it was yesterday. "Direction of data flow" simply is nothing more than the opposite of the order in which you write arguments. So naturally you can't get both of those in the same order (unless you go the OO route and treat one argument specially, and then only think about "flows" of the special arguments... that syntax design is actually cleverer than I had realised). – Ben Jun 27 '17 at 23:21