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

Emulating Maybe style branching #122

Closed
babakness opened this issue Apr 19, 2018 · 7 comments
Closed

Emulating Maybe style branching #122

babakness opened this issue Apr 19, 2018 · 7 comments

Comments

@babakness
Copy link

babakness commented Apr 19, 2018

It would be very useful to branch logic within a pipeline. Consider the Maybe Monad

https://mostly-adequate.gitbooks.io/mostly-adequate-guide/ch08.html#schr%C3%B6dingers-maybe

Maybe (no pun) we can have something like this, suppose <!> was a pipeline catch expression

class Nothing {} // or a Proxy that returns itself (Nothing) for all operations.
const Maybish = x => x ?? throw Nothing // TC39 Nullish Coalesce  + TC39 Throw Expressions

value 
  |> Number
  |> Maybish
  |> double
 <!> e => match ( e ) { ... } // TC39 Pattern Matching

Or something like that. Some way to differ evaluation to the end of a pipeline to handle a special case.

I realize the Maybish proposed here is not a Monad, just wanting to illustrate a way to branch.

@mAAdhaTTah
Copy link
Collaborator

Couple things:

  1. You can handle Maybe the old-fashioned way: mapping over its value. The Maybe monad will only call the callback if it's a Just and return the same Nothing if it isn't.

  2. I believe this is currently covered by the lifted pipeline proposal, so that may be worth getting involved with if this is something that interests you.

  3. I'm personally not willing to bloat the syntax of the pipeline more than it is for either proposal for the current push through the process.

  4. I'm not sure this is better than wrapping the pipeline in a standard try / catch:

    try {
    
      value 
        |> Number
        |> Maybish
        |> double
    } catch(e) {
      match ( e ) { ... } // TC39 Pattern Matching
    }

    We already have syntax for this. I don't think we need a pipeline-specific version.

@babakness
Copy link
Author

babakness commented Apr 19, 2018

Hey! Of course this is just to save keystrokes and without using the full Monad, just a way to branch the data flow. The pipeline operator itself can be emulated with a one-liner.

const pipeline = (input,...funcs) => funcs.reduce( (acc,fn) => fn(acc) , input )

pipeline(
  10,
  /* |> */ double,
  /* |> */ triple,
)

Thanks for the link, you're right in that :> would work well with functors. It is better but seems more of a stretch to get through, seems more niche than this.

I just saw # being proposed here, which seems like that could be its own proposal, and thought, well, this is something related to the pipeline.

# would be cool even without a pipeline.

const inc = # + 1

@mAAdhaTTah
Copy link
Collaborator

mAAdhaTTah commented Apr 20, 2018

@babakness The # can't stand without the pipeline; the major benefit of using # with the pipeline is it can be used with await & yield:

url
  |> await api.get(#)
  |> processResponse

Writing it without the pipeline doesn't make sense:

const awaitThing = await #

We can't assign this to a function, because await needs to be used within an async function.

@babakness
Copy link
Author

babakness commented Apr 20, 2018

I agree, the const foo = # thing felt good at first but now I see its confusing...

Yet the same feeling about that apply here. I feel this should really be its own operator |: or +> or whatever it is. You are thinking of nice situations but I can imagine a long line with a # buried. There should be a clear indicator.

url
  |: await api.get(#)
  |> processResponse

This is better. Because people will do this

const foo = url |> await api.get(#)

Or much worse

const T = x => f => f(x)

const foo = bar |> T(`... really long template. Call #800-333-5555  press # followed by 0 to get an operator. ${ # }`)

const foo = bar |> T(`... really long template. Call #800-333-5555  press # followed by 0 to get an operator.`)

In one situation we have T( textWithBarReplace ) and we return a function that takes a function
In the second situation it is either a syntax error or we have T(text)(bar)

Its confusing. With a |: you mandate #. It is always a syntax error without it. They are a necessary pair. With |> using # is always an error. This is better.

@mAAdhaTTah
Copy link
Collaborator

From the other thread:

This is basically what Smart Pipelines do now, but without needing two related but slightly different operators. We're unlikely to get multiple variants of the idea of pipelining through committee. This is part of the reason the bind operator stalled; pipeline operator handles the bind's pipelining features.

I personally think Smart Pipelines are complex enough as-is without needing to then remember separate rules for when you can and can't use the Lexical Topic. The second const foo = bar |> T(...) would error in the current Smart Pipeline without a Lexical Topic identifier. This is what makes the point-free / curried function use case asked in #116 harder to use.

@dead-claudia
Copy link
Contributor

You may be intrigued to hear that I've been looking into this thing myself, with a built-in nullable wrapper. I'm just attacking the issue from a more functional mindset than a syntax mindset.

@tabatkins
Copy link
Collaborator

Closing this issue, as the proposal has advanced to stage 2 with Hack-style syntax.

(However, extending piping to cover functor/monadic cases is very interesting, and would be great to pursue in the future.)

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 11, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants