char0n / ramda-adjunct

Ramda Adjunct is the most popular and most comprehensive set of functional utilities for use with Ramda, providing a variety of useful, well tested functions with excellent documentation.
https://char0n.github.io/ramda-adjunct/
BSD 3-Clause "New" or "Revised" License
682 stars 86 forks source link

minList, maxList, minListBy, maxListBy #415

Open Undistraction opened 6 years ago

Undistraction commented 6 years ago

Is your feature request related to a problem? Please describe. Finds a min/max value in an array. These functions will be partial only. They will not be total as we need to return undefined when provided array is empty.

Describe the solution you'd like

const minListBy = curry((fn, list) => converge(R.reduce(R.minBy(fn)), [head, identity])(list))
const maxListBy = curry((fn, list) => converge(R.reduce(R.maxBy(fn)), [head, identity])(list))
const minList = minListBy(identity)
const maxList = maxListBy(identity)

const list = [12, 77, 16, 6, -40, -12, 99, 121]

minList(list) // -40
maxList(list) // 121

const addDigits = compose(reduce(add, 0), map(Number), split(''), toString)

minListBy(addDigits, list) // 12
maxListBy(addDigits, list) // 99

Describe alternatives you've considered

R.reduce(R.maxBy(R.identity), 0, [-4, -5, 3]); //=> 3

Additional context

This ramda issue explains why these functions were rejected from ramda.

char0n commented 6 years ago

The answer seems to be the same as for RA.concatAll. We return the undefined. One can lift the e.g. maxListBy into monadic context and it becomes Nothing monad at the end.

Example:


const safeMaxListBy = (list) => {
  if (isEmptyArray(list)) {  
    return Maybe.Nothing();
  } 
  return Maybe.Just(maxListBy(list));
}
``
Undistraction commented 6 years ago

Yep. I think undefined makes sense in the context of R and RA.

Might be nicer to offer another version though that has functions like these wrapped as in your example. That would be a good compromise.

char0n commented 6 years ago

The problem is that everybody is using different implementations of monadic types. You have ramda-fantasy, folktale, monet, sanctuary, we have our own RA.Identity (and more will come) etc.. How do you create a mondic abstraction on top of pure functional library ? The answer is you have to make it through tight coupling. That is why the ramda-sanctuary was proposed in last comment in that issue.

I've dealt with this problem in experiment called monad-t here https://github.com/char0n/monad-t, where I wanted to integrate multiple monadic libraries...But again, tight coupling...

Undistraction commented 6 years ago

Well I suppose given that the wrappers would be very thin you could provide wrappers for the most used libs. Even if it was just Folktale and Sanctuary that would cover a lot of usecases. You could generate the wrappers dynamically.

char0n commented 6 years ago

I think we should have our own monadic types. I already started the work here. Then we can provide some abstraction layer on top of functions like maxListBy or concatAll.

Undistraction commented 6 years ago

I think we should have our own monadic types

Isn't that just adding another implementation into the mix though? Isn't that going to mean that the consumers are going to have to deal with our own Monads themselves (rather than the other way around)?

char0n commented 6 years ago

Well your concerns are valid and I understand them. My vision with monadic types is to be used internally in our compositions and using monads should be prefered way for branching the composition logic (e.g. Maybe vs when) and dealing with various problems. Then I don't see a reason why not to expose them and continually add new types. Ramda is using monads heavily under the hood (e.g. lenses), but is creating them always in ad-hod manner. The end goal is to use R and RA and have full power of FP ecosystem ready to be used and integrated (pure functions + monads). We could use already available library but this brings the problem of dependency and total control of our codebase. We can then provide monad transformers that transforms our monads to other library monads using monad-t. Well this were my ideas when I started this project, it's questionable if they are still valid, but I still believe they are.

Undistraction commented 6 years ago

@char0n Using them internally makes absolute sense. My concern with making them a public part of the library is that it dilutes the purpose of the library - it starts to look like it is yet another lib offering some monads. I feel there should be a very clear distinction between RA and any monads it provides to keep RA focussed on providing utilities extending R. It should be really focussed, not another grab-bag of functional bits and pieces. Also I think exposing Monads we use internally publicly is not so different to using a third-party API - we become tied to a public API anyway (even if we do control it).

This is another aspect that points to the monorepo suggested by @guillaumearm.

I do see your side of the argument though and to some degree I'm playing devil's advocate.

wojpawlik commented 6 years ago

And, I'm not sure if maxListBy and minListBy are needed:

const longer = R.maxBy(R.length)
const longest = R.reduce(longer)
const longestString = longest('')

longestString(['foo', 'foobar', 'foooooooo'] //=> 'foooooooo'
longestString([]) //=> ''
Undistraction commented 6 years ago

@GingerPlusPlus I'm not sure who the 'we' you keep referring to is, but in all honesty you are introducing rather a lot of unhelpful noise in the issues of this project. Please consider your contributions more carefully and contribute more sparingly.