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 support for pipe operator, function currying, and partials #75

Closed
francescoagati opened this issue Dec 20, 2022 · 20 comments
Closed
Labels
proposal Proposal or discussion about a significant language feature

Comments

@francescoagati
Copy link

Livescript support piping partials and curry function. Should be nice have this in Civet

@STRd6
Copy link
Contributor

STRd6 commented Dec 20, 2022

Thanks for the suggestions!

We'll probably add the |> pipe operator at some point.

I don't think we'll add LiveScript's operators as functions, instead we have & function syntax which covers a lot of that same need.

list.map &+1
people.map &.name
---
list.map($=>$+1)
people.map($=>$.name)

I also don't think we'll add --> curried functions but I'm open to be convinced with examples.

STRd6 pushed a commit that referenced this issue Dec 26, 2022
* Add support for |> pipelines

Related: #75

* Support yield and await in pipelines
@edemaine
Copy link
Collaborator

We now have the pipe operator |>, but I think we're still lacking in what are called placeholders in the TC39 proposal, which I think is a natural way to support partials/currying. Copying from #83 (comment) :

I imagine it's really common to want to pass the piped value in as a first argument to a function, with other arguments given explicitly. What we want is shorthand for ($) => func($, foo, bar).

  • One proposal on Discord was func((&), foo, bar), where each extra layer of parens around & pushes the function wrapper up a level.
  • Another option might be func(&&, foo, bar), where each additional & pushes the function wrapper up a level.
  • A bolder option would be to redefine the meaning of func(&, foo, bar) in the context of the right-hand side of |> (probably a bad idea?).
  • The TC39 proposal suggests func(%, foo, bar) or various other notation that is specific to pipelining; for example, ^^ might mean the left-hand side of |> (no matter how deep we are), or we could optionally add an identifier like |foo> to mean "wrap the right-hand side in a function with single argument foo). For example, |$> func($, foo, bar) would be equivalent to |> ($) => func($, foo, bar).

Not sure what's best, but it kind of highlights the limitation of the current & notation... yet it feels like we're close.

@STRd6
Copy link
Contributor

STRd6 commented Dec 26, 2022

I think using explicit arrow functions as a way to re-order arguments or specify a local alias is good. That way you can add type annotations and make use of destructuring and everything should compose fine. This way we can combine with existing language features rather than making special purpose syntaxes for every situation.

x + 1 |> ($) => $ * $
credentials
|> login
|> getUser
|> ({id}) => register id, 1

@STRd6 STRd6 changed the title Add support for pipe operator curryand partials Add support for pipe operator, function currying, and partials Dec 30, 2022
@edemaine edemaine added the proposal Proposal or discussion about a significant language feature label Jan 4, 2023
@sultan99
Copy link

sultan99 commented Jan 8, 2023

I have noticed the -> is taken for the function declaration.
It could be great if it were for curried functions.
Just an example:

fun add(x::number, y::number)
  x + y

// equals 👇
function add(x: number, y: number) {
  return x + y
}

add := (x:: number, y:: number) => x + y
// equals 👇
const add = (x: number, y: number) => x + y

add := (x:: number, y:: number) -> x + y
// equals 👇
const add = curry((x: number, y: number) => x + y)

// 🧂add can be called:
add(1, 2)
add(1)(2)


// usage
input := 4

// pipe version
input
  |> add 3
  |> subtract 2
  |> console.log
 
// function composition
compute := compose(
  console.log
  minus 2
  add 3
)

compute input

// prints 👉 5

Or maybe we can use '>->' ?
image

Many monospaced fonts have that array in ligatures.

@STRd6
Copy link
Contributor

STRd6 commented Jan 8, 2023

@sultan99 Thanks that's a great example for how curried functions can combine with the pipe operator.

I think using LiveScript's --> or your suggestion of >-> for a curried function declaration is something we could add.

@sultan99
Copy link

sultan99 commented Jan 8, 2023

@STRd6, any of these can be!

>-> & --> monolisa & fira fonts have them:
image

@gustavopch
Copy link

What about ~>?

@STRd6
Copy link
Contributor

STRd6 commented Jan 8, 2023

What about ~>?

Ideally whichever we choose would have both ==> and --> versions to correspond to the two ES flavors of functions.

@sultan99
Copy link

sultan99 commented Jan 8, 2023

I have checked the ligatures, and we have all of them!

== === != !== > --> => ==> ~> >->
👇
image

I think ==> & --> are the best:

add := (x:: number, y:: number) ==> x + y
// equals 👇
const add = curry((x: number, y: number) => x + y)

add := (x:: number, y:: number) --> x + y
// equals 👇
const add = curry(
  function(x: number, y: number) {
    return x + y
  }
)

