Compose function operator

Showing 1-20 of 20 messages
Compose function operator Vladimir Brankov 8/31/12 8:48 AM
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.

Re: Compose function operator David House 8/31/12 9:01 AM
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.
Re: Compose function operator Sean McLaughlin 8/31/12 9:13 AM
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

On Fri, Aug 31, 2012 at 4:48 PM, Vladimir Brankov <bran...@gmail.com> wrote:
> 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.
>
> http://batteries.forge.ocamlcore.org/doc.preview:batteries-beta1/html/api/Standard.html

Re: Compose function operator Vladimir Brankov 8/31/12 1:00 PM
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.
Re: Compose function operator David House 8/31/12 1:04 PM
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.
Re: Compose function operator Vladimir Brankov 8/31/12 1:20 PM
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?
Re: Compose function operator David House 8/31/12 2:11 PM
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.)
Re: Compose function operator Edgar Friendly 8/31/12 2:36 PM
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.
Re: Compose function operator Yaron Minsky 8/31/12 2:42 PM
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
Re: Compose function operator David House 8/31/12 2:44 PM
Yeah, I've also thought that. |> does seem like a slightly better choice.
Re: Compose function operator Yaron Minsky 8/31/12 2:44 PM
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
Re: Compose function operator Vladimir Brankov 9/4/12 6:26 AM
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.
Re: Compose function operator Yaron Minsky 9/4/12 8:25 AM
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.
Re: Compose function operator Sebastien Mondet 9/4/12 9:40 AM

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
Re: Compose function operator Yaron Minsky 9/4/12 9:54 AM
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...
Re: Compose function operator Francois Berenger 9/4/12 9:33 PM
<<pipe>> is a little too verbose I think.
But Sebastian's examples looks cool and readable.
Re: Compose function operator Matej Kosik 9/5/12 2:19 AM
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.
Re: Compose function operator Francois Berenger 9/6/12 12:04 AM
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.
Re: Compose function operator Nicolas Braud-Santoni 9/7/12 3:20 AM
Exactly.

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

2012/9/6 Francois Berenger <francois.ber...@gmail.com>:
Re: Compose function operator Anil Madhavapeddy 12/29/12 8:37 AM
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