Giter Club home page Giter Club logo

fpo's People

Contributors

amonks avatar christianhg avatar getify avatar gunar avatar josephfrazier avatar sebastiandedeyne avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fpo's Issues

Breaking: Make curry accept list of mandatory arguments

Hi Kyle, love this lib!

I've thought about this in the past as well.

From that discussion, it seems that it'd be better for named curry to accept the argument names instead of just the number of arguments. This is my experience as well.

Otherwise, we may fall into the following trap. See that we expected 'z' but got 'k'.

 function foo({ x,y,z }) { return x + y + z; }

 var f = FPO.curry( {fn: foo, n: 3} );

 f( {y: "b" } )()( {} )()( {x: "a", k: "c"} );
 // "ab"

I like https://github.com/rjmk/named-curry implementation better. Although more verbose, it's safer and more flexible.

Also, you get "optional arguments" for free.

I'd be willing to write the code, but it is a breaking change.

Rationale?

The approach to this library is interesting but I would like to hear from you, @getify: what was the reason(s) behind it?

Some of the points of interest you pointed out or I've noticed so far are:

  1. named-arguments replace argument position, meaning we need to remember names rather than position. (Which might be more intuitive for some)
  2. named-arguments do away with placeholders for partial application!
  3. variadic arguments aren't lost as a named-argument can be an array of any length (e.g. compose({ fns: [] }))

It would be great for you to expand on the above and say what you think are the advantages and draw backs to this approach! Thanks

reorg and improve build process

  • reorg files to be in more standardized locations (subdirs, etc)
  • change build process to add copyright header (version and year) to dist file automatically
  • add travis build
  • add badges

Support ES6 exports for tree shaking

This looks great! I was wondering about how one could either use only the functions needed via tree shaking, or will the build process allow a custom build to be generated with a few functions?

add some more methods

Want to add:

  • mapObj(..): like map(..) but across all enumerable owned properties
  • flatMapObj(..): like flatMap(..) but across all enumerable owned properties
  • filterObj(..): like filter(..) but across all enumerable owned properties
  • reduceObj(..): like reduce(..) but across all enumerable owned properties
  • take(..): like in Ramda
  • head(..) / tail(..): like in Ramda
  • memoize(..): like in Ramda
  • alias sequence(..) to pipe(..)
  • alias reject(..) to filterOut(..)
  • alias keep(..) to filterIn(..) (to mirror reject(..))

interesting quirk with named currying

Just got bitten by this quirk, but not sure if it's a bug or if we should leave it alone... thoughts?

function foo({x,y,z}) {
	console.log(x,y,z);
}

var f = FPO.curryMultiple( {fn: foo, n: 3} );

f( {x:1,y:2} )( {x:4,z:3} );

What would you expect that output to be? I expected 1,2,3 since x was already curried in the first call, but the result is that x:4 effectively overrides (re-curries?) the x:1. That was surprising to me and caused me a bug.

Should named curry(..) and curryMultiple(..) restrict each input to only being specified once, the first time?

Partially related to #19

Add: `composeN(..)` and `pipeN(..)`

Inspired by the curryN(..) from Ramda, these two would be loose-curried for a specified number of input functions. Allows lazy composition.

Is there an easy way to install only a function or two?

I'd like to use one of the functions in a project - but I don't want to depend on everything in this package.

For e.g. I'd like to use apply. Is my only option to copy-paste the relevant code? Are there / will there be "micro-lib" implementations of the functions in this package?

btw, I am aware that some build tools can detect unused code and remove it from the built bundle.

should we pick up some tricks/optimizations from Ramda?

Some interesting things from Ramda to consider adding to FPO:

  • _arity(..) is used to create a function with a proper .length, from the results of utils like curry(..), etc

  • utils like flip(..) will re-curry the function (in other words, assuming you always want currying)

Avoid abbreviated named arguments

I wanted to float an idea to take advantage of additional benefits from destructured object param function signatures... But first I want to include my thoughts on the pattern.

Here's an excerpt from an article I'm writing to explore the 'why' and trade-offs of this approach.


Destructured named object parameters is preferred to positional arguments in almost every case.