In the second example, we can use this.
Not sure why but anyway:

add := (x:: number, y:: number) -->
  console.log(@.name)
  x + y

@sultan99
Copy link

sultan99 commented Jan 8, 2023

Guys, it would be sexy syntax!

toJson := res => res.toJson()
pick := (key:: keyof T, obj:: T) ==> obj[key]


id := 123
fetch 'api/users/:#{id}'
  .then toJson
  .catch pick 'message'
  .finally console.log

@orenelbaum
Copy link

If we're using the ==> operator, why do we need the :: instead of normal :?
As for the compilation output, not sure you need a utility function, you could just compile it this way

add := (x:: number, y:: number) ==> x + y
const add = (x: number) => (y: number) => x + y

add := (x:: number, y:: number) --> x + y
const add = (x: number) => function (y: number) { return x + y }

@sultan99
Copy link

sultan99 commented Jan 8, 2023

I have seen :: in the discord chat as a suggestion to use it for typing.
I'm not familiar with syntax yet and I'm still confusing proposals with real syntax.
Sorry...

@gustavopch
Copy link

@sultan99 The :: is for #126, where we plan to provide a more concise way to destructure and type parameters at the same time.

@kabo
Copy link

kabo commented Feb 28, 2023

Not sure if this is the right ticket for what I'm after, let me know if not and I'll create a new ticket instead.

I use ramda a lot and I'm curious if civet could support a few things natively. Something I do a lot with ramda is create a function using pipe. For example:

const doTheThing = R.pipe(
  R.pluck('myprop'),
  R.map(R.toUpper)
)

console.log(doTheThing([{myprop: 'hello'}])) // outputs [HELLO]

My attempt at doing this with civet

input := [myprop: 'hello']
pluck := (prop) => .map .`${prop}`

// works
input
  |> pluck 'myprop'
  |> .map .toUpperCase() 
  |> console.log

// doesn't do what I want it to do
doTheThing := pluck 'myprop' |> .map .toUpperCase()

// doesn't work either, but in a different way
doTheThing2 := .map .myprop |> .map .toUpperCase()

console.log doTheThing2 input

doTheThing comes out as

const doTheThing = pluck("myprop").map(($3) =>
  $3.toUpperCase()
);

// as the first thing in the pipe is a function I would have wanted
const doTheThing = ($) => pluck("myprop")($).map(($3) =>
  $3.toUpperCase()
);

doTheThing2 comes out as

const doTheThing2 = (($4) =>
  $4.map(($5) => $5.myprop)).map(($6) =>
  $6.toUpperCase()
);

// I would have wanted (note one less paren after $5.myprop, it's added to the last line instead
const doTheThing2 = (($4) =>
  $4.map(($5) => $5.myprop).map(($6) =>
  $6.toUpperCase()
));

playground link

Or perhaps I'm just using it wrong, perhaps the pipe operator |> is only intended to be used when there's a start value, and there's something like fp-ts flow operator that I'm missing? Pipe vs flow in fp-ts.

@orenelbaum
Copy link

the operator works only on a start value, you are looking for a function composition operator (flow in fp-ts) which we discussed a bit in the Discord.

@STRd6
Copy link
Contributor

STRd6 commented Mar 1, 2023

@kabo this might work but I agree that it would be nice to have a cleaner syntax for function composition kinds of things.

doTheThing := ($) => (pluck 'myprop' |> .map .toUpperCase())($)

@kabo
Copy link

kabo commented Mar 1, 2023

ok, so I can go ahead and create a ticket for a function composition / flow operator?

@STRd6
Copy link
Contributor

STRd6 commented Mar 1, 2023

@kabo Sounds good. I don't think we have another issue specifically for composition / flow yet. There are some discussions and maybe an issue that are related but we can link them in later.

Thanks for the detailed report and clear description!

@danielbayley
Copy link

danielbayley commented Feb 5, 2024

I think ==> & --> are the best:

@sultan99 Agreed, out of those… There is also =>> and ->>, which haven’t been mentioned, and I think visually express currying quite well… also have ligatures. cc: @STRd6 @edemaine

@edemaine
Copy link
Collaborator

edemaine commented May 1, 2024

Closing this as:

  • Pipelines have been implemented
  • Partials have been implemented: f(x, ., y) compiles to $ => f(x, $, y), especially useful in pipelines
    • Note that we still don't have a pipeline placeholder operator %. There are some limited cases not yet covered, like |> foo(a, bar(b, %)). But we can revisit that in the future.
  • Currying has a separate issue Currying #818
  • Composition has a separate issue Proper compose operator #1112

@edemaine edemaine closed this as completed May 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
proposal Proposal or discussion about a significant language feature
Projects
None yet
Development

No branches or pull requests

8 participants