Giter VIP home page Giter VIP logo

Comments (6)

lpil avatar lpil commented on September 15, 2024 1

The only language that has right to left composition I could find was Haskell, all others I researched defaulted to left-to-right. I think left to right composition is nicer as it reads top-to-bottom and left-to-right, like regular Gleam code, making it a good fit for the language.

from stdlib.

chuckwondo avatar chuckwondo commented on September 15, 2024

Thanks for the prompt reply. Would you mind providing some examples?

Let me apologize in advance for my persistence on this subject. I realize this reply may be exhibit borderline OCD behavior on my part, so please forgive me.

Although I've only recently discovered Gleam, I find it to be extremely well designed and an absolute joy to use (thank you for writing it!), so my motivation here is to see that Gleam is consistent with what is, in my experience, common convention, so that those coming from other languages/libraries are not surprised, like I was, by how Gleam's compose function behaves. (BTW, just to connect the dots, you recently reviewed my Forth exercise in Gleam on exercism.)

I absolutely agree with you that left-to-right evaluation is nicer (more natural) to read, because the order of evaluation matches the order in which the arguments are written. However, I feel that those coming from other languages will be very confused by the left-to-right evaluation order of Gleam's compose function, because, in my experience, libraries name functions for left-to-right evaluation things like "pipe" or "flow" or "composeLeft" (or "compose_left"), and a "compose" function (or operator) in such langs/libs performs right-to-left evaluation (opposite the reading order).

Again, I apologize if you feel that I'm belaboring the point, but I feel that most others will also find the left-to-right evaluation order of Gleam's compose function to be the reverse of general expectations.

In fact, because I, like you, find left-to-right evaluation order more "natural", I almost exclusively use a "pipe" function rather than a "compose" function. This is why I was confused to find that Gleam's compose function behaves like a "pipe" function in other langs/libs.

In fact, Gleam's own pipe operator (|>) performs left-to-right evaluation (as is conventional for languages with a pipe operator), so using the name "pipe" (not "compose") for a function that also performs left-to-right evaluation would be consistent with that operator.

Here are some examples from my experience. Of course, this is your language, so you're obviously free to disagree, and there's no "right" or "wrong" answer. I'm simply making a case for consistency with what I believe most people will find to be "conventional" behavior of a "compose" function.

Again, apologies for possibly belaboring the issue.

To start with, the mathematical conventional for the composition of 2 functions is represented like so (or similarly), where ∘  is the compose operator:

(g ∘ f)(x) = g(f(x))

Or, using prefix notation rather than infix notation:

∘(g, f)(x) = g(f(x))

This definition shows that although the functions g and f are written from left-to-right, they are evaluated right-to-left (i.e., the function on the right, f, is evaluated first, followed by g on the left).

Arguably, perhaps, this definition alone might be enough to convince you, but if not, here are some examples from langs/libs that arguably cover the vast majority of use/popularity of languages:

  • Haskell, as you mentioned, has a composition operator built into the language, and evaluates from right-to-left. For example, g . f corresponds directly to, and is evaluated exactly as, the definition shown above.
  • Python
    • functoolz.compose: right-to-left evaluation
    • functoolz.compose_left: left-to-right evaluation
    • functoolz.pipe: left-to-right evaluation (alias of compose_left)
    • funcy.compose: right-to-left evaluation
    • funcy.rcompose: confusingly named, but left-to-right evaluation
    • pyramda: hard to find, but the compose function's signature indicates right-to-left evaluation: compose :: (y -> z) ... (a -> b) -> a -> z
    • pfun.compose: the description is confusing because it says it composes functions from left to right, but that refers to how the functions are "wrapped", not how they are evaluated. The corresponding code example shows that evaluation is right-to-left
    • pfun.pipeline: confusing description here too, but code example shows left-to-right evaluation
    • Awesome Functional Python Libraries: additional libraries where you're likely to find a "compose" function with right-to-left evaluation
    • Function Composition in Python: one of many posts/answers that show right-to-left evaluation for a compose function
    • Composing functions in python: the first StackOverflow link that showed up when I performed and online search for "compose function in python", which shows solutions using right-to-left evaluation
  • JavaScript/TypeScript
  • Java
    • java.util.function.Function.compose: right-to-left evaluation (it's not obvious from the docs, but g.compose(f).apply(x) is evaluated as g(f(x)), the right-to-left evaluation)
    • java.util.function.Function.andThen: left-to-right evaluation (in this case, this is more obvious, as g.andThen(f).apply(x) first applies g and then applies f, so is equivalent to f(g(x)), so this is akin to the "pipe" functions listed elsewhere)
  • Clojure
    • clojure.core/comp: right-to-left evaluation
    • threading macros: the thread-first macro (->) is akin to Gleam's pipe operator (|>), and thus performs left-to-right evaluation
  • Rust
    • Composition in Rust: article shows a pipe macro that performs left-to-right evaluation and compose macro that performs right-to-left evaluation
  • R
    • compose function in purrr library: default evaluation order is right-to-left (can be changed using the dir parameter, but that's not the default, and it would make it akin to a "pipe" function according to everything listed above)

I could go on, but I simply cannot find where a "compose" function evaluates functions in left-to-right order, which is why I find Gleam's right-to-left evaluation counter-intuitive. It behaves like "pipe" functions everywhere I've listed above.

Where do you see otherwise?

from stdlib.

chuckwondo avatar chuckwondo commented on September 15, 2024

TL;DR

Specifically, I encourage you to consider reversing the order of evaluation of Gleam's compose function like so, which would make it consistent with every "compose" function listed in my previous comment:

pub fn compose(fun1: fn(b) -> c, fun2: fn(a) -> b) -> fn(a) -> c {
  fn(a) { fun1(fun2(a)) }
}

Additionally, consider adding a pipe function implemented like so, which would be consistent with every "pipe" function listed above (this is simply a rename of the existing Gleam compose function to pipe):

pub fn pipe(fun1: fn(a) -> b, fun2: fn(b) -> c) -> fn(a) -> c {
  fn(a) { fun2(fun1(a)) }
}

from stdlib.

lpil avatar lpil commented on September 15, 2024

Sorry, we're not going to adopt right to left composition, it would be inconsistent with everything else in the language.

We generally encourage using anonymous functions over functions like compose as that's typically clearer and avoid this confusion. We're more likely to remove this function than to change the behaviour, but that is also unlikely as it would break people's code.

from stdlib.

chuckwondo avatar chuckwondo commented on September 15, 2024

I can understand. Thanks for putting up with my diatribe.

Just for my edification, are you saying that right-to-left evaluation would be inconsistent with everything else in the language because the pipe operator (|>) is fundamental to Gleam, and evaluates left-to-right?

If so, that's why I'm suggesting that the compose function might be better named as pipe, as that name would not only be consistent with the pipe operator (both the operator and function would evaluate in the same direction), but it would also be consistent everything that I've encountered in other languages.

Might you at least consider adding a pipe function as an alias for compose? EDIT: Nevermind. That would perhaps only add to the confusion.

If not, no worries, and I'll make no further comments about it. Thanks again for your time and for Gleam!

from stdlib.

inoas avatar inoas commented on September 15, 2024

Might you at least consider adding a pipe function as an alias for compose? EDIT: Nevermind. That would perhaps only add to the confusion.

Would people expect pipe over compose to exist (if it did exactly the same)?
If yes, we could rename it?

p.s. we should also rename all length functions to count while not 1.0 yet.

from stdlib.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.