Repository: prettier/prettier (JavaScript)
Date: 2018-05-06 19:08:16+00:00
Task type: Feature

Issue:
Long curried function calls
Prettier changed this:

```js
const ArtistInput = connect(mapStateToProps, mapDispatchToProps, mergeProps)(Component);
```

to this due to line-length:

```js
const ArtistInput = connect(mapStateToProps, mapDispatchToProps, mergeProps)(
  Component
);
```

At this risk of making a poorly written issue, I'm actually not sure if there is a more ideal way to format this. Nevertheless, some people on my team have complained about how prettier made this line a bit uglier. Perhaps we can brainstorm a better way to represent this?


Issue discussion:
I'm curious, how would you format this if it didn't fit in a single line?
As I've said, I am not sure if there is a more ideal way to reformat this.

But here are a couple ideas (that might be worse than the status quo):

1. This feels sensible enough:

```js
const ArtistInput =
  connect(mapStateToProps, mapDispatchToProps, mergeProps)(Component);
```

2. This might be over-doing it.

```js
const ArtistInput = connect(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps
)(Component);
```
@adrianmcli Think both of the examples you provided fit better into the style of how prettier is currently formatting arrow functions and functions in general with multiple arguments than how this is formatted in its current state.
@adrianmcli I would prefer option 2 over 1. Especially when arguments are partially applied functions. For instance:

```js
import R from 'ramda';

const mapStateToProps = state => ({
  users: R.compose(
    R.filter(R.propEq('status', 'active')),
    R.values
  )(state.users)
});
```
Option 2 is what naturally happens when once the argument list gets a bit longer.

```js
const App = compose(withState('thing', 'setThing'), withHandlers({
  onClick: props => event => { props.setThing(event.target.value); },
}))(BaseApp);
```
becomes
```js
const App = compose(
  withState('thing', 'setThing'),
  withHandlers({
    onClick: props => event => {
      props.setThing(event.target.value);
    },
  }),
)(BaseApp);
```

Still, this was previously closed in #1116 and #1035, so we may need to make a `prettier-fp` fork to handle this a bit better at a global level.
For the original report, the ideal way would be for prettier to format it as

```js
const ArtistInput = connect(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps
)(Component);
```

The root of the issue here is that the first and second arguments are next to each other in terms of the intermediate representation and not nested. Right now we have very little control around how the low level formatters choses which one to break.

Another issue with the same root cause: https://github.com/prettier/prettier/issues/1825

@threehams please do fork prettier and try to make it work well for functional programming :) If you find great heuristic for them, we should port them back to prettier.
OK! I was only planning to do that since it was your recommendation in the (closed) linked issues.

Ideally there's a rule that could change formatting for nested functions without negatively affecting other code. I don't think that `function(argument)(argument)` is a very common pattern in non-FP code styles, but I could be wrong. I'll take a look.
@threehams any news on this ? :) 
I tried a PR, but it made other code worse (I detected function calls within argument lists and forced line breaks). It's tricky to come up with a rule for this one.