Inevitably, every design decision involves trade-offs. So, let me frame 2 priority goals, clarity and memorability, as more important than extreme DRY adherence.

  1. Clarity. Using the same names for function parameters AND in their invocations' arguments helps cement the pattern deeper in the brain. Flexibility in the key sequence lets you draw attention to contextually important parameters.
  2. Limit guessing game. Help people read, evaluate, and understand usage examples & implementation code faster, while reducing need to repeatedly check the functions signature ๐Ÿ˜ฐ(or relying on IDE features or keeping the docs open in another tab.)
  3. Improve Signal:Noise ratio. Positional arguments have virtually zero built-in indication of what each signifies. Related groups of names are naturally better "anchor points" in your brain.
  4. Better ergonomics for optional arguments (avoid needing to pass in an undefined argument).

Exceptions: There are a few use cases which I agree should be positional: for example the copy or mv file commands are a good fit for positional arguments. (A low, fixed arity tends to make things easier.)


With that considered, I feel argument names which are abbreviated or single letters are unnecessarily harder to memorize. Learning names is hard enough, and abbreviations invite too much confusion (common example are all over, see unix /usr path was originally UNIX source repository, now it's UNIX system resources, not user).

While it can be annoying typing accumulator in every reduce, it's unambiguous as it's not clear that an abbreviation would be acc instead of accum. Perhaps a non-standard term is a better fit on that one, say results or state. (This bit is a discussion for later...)

If single letter variables should be avoided in code generally (except in constructs like loops,) why are they ok in parameters when the knock-on effect is a leaky abstraction passing weirdness to every invocation.

Please let me know how you feel about my take.
I really appreciate your time Kyle!
Thanks in advance!

do we need named-argument aware `compose(..)` / `pipe(..)`?

All four of FPO.compose(..), FPO.std.compose(..), FPO.pipe(..), and FPO.std.pipe(..) do function composition where the output of the first function is pumped directly into the second one, not wrapped in an named-argument-object like { v: .. } before being passed in. That means you can't really compose a sequence of functions that all expect named-arguments without unapply(..)ing each subsequent function, such as:

var f = FPO.compose( {fns: [
   FPO.unapply( {fn: FPO.flatten, props: ["v"]} ),
   FPO.unapply( {fn: FPO.prop( {prop: 2} ), props: ["v"]} ),
   FPO.prop( {prop: "x"} )
]} );

var obj = { x: [1,[2,3,4],[5,[6,7],8],9] };

f( {v: obj} );
// [5,6,7,8]

So should we add a xxxNamed(..) -- I dunno what this naming should be! -- methods that does that wrapping. In other words, it would then be possible to do this:

var f = FPO.composeWHATEVER( {fns: [
   FPO.flatten,
   FPO.prop( {prop: 2} ),
   FPO.prop( {prop: "x"} )
]} );

var obj = { x: [1,[2,3,4],[5,[6,7],8],9] };

f( {v: obj} );
// [5,6,7,8]

In the context of the philosophy of FPO -- it's all about named-argument aware behavior! -- it seems like these methods should exist. As a matter of fact, we could even make the case this is how the FPO.compose(..) and FPO.pipe(..) themselves should work by default, leaving only FPO.std.compose(..) and FPO.std.pipe(..) to work more "normally" without the wrapping.

So... thoughts?

Should some named-args be renamed for more consistency?

Spun off from #3:

[For flatten(..), which uses v as its named argument...] what's the reasoning of not being arr in the first place?

Well, it's a judgement call and it could have gone either way. And TBH, I don't feel strongly that it has to be v over arr. But there's a bunch of other methods that take a single "value" argument to operate on, and in all those, we use v. There are other methods that take an array and call it arr, but those all take other params, too. For example, reduce(..) takes v for initial value and arr for the array.

FTR, my thinking was, is flatten(..) more like flatMap(..) or is it more like pick(..) or setProp(..)? I leaned to the latter instead of the former only because with flatMap(..), the arr is operated on by a fn, but with pick(..), the value (object in this case) is the main focus, so it's called v.

Of course, we have zip(..) which names its two params arr1 and arr2 instead of v1 and v2. I almost did that, for the same reason as v for flatten(..), but then I thought that v1 and v2 might be slightly confusing as "version 1" / "version 2".

So, I'm open to debate here, would like feedback. Should we:

  1. Change flatten(..) to use arr instead of v?
  2. Change zip(..) to use v1 / v2 instead of arr1 / arr2?
  3. ...any others?

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.