Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fp export #564

Draft
wants to merge 16 commits into
base: main
Choose a base branch
from
Draft

Add fp export #564

wants to merge 16 commits into from

Conversation

seungrodotlee
Copy link
Contributor

Add es-toolkit/fp export to support functional programming and pipeline syntax.

// example

pipe(
  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
  filter(isEven),
  map(double),
  join(", "),
) // Expect: "4, 8, 12, 16, 20"

TODO

Scripts

  • Write a script to clone and convert existing functions into 'pipable' functions for pipelining.
  • Enhance a script to clone and convert existing specs to fp-version specs.

Codes

  • Write basic functions for fp (ex. pipe, map, filter ...)
  • Convert all available existing functions

Documents

  • Write documents about es-toolkit/fp

Copy link

vercel bot commented Sep 20, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
es-toolkit ✅ Ready (Inspect) Visit Preview 💬 Add feedback Nov 19, 2024 2:35pm

@gwansikk
Copy link
Contributor

gwansikk commented Oct 22, 2024

Hello, I find this task interesting and believe it will bring high productivity and convenience. Are there any additional updates? I’d love to collaborate on this.

How about structuring the API interface as shown below? (This is just one suggestion.)

import { array } from 'es-toolkit/fp';

array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).pipe(
  filter(isEven),
  map(double),
  join(", "),
) // Expect: "4, 8, 12, 16, 20"

This structure holds several potential advantages

  • Clear input, making the flow of data transformation intuitive.
  • Flexible scalability, allowing for future expansion not only in pipe but also in other interfaces.
  • Consistent API interface patterns that maintain uniformity.
  • Enhanced TypeScript type inference for greater precision.

@D-Sketon
Copy link
Contributor

D-Sketon commented Nov 1, 2024

any progress on this thread?

@seungrodotlee
Copy link
Contributor Author

While I am trying to work as quickly as possible, but it is not a very high priority, so I am falling behind. I hope to be able to work on it this week. 🥲

@seungrodotlee
Copy link
Contributor Author

The syntax for the pipe function is planned as follows.

function toString(value: unknown) {
  return `string:${value}`;
}

function toStringAsync(value: unknown) {
  return Promise.resolve(`string:${value}`);
}

function length(value: string) {
  return value.length;
}

const result = pipe(1, toString, length);
console.log(result); // 8

// Use pipe with async function
const asyncResult = await pipe(1, toStringAsync, length);
console.log(asyncResult); // 8

// Use pipe with curried function
const mapKeyResult = await pipe(
  { a: 1, b: 2 },
  mapKeys((value, key) => key + value)
);
console.log(mapKeyResult); // { a1: 1, b2: 2 }
  • receive the initial value as first parameter
  • receive functions that process the value from the second parameter onward
  • can handle async functions

@seungrodotlee
Copy link
Contributor Author

image

bench result of pipe

@sunrabbit123
Copy link
Contributor

As far as I remember, this team was not interested in that work, but it is a work that I am interested in.

But is there a reason why I have to copy the implementation?
It also looks good to refer to the original function with curring to take the consistency of the implementation.

const pipeableCountBy = <T>(fn: (arg: T) => boolean) => (arr: Array<T>) => countBy(arr, fn);
pipe(
   [1, 2, 3, 4],
   pipeableCountBy(v => v % 2 == 0)
);

If you're not going to make it in consideration of curring from the beginning, like fxts, that seems to be the best option.

The advantage of p.spipe comes as a delay assessment.
That's the linq of dotnet, that's the fxts, that's the effect-ts.

@seungrodotlee
Copy link
Contributor Author

Maybe we don't need to copy original implementation even if there is a script for copying it... I agree with that idea.

But it would be better to keep current design that 'auto-currying' the function by number of received argument to avoid situations like the one below.

// BAD
import { omit } from "es-toolkit";
import { omit as pipableOmit } from "es-toolkit/fp";

const omitted = omit(obj, ['a', 'b']); // usage without pipe

// ...

pipe(
  obj,
  pipableOmit(['a', 'b'])
); // usage with pipe
// GOOD
import { omit } from "es-toolkit/fp";

const omitted = omit(obj, ['a', 'b']); // usage without pipe

// ...

pipe(
  obj,
  omit(['a', 'b'])
); // usage with pipe

I gonna fix the implementation and script to refer it, not copy the implementation of original one.

@sunrabbit123
Copy link
Contributor

I think that's a good idea, too.
The core of my story is to refer to the feature implementation to follow the original, but only to change the interface.

@seungrodotlee
Copy link
Contributor Author

seungrodotlee commented Nov 19, 2024

Move functions to fp

array

  • at
  • chunk
  • compact
  • countBy
  • difference
  • differenceBy
  • differenceWith
  • drop
  • dropRight
  • dropRightWhile
  • dropWhile
  • fill
  • flatMap
  • flatMapDeep
  • flatten
  • flattenDeep
  • forEachRight
  • groupBy
  • head
  • initial
  • intersection
  • intersectionBy
  • intersectionWith
  • isSubset
  • keyBy
  • last
  • maxBy
  • minBy
  • orderBy
  • partition
  • pullAt
  • sample
  • sampleSize
  • shuffle
  • sortBy
  • tail
  • take
  • takeRight
  • takeRightWhile
  • takeWhile
  • toFilled
  • union
  • unionBy
  • unionWith
  • uniq
  • uniqBy
  • uniqWith
  • unzip
  • unzipWith
  • without
  • xor
  • xorBy
  • xorWith
  • zip
  • zipObject
  • zipWith

function

  • after
  • ary
  • before
  • curry
  • curryRight
  • debounce
  • flow
  • flowRight
  • identity
  • memoize
  • negate
  • noop
  • once
  • partial
  • partialRight
  • rest
  • spread
  • throttle
  • unary

math

  • clamp
  • inRange
  • mean
  • meanBy
  • median
  • medianBy
  • random
  • randomInt
  • range
  • rangeRight
  • round
  • sum
  • sumBy

object

  • clone
  • cloneDeep
  • flattenObject
  • invert
  • mapKeys
  • mapValues
  • merge
  • mergeWith
  • omit
  • omitBy
  • pick
  • pickBy
  • toMerged

string

  • camelCase
  • capitalize
  • constantCase
  • deburr
  • escape
  • escapeRegExp
  • kebabCase
  • lowerCase
  • lowerFirst
  • pad
  • pascalCase
  • snakeCase
  • startCase
  • trim
  • trimEnd
  • trimStart
  • unescape
  • upperCase
  • upperFirst

util

  • invariant

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants