Compose function operator

471 views
Skip to first unread message

Vladimir Brankov

unread,
Aug 31, 2012, 11:48:17 AM8/31/12
to ocaml...@googlegroups.com
As far as I can tell, the only way to compose functions is Fn.compose.  Why not having an operator as well?  It's easier to say:

    f1 |- f2 |- f3

than

    compose (compose f1 f2) f3

Also, I see that Batteries has some other interesting function composition operators.  I don't have any use for it now, but we may want to think about it.

David House

unread,
Aug 31, 2012, 12:01:10 PM8/31/12
to ocaml...@googlegroups.com
There is already (|!) which almost does what you want, but in a more
logical order.

I say "almost" since it does not allow you to build up a partially
applied function. But I for one very much dislike Fn.compose in those
scenarios. I think it's generally much clearer to write out the
lambda, since then one is forced to give a name to the variable. E.g.
which is clearer?

List.map events ~f:(Fn.compose to_string fst)
List.map events ~f:(fun (kind, _time) -> to_string kind)

The latter code is much more annotated and, to me, clearer as a result.

I know that sometimes writing names is pretty unnecessary, because
it's already clear what the output of a function will be. And in that
case, adding names can just clutter the code and decrease the
information density, making it harder to read. But one can just use
(|!), which as I said makes things appear in a more logical order.

Finally, there is a significant cost for any new infix operator. If
that thing is not used all the time, it is very obscure and makes code
quite opaque.

Sean McLaughlin

unread,
Aug 31, 2012, 12:13:45 PM8/31/12
to ocaml...@googlegroups.com
Finally, there is a significant cost for any new infix operator. If
that thing is not used all the time, it is very obscure and makes code
quite opaque.

