Comments (6)
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.
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
- lodash.flowRight: right-to-left evaluation, and in the LoDash FP Guide, this alias is given:
_.compose is an alias of _.flowRight
- lodash.flow: left-to-right evaluation, and in the LoDash FP Guide, this alias is given:
_.pipe is an alias of _.flow
- ramda.compose: right-to-left evaluation
- ramda.pipe: left-to-right evaluation
- fp-ts.pipe: left-to-right evaluation
- lodash.flowRight: right-to-left evaluation, and in the LoDash FP Guide, this alias is given:
- 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 asg(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 appliesg
and then appliesf
, so is equivalent tof(g(x))
, so this is akin to the "pipe" functions listed elsewhere)
- java.util.function.Function.compose: right-to-left evaluation (it's not obvious from the docs, but
- 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 andcompose
macro that performs right-to-left evaluation
- Composition in Rust: article shows a
- 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)
- compose function in purrr library: default evaluation order is right-to-left (can be changed using the
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.
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.
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.
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 EDIT: Nevermind. That would perhaps only add to the confusion.pipe
function as an alias for compose
?
If not, no worries, and I'll make no further comments about it. Thanks again for your time and for Gleam!
from stdlib.
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)
- Bit array slices of slices incorrect on JavaScript
- Add `dict.change`? HOT 5
- Escape backspace etc graphemes in string.inspect HOT 15
- Failing test when negating zero HOT 5
- `JSON.stringify` produces invalid Gleam escape sequences and should be replaced HOT 1
- `list.window` going into infinite recursion when `n` is 0 HOT 1
- Add primitives to set fields on `Uri` objects. HOT 4
- Add a `new()` primitive to the `uri` package. HOT 5
- Consider adding a replace function to regex HOT 2
- Add the ability to decode more than 9 fields. HOT 4
- uri.origin should not have any path HOT 2
- Have iterator.yield not wait for the next to be available before yielding the previous HOT 3
- Question: round and truncate return Int instead Float HOT 1
- Return index of undecodable element in dynamic.list DecodeError path HOT 1
- Error in interator.find_map documentation
- Add `gleam/set.{map}`
- `float.parse` fails on exponential notation on JavaScript
- `bit_array.base64_encode` throws exception on large inputs
- Add `is_empty` to `set` and `dict` HOT 2
- `dynamic.element` does not work for lists on erlang HOT 5
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from stdlib.