Solving the linked issue (#1825) would be the next approach, though I've mostly just accepted that <1% of code will be less readable with Prettier than without. That's a really good tradeoff.
Great to see this is already being looked at. Since my issue was closed, I'll just put the contents here in case any of it helps the discussion here.

**Prettier 1.7.0**
[Playground link](https://prettier.io/playground/#%7B%22content%22%3A%22export%20const%20firstComp%20%3D%20compose(funcOne%2C%20funcTwo%2C%20funcThree%2C%20funcFour)(component)%3B%5Cn%5Cnexport%20const%20secondComp%20%3D%20compose(funcOne%2C%20funcTwo%2C%20funcThree%2C%20funcFour%2C%20funcFive)(component)%3B%22%2C%22options%22%3A%7B%22ast%22%3Afalse%2C%22bracketSpacing%22%3Atrue%2C%22doc%22%3Afalse%2C%22jsxBracketSameLine%22%3Afalse%2C%22output2%22%3Afalse%2C%22parser%22%3A%22babylon%22%2C%22printWidth%22%3A80%2C%22semi%22%3Atrue%2C%22singleQuote%22%3Afalse%2C%22tabWidth%22%3A2%2C%22trailingComma%22%3A%22none%22%2C%22useTabs%22%3Afalse%7D%7D)

When immediately invoking a function returned from a function, if the line length is just right, then the parameters to the invocation are isolated on single lines instead of the parameters to the initial call.

It would be nice if Prettier could favor isolating the longer parameter set. The longer parameter set is the one that is more likely to have additions/deletions in refactoring, and so will benefit from being formatted on a line-by-line basis.

If the parameter counts are equal, favor the parameter set with the longer character count.

As an example, this is incredibly common to deal with when using the `compose` function from Redux, often in a React codebase.

**Input:**
```jsx
export const firstComp = compose(funcOne, funcTwo, funcThree, funcFour)(component);

export const secondComp = compose(funcOne, funcTwo, funcThree, funcFour, funcFive)(component);
```

**Output:**
```jsx
// This seems ugly and unhelpful
export const firstComp = compose(funcOne, funcTwo, funcThree, funcFour)(
  component
);

export const secondComp = compose(
  funcOne,
  funcTwo,
  funcThree,
  funcFour,
  funcFive
)(component);

```

**Expected behavior:**
```jsx
export const firstComp = compose(
  funcOne,
  funcTwo,
  funcThree,
  funcFour,
)(component);

export const secondComp = compose(
  funcOne,
  funcTwo,
  funcThree,
  funcFour,
  funcFive
)(component);

```
Any progress on that concern? This is the main reason why our team still hesitate to use prettier...
This might be a good time to use `conditionalGroup`:

```js
// condition 1
const ArtistInput = connect(mapStateToProps, mapDispatchToProps, mergeProps)(Component);

// condition 2
const ArtistInput = connect(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps
)(Component);

// condition 3
const ArtistInput = connect(mapStateToProps, mapDispatchToProps, mergeProps)(
  Component
);

// conditions 2 + 3 are ordered by trying not to break whichever one has less (but not 1) arguments
// i.e. in this case, we try to break the one with multiple arguments first,
// but if we add `, foo` after `Component`, the conditions get swapped.

// condition 4
const ArtistInput = connect(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps
)(
  Component
);

```
If this is a discussion, lets raised some concrete questions.

> Do we need this?
> How can we deal with it, if we do?

Within increasing popularity of fp patterns in javascript I do believe this issue is important. New line for an each function call looks the best for me.
@j-f1 I like that. This would be similar code to how print chain works?
As an additional condition, perhaps if the result of a function is then invoked, if the first call has more than 1 (2?) arguments, split into condition 2.

Similarly, if the returned function is invoked with more than N arguments it would also be spread to multi line?



Solution patch:
diff --git a/src/language-js/printer-estree.js b/src/language-js/printer-estree.js
index cbb232c4b731..be846a3882e6 100644
--- a/src/language-js/printer-estree.js
+++ b/src/language-js/printer-estree.js
@@ -3322,8 +3322,36 @@ function shouldGroupFirstArg(args) {
   );
 }
 
+const functionCompositionFunctionNames = {
+  pipe: true, // RxJS, Ramda
+  pipeP: true, // Ramda
+  pipeK: true, // Ramda
+  compose: true, // Ramda, Redux
+  composeFlipped: true, // Not from any library, but common in Haskell, so supported
+  composeP: true, // Ramda
+  composeK: true, // Ramda
+  flow: true, // Lodash
+  flowRight: true, // Lodash
+  connect: true // Redux
+};
+function isFunctionCompositionFunction(node) {
+  switch (node.type) {
+    case "MemberExpression": {
+      return isFunctionCompositionFunction(node.property);
+    }
+    case "Identifier": {
+      return functionCompositionFunctionNames[node.name];
+    }
+    case "StringLiteral":
+    case "Literal": {
+      return functionCompositionFunctionNames[node.value];
+    }
+  }
+}
+
 function printArgumentsList(path, options, print) {
-  const args = path.getValue().arguments;
+  const node = path.getValue();
+  const args = node.arguments;
 
   if (args.length === 0) {
     return concat([
@@ -3356,6 +3384,32 @@ function printArgumentsList(path, options, print) {
     return concat(parts);
   }, "arguments");
 
+  const maybeTrailingComma = shouldPrintComma(options, "all") ? "," : "";
+
+  function allArgsBrokenOut() {
+    return group(
+      concat([
+        "(",
+        indent(concat([line, concat(printedArguments)])),
+        maybeTrailingComma,
+        line,
+        ")"
+      ]),
+      { shouldBreak: true }
+    );
+  }
+
+  // We want to get
+  //    pipe(
+  //      x => x + 1,
+  //      x => x - 1
+  //    )
+  // here, but not
+  //    process.stdout.pipe(socket)
+  if (isFunctionCompositionFunction(node.callee) && args.length > 1) {
+    return allArgsBrokenOut();
+  }
+
   const shouldGroupFirst = shouldGroupFirstArg(args);
   const shouldGroupLast = shouldGroupLastArg(args);
   if (shouldGroupFirst || shouldGroupLast) {
@@ -3388,8 +3442,6 @@ function printArgumentsList(path, options, print) {
 
     const somePrintedArgumentsWillBreak = printedArguments.some(willBreak);
 
-    const maybeTrailingComma = shouldPrintComma(options, "all") ? "," : "";
-
     return concat([
       somePrintedArgumentsWillBreak ? breakParent : "",
       conditionalGroup(
@@ -3419,16 +3471,7 @@ function printArgumentsList(path, options, print) {
                 }),
                 ")"
               ]),
-          group(
-            concat([
-              "(",
-              indent(concat([line, concat(printedArguments)])),
-              maybeTrailingComma,
-              line,
-              ")"
-            ]),
-            { shouldBreak: true }
-          )
+          allArgsBrokenOut()
         ],
         { shouldBreak }
       )


Test patch:
diff --git a/tests/decorators/__snapshots__/jsfmt.spec.js.snap b/tests/decorators/__snapshots__/jsfmt.spec.js.snap
index 7079563c8449..cf18dabc6b34 100644
--- a/tests/decorators/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/decorators/__snapshots__/jsfmt.spec.js.snap
@@ -152,7 +152,10 @@ export class MyApp extends React.Component {}
 @connect(state => ({ todos: state.todos }))
 export class Home extends React.Component {}
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-@connect(mapStateToProps, mapDispatchToProps)
+@connect(
+  mapStateToProps,
+  mapDispatchToProps
+)
 export class MyApp extends React.Component {}
 
 @connect(state => ({ todos: state.todos }))
diff --git a/tests/first_argument_expansion/__snapshots__/jsfmt.spec.js.snap b/tests/first_argument_expansion/__snapshots__/jsfmt.spec.js.snap
index b21d702cdf52..3ee6c88f15fa 100644
--- a/tests/first_argument_expansion/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/first_argument_expansion/__snapshots__/jsfmt.spec.js.snap
@@ -125,9 +125,12 @@ func(() => {
   thing();
 }, /regex.*?/);
 
-compose(a => {
-  return a.thing;
-}, b => b * b);
+compose(
+  a => {
+    return a.thing;
+  },
+  b => b * b
+);
 
 somthing.reduce(function(item, thing) {
   return (thing.blah = item);
diff --git a/tests/functional_composition/__snapshots__/jsfmt.spec.js.snap b/tests/functional_composition/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000000..b23edb738eef
--- /dev/null
+++ b/tests/functional_composition/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,362 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`functional_compose.js 1`] = `
+compose(
+  sortBy(x => x), 
+  flatten, 
+  map(x => [x, x*2])
+);
+
+somelib.compose(
+  sortBy(x => x), 
+  flatten, 
+  map(x => [x, x*2])
+);
+
+composeFlipped(
+  sortBy(x => x), 
+  flatten, 
+  map(x => [x, x*2])
+);
+
+somelib.composeFlipped(
+  sortBy(x => x), 
+  flatten, 
+  map(x => [x, x*2])
+);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+compose(
+  sortBy(x => x),
+  flatten,
+  map(x => [x, x * 2])
+);
+
+somelib.compose(
+  sortBy(x => x),
+  flatten,
+  map(x => [x, x * 2])
+);
+
+composeFlipped(
+  sortBy(x => x),
+  flatten,
+  map(x => [x, x * 2])
+);
+
+somelib.composeFlipped(
+  sortBy(x => x),
+  flatten,
+  map(x => [x, x * 2])
+);
+
+`;
+
+exports[`lodash_flow.js 1`] = `
+import { flow } from "lodash";
+
+const foo = flow(
+  x => x + 1,
+  x => x * 3,
+  x => x - 6,
+);
+
+foo(6);
+
+import * as _ from "lodash";
+
+const foo = _.flow(
+  x => x + 1,
+  x => x * 3,
+  x => x - 6,
+);
+
+foo(6);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+import { flow } from "lodash";
+
+const foo = flow(
+  x => x + 1,
+  x => x * 3,
+  x => x - 6
+);
+
+foo(6);
+
+import * as _ from "lodash";
+
+const foo = _.flow(
+  x => x + 1,
+  x => x * 3,
+  x => x - 6
+);
+
+foo(6);
+
+`;
+
+exports[`lodash_flow_right.js 1`] = `
+import { flowRight } from "lodash";
+
+const foo = flowRight(
+  x => x + 1,
+  x => x * 3,
+  x => x - 6,
+);
+
+foo(6);
+
+import * as _ from "lodash";
+
+const foo = _.flowRight(
+  x => x + 1,
+  x => x * 3,
+  x => x - 6,
+);
+
+foo(6);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+import { flowRight } from "lodash";
+
+const foo = flowRight(
+  x => x + 1,
+  x => x * 3,
+  x => x - 6
+);
+
+foo(6);
+
+import * as _ from "lodash";
+
+const foo = _.flowRight(
+  x => x + 1,
+  x => x * 3,
+  x => x - 6
+);
+
+foo(6);
+
+`;
+
+exports[`ramda_compose.js 1`] = `
+var classyGreeting = (firstName, lastName) =>
+  "The name's " + lastName + ", " + firstName + " " + lastName;
+var yellGreeting = R.compose(R.toUpper, classyGreeting);
+yellGreeting("James", "Bond"); //=> "THE NAME'S BOND, JAMES BOND"
+
+R.compose(Math.abs, R.add(1), R.multiply(2))(-4); //=> 7
+
+//  get :: String -> Object -> Maybe *
+var get = R.curry((propName, obj) => Maybe(obj[propName]));
+
+//  getStateCode :: Maybe String -> Maybe String
+var getStateCode = R.composeK(
+  R.compose(Maybe.of, R.toUpper),
+  get("state"),
+  get("address"),
+  get("user")
+);
+getStateCode({ user: { address: { state: "ny" } } }); //=> Maybe.Just("NY")
+getStateCode({}); //=> Maybe.Nothing()
+
+var db = {
+  users: {
+    JOE: {
+      name: "Joe",
+      followers: ["STEVE", "SUZY"]
+    }
+  }
+};
+
+// We'll pretend to do a db lookup which returns a promise
+var lookupUser = userId => Promise.resolve(db.users[userId]);
+var lookupFollowers = user => Promise.resolve(user.followers);
+lookupUser("JOE").then(lookupFollowers);
+
+//  followersForUser :: String -> Promise [UserId]
+var followersForUser = R.composeP(lookupFollowers, lookupUser);
+followersForUser("JOE").then(followers => console.log("Followers:", followers));
+// Followers: ["STEVE","SUZY"]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+var classyGreeting = (firstName, lastName) =>
+  "The name's " + lastName + ", " + firstName + " " + lastName;
+var yellGreeting = R.compose(
+  R.toUpper,
+  classyGreeting
+);
+yellGreeting("James", "Bond"); //=> "THE NAME'S BOND, JAMES BOND"
+
+R.compose(
+  Math.abs,
+  R.add(1),
+  R.multiply(2)
+)(-4); //=> 7
+
+//  get :: String -> Object -> Maybe *
+var get = R.curry((propName, obj) => Maybe(obj[propName]));
+
+//  getStateCode :: Maybe String -> Maybe String
+var getStateCode = R.composeK(
+  R.compose(
+    Maybe.of,
+    R.toUpper
+  ),
+  get("state"),
+  get("address"),
+  get("user")
+);
+getStateCode({ user: { address: { state: "ny" } } }); //=> Maybe.Just("NY")
+getStateCode({}); //=> Maybe.Nothing()
+
+var db = {
+  users: {
+    JOE: {
+      name: "Joe",
+      followers: ["STEVE", "SUZY"]
+    }
+  }
+};
+
+// We'll pretend to do a db lookup which returns a promise
+var lookupUser = userId => Promise.resolve(db.users[userId]);
+var lookupFollowers = user => Promise.resolve(user.followers);
+lookupUser("JOE").then(lookupFollowers);
+
+//  followersForUser :: String -> Promise [UserId]
+var followersForUser = R.composeP(
+  lookupFollowers,
+  lookupUser
+);
+followersForUser("JOE").then(followers => console.log("Followers:", followers));
+// Followers: ["STEVE","SUZY"]
+
+`;
+
+exports[`ramda_pipe.js 1`] = `
+var f = R.pipe(Math.pow, R.negate, R.inc);
+
+f(3, 4); // -(3^4) + 1
+
+//  parseJson :: String -> Maybe *
+//  get :: String -> Object -> Maybe *
+
+//  getStateCode :: Maybe String -> Maybe String
+var getStateCode = R.pipeK(
+  parseJson,
+  get("user"),
+  get("address"),
+  get("state"),
+  R.compose(Maybe.of, R.toUpper)
+);
+
+getStateCode('{"user":{"address":{"state":"ny"}}}');
+//=> Just('NY')
+getStateCode("[Invalid JSON]");
+//=> Nothing()
+
+//  followersForUser :: String -> Promise [User]
+var followersForUser = R.pipeP(db.getUserById, db.getFollowers);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+var f = R.pipe(
+  Math.pow,
+  R.negate,
+  R.inc
+);
+
+f(3, 4); // -(3^4) + 1
+
+//  parseJson :: String -> Maybe *
+//  get :: String -> Object -> Maybe *
+
+//  getStateCode :: Maybe String -> Maybe String
+var getStateCode = R.pipeK(
+  parseJson,
+  get("user"),
+  get("address"),
+  get("state"),
+  R.compose(
+    Maybe.of,
+    R.toUpper
+  )
+);
+
+getStateCode('{"user":{"address":{"state":"ny"}}}');
+//=> Just('NY')
+getStateCode("[Invalid JSON]");
+//=> Nothing()
+
+//  followersForUser :: String -> Promise [User]
+var followersForUser = R.pipeP(
+  db.getUserById,
+  db.getFollowers
+);
+
+`;
+
+exports[`redux_compose.js 1`] = `
+import { createStore, applyMiddleware, compose } from 'redux';
+import thunk from 'redux-thunk';
+import DevTools from './containers/DevTools';
+import reducer from '../reducers';
+
+const store = createStore(
+  reducer,
+  compose(
+    applyMiddleware(thunk),
+    DevTools.instrument()
+  )
+)
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+import { createStore, applyMiddleware, compose } from "redux";
+import thunk from "redux-thunk";
+import DevTools from "./containers/DevTools";
+import reducer from "../reducers";
+
+const store = createStore(
+  reducer,
+  compose(
+    applyMiddleware(thunk),
+    DevTools.instrument()
+  )
+);
+
+`;
+
+exports[`redux_connect.js 1`] = `
+const ArtistInput = connect(mapStateToProps, mapDispatchToProps, mergeProps)(Component);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const ArtistInput = connect(
+  mapStateToProps,
+  mapDispatchToProps,
+  mergeProps
+)(Component);
+
+`;
+
+exports[`rxjs_pipe.js 1`] = `
+import { range } from 'rxjs/observable/range';
+import { map, filter, scan } from 'rxjs/operators';
+
+const source$ = range(0, 10);
+
+source$.pipe(
+  filter(x => x % 2 === 0),
+  map(x => x + x),
+  scan((acc, x) => acc + x, 0)
+)
+.subscribe(x => console.log(x))
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+import { range } from "rxjs/observable/range";
+import { map, filter, scan } from "rxjs/operators";
+
+const source$ = range(0, 10);
+
+source$
+  .pipe(
+    filter(x => x % 2 === 0),
+    map(x => x + x),
+    scan((acc, x) => acc + x, 0)
+  )
+  .subscribe(x => console.log(x));
+
+`;
diff --git a/tests/functional_composition/functional_compose.js b/tests/functional_composition/functional_compose.js
new file mode 100644
index 000000000000..a60d7407e92f
--- /dev/null
+++ b/tests/functional_composition/functional_compose.js
@@ -0,0 +1,23 @@
+compose(
+  sortBy(x => x), 
+  flatten, 
+  map(x => [x, x*2])
+);
+
+somelib.compose(
+  sortBy(x => x), 
+  flatten, 
+  map(x => [x, x*2])
+);
+
+composeFlipped(
+  sortBy(x => x), 
+  flatten, 
+  map(x => [x, x*2])
+);
+
+somelib.composeFlipped(
+  sortBy(x => x), 
+  flatten, 
+  map(x => [x, x*2])
+);
diff --git a/tests/functional_composition/jsfmt.spec.js b/tests/functional_composition/jsfmt.spec.js
new file mode 100644
index 000000000000..aeb016da0388
--- /dev/null
+++ b/tests/functional_composition/jsfmt.spec.js
@@ -0,0 +1,1 @@
+run_spec(__dirname, ["flow", "babylon", "typescript"]);
diff --git a/tests/functional_composition/lodash_flow.js b/tests/functional_composition/lodash_flow.js
new file mode 100644
index 000000000000..afa84db09def
--- /dev/null
+++ b/tests/functional_composition/lodash_flow.js
@@ -0,0 +1,19 @@
+import { flow } from "lodash";
+
+const foo = flow(
+  x => x + 1,
+  x => x * 3,
+  x => x - 6,
+);
+
+foo(6);
+
+import * as _ from "lodash";
+
+const foo = _.flow(
+  x => x + 1,
+  x => x * 3,
+  x => x - 6,
+);
+
+foo(6);
diff --git a/tests/functional_composition/lodash_flow_right.js b/tests/functional_composition/lodash_flow_right.js
new file mode 100644
index 000000000000..5afd60bf239e
--- /dev/null
+++ b/tests/functional_composition/lodash_flow_right.js
@@ -0,0 +1,19 @@
+import { flowRight } from "lodash";
+
+const foo = flowRight(
+  x => x + 1,
+  x => x * 3,
+  x => x - 6,
+);
+
+foo(6);
+
+import * as _ from "lodash";
+
+const foo = _.flowRight(
+  x => x + 1,
+  x => x * 3,
+  x => x - 6,
+);
+
+foo(6);
diff --git a/tests/functional_composition/ramda_compose.js b/tests/functional_composition/ramda_compose.js
new file mode 100644
index 000000000000..6ccfc238f913
--- /dev/null
+++ b/tests/functional_composition/ramda_compose.js
@@ -0,0 +1,38 @@
+var classyGreeting = (firstName, lastName) =>
+  "The name's " + lastName + ", " + firstName + " " + lastName;
+var yellGreeting = R.compose(R.toUpper, classyGreeting);
+yellGreeting("James", "Bond"); //=> "THE NAME'S BOND, JAMES BOND"
+
+R.compose(Math.abs, R.add(1), R.multiply(2))(-4); //=> 7
+
+//  get :: String -> Object -> Maybe *
+var get = R.curry((propName, obj) => Maybe(obj[propName]));
+
+//  getStateCode :: Maybe String -> Maybe String
+var getStateCode = R.composeK(
+  R.compose(Maybe.of, R.toUpper),
+  get("state"),
+  get("address"),
+  get("user")
+);
+getStateCode({ user: { address: { state: "ny" } } }); //=> Maybe.Just("NY")
+getStateCode({}); //=> Maybe.Nothing()
+
+var db = {
+  users: {
+    JOE: {
+      name: "Joe",
+      followers: ["STEVE", "SUZY"]
+    }
+  }
+};
+
+// We'll pretend to do a db lookup which returns a promise
+var lookupUser = userId => Promise.resolve(db.users[userId]);
+var lookupFollowers = user => Promise.resolve(user.followers);
+lookupUser("JOE").then(lookupFollowers);
+
+//  followersForUser :: String -> Promise [UserId]
+var followersForUser = R.composeP(lookupFollowers, lookupUser);
+followersForUser("JOE").then(followers => console.log("Followers:", followers));
+// Followers: ["STEVE","SUZY"]
diff --git a/tests/functional_composition/ramda_pipe.js b/tests/functional_composition/ramda_pipe.js
new file mode 100644
index 000000000000..fa5a220effe2
--- /dev/null
+++ b/tests/functional_composition/ramda_pipe.js
@@ -0,0 +1,23 @@
+var f = R.pipe(Math.pow, R.negate, R.inc);
+
+f(3, 4); // -(3^4) + 1
+
+//  parseJson :: String -> Maybe *
+//  get :: String -> Object -> Maybe *
+
+//  getStateCode :: Maybe String -> Maybe String
+var getStateCode = R.pipeK(
+  parseJson,
+  get("user"),
+  get("address"),
+  get("state"),
+  R.compose(Maybe.of, R.toUpper)
+);
+
+getStateCode('{"user":{"address":{"state":"ny"}}}');
+//=> Just('NY')
+getStateCode("[Invalid JSON]");
+//=> Nothing()
+
+//  followersForUser :: String -> Promise [User]
+var followersForUser = R.pipeP(db.getUserById, db.getFollowers);
diff --git a/tests/functional_composition/redux_compose.js b/tests/functional_composition/redux_compose.js
new file mode 100644
index 000000000000..2cf62d4dd3ad
--- /dev/null
+++ b/tests/functional_composition/redux_compose.js
@@ -0,0 +1,13 @@
+import { createStore, applyMiddleware, compose } from 'redux';
+import thunk from 'redux-thunk';
+import DevTools from './containers/DevTools';
+import reducer from '../reducers';
+
+const store = createStore(
+  reducer,
+  compose(
+    applyMiddleware(thunk),
+    DevTools.instrument()
+  )
+)
+
diff --git a/tests/functional_composition/redux_connect.js b/tests/functional_composition/redux_connect.js
new file mode 100644
index 000000000000..4d9e1965a4dc
--- /dev/null
+++ b/tests/functional_composition/redux_connect.js
@@ -0,0 +1,1 @@
+const ArtistInput = connect(mapStateToProps, mapDispatchToProps, mergeProps)(Component);
diff --git a/tests/functional_composition/rxjs_pipe.js b/tests/functional_composition/rxjs_pipe.js
new file mode 100644
index 000000000000..75ee5324ad6f
--- /dev/null
+++ b/tests/functional_composition/rxjs_pipe.js
@@ -0,0 +1,11 @@
+import { range } from 'rxjs/observable/range';
+import { map, filter, scan } from 'rxjs/operators';
+
+const source$ = range(0, 10);
+
+source$.pipe(
+  filter(x => x % 2 === 0),
+  map(x => x + x),
+  scan((acc, x) => acc + x, 0)
+)
+.subscribe(x => console.log(x))