Function composition seems fundamental, and has built-in operators in SML and Haskell.  I don't see a great reason why we don't have one in ocaml.   I personally think composition in Haskell can make code more clear (and beautiful, if we're using aesthetics as some kind of metric as above.)  

One major problem with composition in SML (also ocaml), is that the value restriction prevents useful composition of pure functions.

$ sml
Standard ML of New Jersey v110.74 [built: Tue Jan 31 16:23:10 2012]
- fun id x = x
val id = fn : 'a -> 'a
- id o id;
stdIn:3.1-3.8 Warning: type vars not generalized because of
   value restriction are instantiated to dummy types (X1,X2,...)
val it = fn : ?.X1 -> ?.X1

Thus it's not nearly as useful as in Haskell.  You could imagine always wrapping compositions in a big lambda 

- fn x => (id o id o id o id) x;
val it = fn : 'a -> 'a

but this pretty much defeats the purpose if you have |! 
fun x -> x |! id |! id |! id |! id

Vladimir Brankov

unread,
Aug 31, 2012, 4:00:39 PM8/31/12
to ocaml...@googlegroups.com
Composition would not hurt in this case:

    let name = to_string |- lowercase

as opposed to

    let name t = to_string t |! lowercase

IMHO both are slightly clearer than:

    let name t = lowercase (to_string t)

This is a small example, imagine if we have a few stacked functions.

David House

unread,
Aug 31, 2012, 4:04:16 PM8/31/12
to ocaml...@googlegroups.com
Don't you mean:

let name = lowercase |- to_string

Functional composition is normally written such that the function on
the right is applied first.

This strikes me as less clear than your example with |!, because of
the weird ordering.

But even without the ordering constraint, if |- were as clear or only
a little clearer than |!, we should still not include it, because |!
already exists (and is more general), and because of that cost of
every new infix operator that I mentioned before.

Vladimir Brankov

unread,
Aug 31, 2012, 4:20:54 PM8/31/12
to ocaml...@googlegroups.com
What about this case:

    List.iter l ~f:(fun t -> to_string t |! lowercase)

as opposed to

    List.iter l ~f:(lowercase |- to_string)

I suppose you would argue that the first one is preferred.  That raises a question - who decides whether a feature gets included or rejected?  I suppose that none of us can claim to know what the majority of the users would or wouldn't want.  What's the procedure for new features?

David House

unread,
Aug 31, 2012, 5:11:51 PM8/31/12
to ocaml...@googlegroups.com
Yes, the first is preferred. You've called the variable [t], which
immediately tells me what kind of a list we're iterating over. In the
second example, I have to think about the type of to_string in order
to infer this.

Plus, the order is still weird, and the new infix operator is very
opaque. Everyone has to commit to memory what this new arbitrary
sequence of characters means. (As an aside, |! has this problem to a
lesser extent, because it visually looks like a unix command-line
pipe. This provides a helpful mnemonic, so remembering is much
easier.)

Edgar Friendly

unread,
Aug 31, 2012, 5:36:05 PM8/31/12
to ocaml...@googlegroups.com
On 08/31/2012 05:11 PM, David House wrote:
> (As an aside, |! has this problem to a
> lesser extent, because it visually looks like a unix command-line
> pipe. This provides a helpful mnemonic, so remembering is much
> easier.)

To me, |! looks quite similar to ||. I'm quite happy with the F# and
batteries |> operator which visually looks like an arrow funneling data
to the right.

E.

Yaron Minsky

unread,
Aug 31, 2012, 5:42:13 PM8/31/12
to ocaml...@googlegroups.com
Yeah, if we'd realized earlier, I would have liked to use |>. I've
toyed with the idea of adding it as an alias, but switching all of our
internal code would be a lot of painful, pointless churn.

y

David House

unread,
Aug 31, 2012, 5:44:17 PM8/31/12
to ocaml...@googlegroups.com
Yeah, I've also thought that. |> does seem like a slightly better choice.

Yaron Minsky

unread,
Aug 31, 2012, 5:44:28 PM8/31/12
to ocaml...@googlegroups.com
We've talked about this one a lot internally over the years, and have
settled on not adding a compose operator. Our experience has been
that code using the compose operator tends to be harder to read, and
House's point about the cognitive load of infix operators is well
taken.

y

Vladimir Brankov

unread,
Sep 4, 2012, 9:26:13 AM9/4/12
to ocaml...@googlegroups.com
I think that not having operators which are standard in Haskell, F# and Batteries is a brave decision.  Being harder to read can come from not being used to.  It would be interesting to hear arguments and experiences from where the operators are available.

Yaron Minsky

unread,
Sep 4, 2012, 11:25:21 AM9/4/12
to ocaml...@googlegroups.com
API design is as much about what to exclude as what to include.
Throwing in every operator that other languages have doesn't seem like
a sound design principle.

Sebastien Mondet

unread,
Sep 4, 2012, 12:40:27 PM9/4/12
to ocaml...@googlegroups.com

Hi

I agree that infix operators kill readability very very fast.


But, this good old trick could be a compromise (?):

Define these two:

  let (<<) f x = x f;;
  let (>>) x f = x f;;

And then create “infix functions” which may be much more readable:

  let compose f g = fun x -> f (g x);;
  let pipe x f = f x;;
  let group f x = f x;;  (* Haskell's dollar... but readable *)


The operator precedence makes things look nice:

  let f = float <<compose>> (+) 42 << compose >> ( * ) 2;;

# f 1;;
- : float = 44.
# 1 <<pipe>> f;;
- : float = 44.
# 1 <<pipe>> f <<pipe>> (+.) 3.;;
- : float = 47.
# float <<group>> (+) 4 3;;
- : float = 7.

etc.


Cheers
Sebastien

Yaron Minsky

unread,
Sep 4, 2012, 12:54:38 PM9/4/12
to ocaml...@googlegroups.com
Ah, cute! This is like Haskell's backtick syntax for converting a
prefix operator into an infix one, yes? The main downside I see of
the particular notation is it looks a hell of a lot like quotations...

Francois Berenger

unread,
Sep 5, 2012, 12:33:17 AM9/5/12
to ocaml...@googlegroups.com
<<pipe>> is a little too verbose I think.
But Sebastian's examples looks cool and readable.

Matej Kosik

unread,
Sep 5, 2012, 5:19:37 AM9/5/12
to ocaml...@googlegroups.com
On 31/08/12 17:01, David House wrote:
> There is already (|!) which almost does what you want, but in a more
> logical order.
>
> I say "almost" since it does not allow you to build up a partially
> applied function. But I for one very much dislike Fn.compose in those
> scenarios. I think it's generally much clearer to write out the
> lambda, since then one is forced to give a name to the variable. E.g.
> which is clearer?
>
> List.map events ~f:(Fn.compose to_string fst)
> List.map events ~f:(fun (kind, _time) -> to_string kind)
>
> The latter code is much more annotated and, to me, clearer as a result.
>
> I know that sometimes writing names is pretty unnecessary, because
> it's already clear what the output of a function will be. And in that
> case, adding names can just clutter the code and decrease the
> information density, making it harder to read. But one can just use
> (|!), which as I said makes things appear in a more logical order.

AFAIK, Batteries provide two relevant infix operators:

|-

-|

One of them is obviously redundant.

<speculation>
Both are kept probably because it is hard to decide which one to drop.
By dropping one, you can annoy some set of people.
By dropping the other one, you can annoy another set of people.
</speculation>

But function composition is an important technique for compressing
source code at micro-level. Even in mathematics, you have "o" operator
so introducing infix operator for function composition does not seem to
be a culturally alien thing. I got used to it. Sometimes really, code
verbosity is just verbosity, not clarity.

Francois Berenger

unread,
Sep 6, 2012, 3:04:23 AM9/6/12
to ocaml...@googlegroups.com
On Wed, Sep 5, 2012 at 1:33 PM, Francois Berenger
<francois.ber...@gmail.com> wrote:
> <<pipe>> is a little too verbose I think.
> But Sebastian's examples looks cool and readable.

I think the notation is . in Haskell: i.e. (f . g) x = f (g x).
It's not very far from the math notation which is a big dot
if I remember correctly.

Nicolas Braud-Santoni

unread,
Sep 7, 2012, 6:20:14 AM9/7/12
to ocaml...@googlegroups.com
Exactly.

As far as I know, the dot notation was chosen for this reason.

2012/9/6 Francois Berenger <francois.ber...@gmail.com>:

Anil Madhavapeddy

unread,
Dec 29, 2012, 11:37:05 AM12/29/12
to ocaml...@googlegroups.com
Reopening this thread; would you consider adding adding an (|>) alias to core
alongside the existing (|!) ?

It has become the defacto standard with F#, and it would be nice to avoid
unnecessary divergence when explaining OCaml to beginners.

-anil
Reply all
Reply to author
Forward
0 new messages