fantasyland / fantasy-land

Specification for interoperability of common algebraic structures in JavaScript
MIT License
10.12k stars 374 forks source link

Fantasy Land proposal process for ECMAScript #204

Open JAForbes opened 7 years ago

JAForbes commented 7 years ago

I'm proposing the fantasy-land community contributes more actively in the ECMAScript language design process. There's been some discussion on gitter, and we have at least 1 proposal idea.

I think it would be good to get a sense of what the process is, so if anyone has any suggestions please say so!

@davidchambers suggested @michaelficarra as a potential ally. I'd like to get @mindeavor's input as well with his experience proposing https://github.com/mindeavor/es-pipeline-operator

A candidate for our first proposal is https://github.com/fantasyland/function-prototype-map suggested by @puffnfresh

JAForbes commented 7 years ago

One administrative thing worth sorting out. Where should proposals be discussed? Should we have a separate repo on fantasy-land to namespace these discussions?

Interested in your thoughts on that @rpominov as you seemed hesitant on gitter.

SimonRichardson commented 7 years ago

Where should proposals be discussed? Should we have a separate repo on fantasy-land to namespace these discussions?

Yes please, esp. with long running discussions, it can get confusing.

rpominov commented 7 years ago

Maybe we should crete some placeholder repo like https://github.com/jsforum/jsforum for all general discussions that are not related to any particular repository? Something like fantasyland/forum.


But meanwhile and before I forgot I'd like to mention here another language feature, that potentially could make into a good proposal.

A special syntax for partial application of a function:

foo#(a, b) // desugars roughly to foo.bind(null, a, b)

Using this and pipe operator we could write code like this:

[1, 2]
 |> List.map#(x => x + 1)
 |> List.chain#(x => [x, x]) // [2, 2, 3, 3]

Which a smart enough compiler could desugar to:

List.chain(x => [x, x], List.map(x => x + 1, [1, 2]))
gilbert commented 7 years ago

@rpominov Adding # as syntax will be a rough and uphill battle. It's hard enough convincing to add papp to Function.prototype.

[1, 2]
  |> List.map.papp(x => x + 1)
  |> List.chain.papp(x => [x, x]) // [2, 2, 3, 3]

@JAForbes Re experience: You need a TC39 champion to be interested in your proposal to move it forward at all. Take note that they're all heavily biased towards OOP, so FP features, even a simple one as the pipeline operator, will likely be seen as "bloat" or "unnecessary" (unlike classes and their bundle of supporting features, apparently).

xgrommx commented 7 years ago

@rpominov u can write some plugin for babel. For example https://www.sitepoint.com/understanding-asts-building-babel-plugin/

safareli commented 7 years ago

btw we can do so something like this using es6 template literals:

const _  = someCoolName({
  '|>' : (L,R) => R(L),
  '>>=': (L,R) => L.chain(R), 
  '<*>': (L, R) => R.ap(L),  
  '<$>': (L, R) => R.map(L), 
})

const res1 = _`
  ${[10]}
    |>  ${a => a.concat([11])}
    >>= ${a => [a + 1]}
    >>= (
      |> ${a => a * 2} 
      |> ${a => [a, a - 1]}
    )
` // [22, 21, 24, 23]

const res2 = _`
  ${[
    a => b => a + b,
    a => b => a - b,
  ]}
    <*> ${[1,2]}
    <*> ${[3,4]}
` // [4, 5, 5, 6, -2, -3, -1, -2]

const res2 = _`
  ${[
    a => b => a + b,
    a => b => a - b,
  ]}
    <*> ${[1,2]}
    <*> ${[3,4]}
` // [4, 5, 5, 6, -2, -3, -1, -2]

const res3 = _`
  ${getCurrentLocation /*:: IO Location*/}
    >>= ${getHashFromLocation /*:: Location -> IO hash*/}
    >>= ${hash => fetch /*:: [URL] -> IO [Responce]*/([
      `/api/users/${userIDFromHash(hash)}`,
      `/api/posts/${postIDFromHash(hash)}`,
    ])}
    >>= ${([uRes, pRes]) => _`
      ${makePage /*:: [User] -> [Post] -> Page */}
        <$> ${userFromResponce(uRes)}
        <*> ${pageFromResponce(pRes)}
    `}
` // :: IO Page
rpominov commented 7 years ago

Adding # as syntax will be a rough and uphill battle. It's hard enough convincing to add papp to Function.prototype.

Sad to hear that. Function.prototype.papp proposal looks great, btw!

Take note that they're all heavily biased towards OOP, so FP features, even a simple one

This is also very sad, although I've suspected this. Seems like overall attitude haven't changed that much since https://github.com/promises-aplus/promises-spec/issues/94 .

Very curious about @michaelficarra's perspective, hopefully it's not so bad.

xgrommx commented 7 years ago

@rpominov @safareli As you remember bilby http://bilby.brianmckenna.org/#do-operator-overloading

JAForbes commented 7 years ago

@mindeavor thanks for the info! That's very helpful.

JAForbes commented 7 years ago

I'm going to say a few blunt harsh truths I think we need to internalise if we are going to have any success.

We are walking into a world where OO is king, Haskell is a symbol for the programming elite, and profit matters.

We need to acknowledge some hard truths, Javascript's history is political, born out of a corporate war, designed in 10 days as a result. Large corporate entities vie for control of the web and they all have their own particular slant. "Anyone can be a part of TC39" as long as your company is willing to pay a fee.

Look at the list of members: http://tc39wiki.calculist.org/about/people/ the overwhelming voice in the committee are large corporate entities that want to make their particular vision of the web happen.

Its not a democracy, its an oligarchy, we need to admit that up front if we are going to come up with a strategy that makes any difference at all.

We need to figure out up front, what our goals are, what the obstacles are, and how do we communicate those goals. Our changes need to be in the committees interests, in other words they need to save corporations money, or make corporations money.

As for ESDiscuss, I'm not sure what role that mailing list plays, but from the threads I've read, getting a receptive response relies on the readership to understand what the feature is and why it is beneficial. I think we'll hit a wall if we get too abstract here, if we get bogged down in theory or elegance. We need to talk about why a given feature will lead to less bugs, or less code, or simplify implementations of well known libraries. We need to relate our proposals back to the mainstream e.g. how would this feature be used in a React app? And we need to assume absolutely 0 knowledge or appreciation of functional programming - in fact it is probably safer to presume antagonism.

Its easy to get defensive, but that won't help us change the language. We need to be calm and patient and ultimately we will need to suffer ignorance graciously. We need to make our case on their terms because at this point we need them and they don't need us.

We have to keep in mind, we are not trying to convince tc39 that FP is the right direction, we are trying to save millions of JS developers time by giving them access to a better set of tools.

We need to lean on the goodwill of the subset of FP that OO people like (Rx/Observables, linq, Elm, F# and React, FlowType/TS). We need to tread lightly referencing Haskell, ML, Categories, Monads etc. We can't start there. We have to be realistic of the terrain. Let's try and get one proposal into the language to prove the value of our counsel, to dispel the myth that FP has no real benefits, that it is all "theoretical, ivory tower, academic nonsense". I know that is the prevailing perspective, we've all seen it I'm sure.

I know how hard that it is to hear, but we know better. It is on our shoulders to communicate the value downstream.

I think we need to stop talking about libraries/transpilers/hacks to make JS better. We are a community who deserves a voice in the process but to date our default reaction is to write a new language, or create a library etc.

Those reactions are all valid and worthy and should be continued. But we need to also direct our collective force at the web platform itself. I think it will be hard, I think we'll need to form a careful strategy, but so many JS users will benefit if our counsel is heard.

Evan Czaplicki's "Let's be Mainstream" talk comes to mind.

I also say this with absolute deference to the knowledge of this community. I know I have a lot to learn about FP, that I am in no way an expert in this field. But I do feel I am a bridge between this world and the other, I can see their connotations and I can empathise with them.

So let's come up with a list of proposals, discuss the pro's / cons of each proposal and different strategies. Maybe we want to put all our weight in one proposal, or maybe its better to hit them with many proposals simultaneously? Let's study previous successes and previous failures and learn from them. But let's not embark on a war of principles, let's not attempt to convert the JS community to "see the light" - that will never work, we need to identify our goals and guide our interactions with the committee to achieve those goals, nothing more.

JAForbes commented 7 years ago

I've got some input from @isiahmeadows re: es-discuss. You can subscribe to the mailing list here:

https://mail.mozilla.org/listinfo/es-discuss

And you can make proposals by posting an email to es-discuss@mozilla.org. The goal is to convince a member of tc39 to become a "champion" of your feature, which then moves your proposal to stage 0.

I don't think we should make any proposals yet, but that's just my view on it. But I'm going to subscribe and get a sense of the climate. Also turns out there is already a proposal for a composition operator in the mailing list: https://esdiscuss.org/topic/function-composition-syntax which might be of interest.

Here are some notes on contributing to ECMAScript

https://github.com/tc39/ecma262/blob/master/CONTRIBUTING.md

dead-claudia commented 7 years ago

Note: at the bottom of that email thread about function composition, here's my gist strawman for it. I'd definitely take suggestions on how to improve upon it. If we come up with something worth adding, that would still work within the confines of the language, this would be wonderful.

dead-claudia commented 7 years ago

Here's what I'm thinking:

  1. Standardize a basic set of protocols based on a subset of the basic algebras here. Let's not add the full package yet, but we can maybe round it out later. (I'm not too attached to the protocol names - they can change)

    • Setoid (equals)
    • Semigroup (concat)
    • Monoid (Semigroup + empty)
    • Functor (map)
    • Apply (Functor + ap)
    • Applicative (Apply + constructor.of)
    • Chain (Apply + chain)
    • Monad (Applicative + Chain)
    • Bifunctor (Functor + bimap)

    (Why include so little? Consider Promises, iterables, etc.: it's much easier to flesh out known, commonly implemented semantics. Most Promise implementations were ES6-compatible from the start, for example.)

  2. Propose that several existing types implement these protocols:

    • All primitive types (including boxed types) except null/undefined implement Setoid
    • Objects implement nothing
    • Functions implement Monad
    • Symbols implement Monad and Setoid
    • Arrays, TypedArrays, Maps, and Sets implement Monad, Monoid, and Setoid
    • WeakMaps and WeakSets implement nothing
    • %IteratorPrototype% implements Functor
    • Promises implement Monad and Bifunctor (return on right)
    • Proposal-wise: %AsyncIteratorPrototype% implements Functor, etc.

    Note that equals on collections works recursively, and that iterator.map(f)'s return value's prototype is %IteratorPrototype%.

  3. Push for the bind operator proposal (or some other pipelining operator) to gain better traction and increased priority.

  4. Propose a few new classes:

    • Option or Maybe, implements Thenable (with coercion, undefined error), Monad and Setoid (either one works)
    • right-biased Either, implements Thenable (flipped, with coercion to right), Monad, Bifunctor, and Setoid
    • return-biased Try, implements Thenable (with coercion), Monad, Bifunctor, and Setoid (for safer exception handling)

We should allow methods to accept arguments beyond what each protocol requires in the proposal itself, since some types with existing correct methods (like Array with Array.of, array.concat, etc.) accept more parameters than necessary.

As for polyfills for steps 2 and 4, I suspect it'd be extraordinarily straightforward to do. Promises will require a boilerplate object to prevent coercion with map/etc., but that's about the end of it. Note that the invariants of callbacks (e.g. matching prototypes for map) are checked, though, and failure is an immediate synchronous TypeError, or synchronous rejection in the case of Promises.

Note that those that implement Bifunctor also implement Thenable, with the arguments in reverse order and with coercion (like what Promises do today).


Oh, and with these, JS might start feeling a little like a lighter, more dynamic Scala.

gilbert commented 7 years ago

For the record, here's the discussion on the pipeline op: https://esdiscuss.org/topic/the-pipeline-operator-making-multiple-function-calls-look-great

JAForbes commented 7 years ago

@isiahmeadows that is a lot more ambitious than I would have expected we could be. But you do have a lot more experience in these matters.

Few questions:

  1. why does Object implement nothing, is that a performance concern?
  2. Do you think pushing for bind would help our cause in the future, or hinder it? I don't think many here are in favour of bind syntax (correct me if I'm wrong). Would getting bind into the language create the impression they've already catered to the FP community so pushing for compose/map/pipe would be harder? Or do you think just getting one proposal through the door would help future proposals?
  3. Why would synchronous types like Option implement thenable? That seems counter to the principle of Promises always be async, also I think this community would prefer map.
  4. Do you think we'd gain more traction if chain was renamed to flatMap when proposing monads?
  5. Do you think we should use terms like Monad/Setoid in esdiscuss? I'm worried it will create the impression that this feature is not useful for most people. But maybe its better to bite the bullet use the correct terms and just be gracious when introducing concepts and provide lots of examples? I hope for the latter I'm just concerned A+ will happen again.
JAForbes commented 7 years ago

Thanks @mindeavor

JAForbes commented 7 years ago

@SimonRichardson also I can't act on your request I'm not a member.

michaelficarra commented 7 years ago

Hey, everyone. Thanks for the mention. I think I can help out a bit here.

I think there have been some mischaracterisations of TC39 as a whole. TC39 has many members, and the representatives that participate have very diverse backgrounds and goals. It would be wrong to try to label the entire group as "anti-FP" or really subscribing to any sort of unified philosophy.

I also don't think that proposals of individual prototype methods are the best way for this community to get started contributing. And I know that the committee would never pull in the entire Fantasy Land protocol wholesale. Every proposal that the committee works on is motivated by real world usage, often already proven out by successful libraries/frameworks or implemented in compile-to-JS languages. So I think two strategies should be taken: build out as much of what you want to see in libraries/frameworks/languages (done here already; good job!) and propose features that will allow you to do more of this prototyping yourselves without involvement from the language authors. Let me give an example.

A few months ago, I prototyped the definition of algebraic interfaces if we were given a kind of "mixin" or "interface implementation" syntax on top of classes. I defined a couple of "type classes" which define their protocol using static symbols (to permanently avoid name collision), then use a new with syntax to extend or implement that interface. Now if I fudged the new syntax bits into some function calls (or just used sweet.js macros), I could get this working today as-is. And that would show that this feature has a real use case. And then, once it is in the language, that feature could be used to create an even more easily implementable and even better "standard protocol" like Fantasy Land. Once it gains enough traction, it would be a no-brainer for the committee to include something like it in the language.

So, at least in my opinion, this won't all happen at once. We need to take a bunch of small steps in a direction that will inevitably lead to the place we actually want to be. Push for an interface-like feature or macros to help in proving out prototypes. Symbols were a big win and we didn't even know it!

I love this community and this effort (I have implemented it in some of my own projects), so feel free to lean on me for advice regarding the TC39 process or to champion a proposal.

JAForbes commented 7 years ago

@michaelficarra Thank you for the advice. There are a lot of helpful insights there that I think we can act upon. I apologise for mis-characterising the committee themselves. My previous comment is really addressing systems and structures. I'm not assuming the members themselves have some ill intentions. That's some advice we can act upon productively.

This is probably a discussion for another time and another place - but I do think a lot of the criticisms and frustrations that are directed at the process are valid, and it would be disingenuous for me to pretend otherwise. Things have definitely improved, discussions are in the open, and are informed by the community. But there is a corporate story there, and whether or not it is intentional (I'm sure it isn't) there is an identifiable power imbalance. But I have no malice toward anyone, its all just people at the end of the day and its a hard thankless job.

It would be great if there was a process for iterating on the process and not just the language.

It seems there are already a lot of functional proposals in the mix, maybe a productive avenue for us is to research the existing proposals and see if they are something our community can get behind, and if not we can debate possible amendments to those proposals and add those amendments to the esdiscuss thread.

Maybe that is a good first step.

joneshf commented 7 years ago

I'm surprised Traversable wasn't in that list.

SimonRichardson commented 7 years ago

@SimonRichardson also I can't act on your request I'm not a member.

That can be arranged 😛

JAForbes commented 7 years ago

@SimonRichardson @rpominov So which way should we go? A repo for tc39 discussions, or a general forum repo for discussions not directly related to any particular project?

rpominov commented 7 years ago

One point for a general repo is discoverability. Say we have an issue discussing creation of a new library there, someone comes to that issue from a link and finds discussions about ES proposals in other issues in same repo.

But I guess either way is good anyway.

dead-claudia commented 7 years ago

@JAForbes Sorry for the late response, but in reply to this comment:

why does Object implement nothing, is that a performance concern?

Practical concern. You could argue for equals being this === other by default, but it wouldn't make sense for it to implement the rest, like, say, map or concat. Otherwise, they'd bleed into other types like functions (which shouldn't implement concat) or strings (which shouldn't implement map).

Do you think pushing for bind would help our cause in the future, or hinder it? I don't think many here are in favour of bind syntax (correct me if I'm wrong). Would getting bind into the language create the impression they've already catered to the FP community so pushing for compose/map/pipe would be harder? Or do you think just getting one proposal through the door would help future proposals?

Why would synchronous types like Option implement thenable? That seems counter to the principle of Promises always be async, also I think this community would prefer map.

This was initially for compatibility with Promises, but it'd be mostly useful for those who want auto-absorption semantics (like what exist with Promises). Admittedly, when I write Option types, I myself include absorption just to simplify the implementation and avoid boilerplate.

Do you think we'd gain more traction if chain was renamed to flatMap when proposing monads?

Yes, and IMHO that would be preferable. chain is a little odd compared to, say, Java's Stream.flatMap or Rx.js's Observable.prototype.flatMap.

Do you think we should use terms like Monad/Setoid in esdiscuss? I'm worried it will create the impression that this feature is not useful for most people. But maybe its better to bite the bullet use the correct terms and just be gracious when introducing concepts and provide lots of examples? I hope for the latter I'm just concerned A+ will happen again.

It might be better to use them (e.g. the term "monad" is starting to become better understood by those less versed in type theory), but with a thorough explanation of how each distinct concept works, why they are distinct concepts (e.g. tuples are applys that aren't applicatives, strings are monoids that aren't monads, among others), and how they relate to OO idioms. I'll note that monads will be easy (they're already using them), but applies that aren't applicatives may be a little harder.

I do suspect names might change as time goes on, mainly because the OO and FP communities don't use the same terminology. What we call monads, they call object wrappers. What we call comonads, they call builders and factories. What we call most monadic monoids, they call collections. Oh, and don't forget the similaries between SQL and FP. It's pretty obvious when you consider the F# version here as a direct translation of the SQL, just using immutable sequences instead:

-- SQL
SELECT Foo.One, Bar.Two FROM Foo
    INNER JOIN Bar ON Foo.Id = Bar.FooId
    WHERE Foo.Total < 100
    SORT BY Bar.Name ASC;
// F# equivalent
fooList
|> Seq.collect (fun foo ->
    barList
    |> Seq.filter (fun bar -> bar.FooId = foo.Id)
    |> Seq.map (fun bar -> (foo, bar))
|> Seq.filter (fun foo -> foo.Total < 100)
|> Seq.sortBy (fun (_, bar) -> bar.Name)
|> Seq.map (fun (foo, bar) -> (foo.One, bar.Two))
dead-claudia commented 7 years ago

@JAForbes

[...] that is a lot more ambitious than I would have expected we could be.

You can't exactly expect to get anywhere without at least a hint of idealism. :wink:

And this is merely adding a bunch of easy-to-polyfill methods initially.

dead-claudia commented 7 years ago

I was just thinking that, in the end, this may be better done with decorators and similar instead. Things like this:

@Functor
class Foo {
    map(f) {}
}

@Monad
class Bar {
    chain(f) {}
    static of(x) {}
}

// etc.

Decorators could fill in all the derivations as well. So, instead of having to implement everything, all you would actually need to implement for a basic List or Promise would be this:

@Monad
@Monoid
@Traversable
class List {
    static of() {}
    static empty() {}
    equals(other) {}
    concat(other) {}
    chain(other) {}
    traverse(f, T) {}

    // Derived:
    // map(f) {}
    // ap(f) {}
    // reduce(f, acc) {}
}

Furthermore, chainRec could be easily derived from any chain with a general function like this, mod the stack space requirement:

const result = done => value => ({done, value})
function chainRec(f, acc) {
    const {done, value} = f(result(false), result(true), acc)
    return done ? value : value.chain(v => chainRec(f, v))
}

It won't be trivial to both avoid blowing the stack and generalizing for both sync and async chains, but pull-stream's drain implementation would be highly informative in this area (they also have to deal with maybe-sync, maybe-async). IMHO, that belongs in Ramda/etc., not here. But I'll stop before this gets too off-topic.

JAForbes commented 7 years ago

@rpominov @SimonRichardson could either of you create the discussion repo? I don't have sufficient permissions.

rpominov commented 7 years ago

Here you go https://github.com/fantasyland/ECMAScript-proposals

JAForbes commented 7 years ago

Thank you @rpominov

safareli commented 7 years ago

btw, If we had a function composition operator in a language, VMs could optimize stack usage, when running composition of many functions without need to do it manually this way

JAForbes commented 7 years ago

@safareli great point.

I was hoping VMs could inline a lot of functions as well if the compositions are static.

masaeedu commented 7 years ago

@JAForbes Hell yeah for this. Some concepts from Promises/A+ are starting to get baked into the language syntax, duck typing and all, and I feel some design decisions are not being sufficiently contested and hashed out. Ironically, some of the problems (e.g. in https://github.com/tc39/proposal-async-iteration) directly stem from issues where input from the fantasy-land community was dismissed as, well, "fantasy land" suggestions:

dead-claudia commented 7 years ago

@masaeedu

Some concepts from Promises/A+ are starting to get baked into the language syntax, duck typing and all, and I feel some design decisions are not being sufficiently contested and hashed out.

I agree they weren't all thought through the best (the iterator protocol isn't particularly elegant), but I disagree about the specific argument with Promises/A+ - the thenable interface has been de-facto standardized in Promise libraries as an interop point since around when Angular was even a thing - jQuery adopted Promises/A+ interop, with absorption semantics, when version 2 was first released. So it's been around for quite a while, including the absorption semantics.

And in general in my experience, absorption makes Promise handling way easier, since I only need to care about "am I doing something asynchronous" rather than "did the caller make this asynchronous".

As for standardization points, I feel that there are some things that could be done to improve the lives of functional JS programmers, such as native Option, Try, and Either types (think of Scala's analogues) and some form of conditional pattern matching. You don't need to specifically assist any particular standard, and keep in mind, there's also the Static Land spec and lodash/fp, so they aren't likely to cater to any individual community without first consulting and identifying how it could help others.

masaeedu commented 7 years ago

@isiahmeadows The recursive flattening that Promises/A+ performs is a useful way of interoperating between the large constellation of promise libraries that have arisen for historical reasons. This doesn't mean then and its recursive flattening are a good abstraction to introduce directly into language constructs.

As a specification for libraries, or even as a class available in the standard library, I'm fine with how Promises behave. However things like await should operate on a more general abstraction than Promises, as is the case in other languages. The fact that a nested promises are reserved as an interoperability concept is baggage associated solely with promises, and should not affect how async functions and async iterators are specified.

jedwards1211 commented 6 years ago

I think there have been some mischaracterisations of TC39 as a whole. TC39 has many members, and the representatives that participate have very diverse backgrounds and goals. It would be wrong to try to label the entire group as "anti-FP" or really subscribing to any sort of unified philosophy.

I agree with this, ECMAScript evolution seems way less corporate and closed off to me than Java, or the way Eran Hammer complained about OAuth 2.0 getting hijacked by enterprise concerns, for instance. The practicality of how ECMAScript has evolved has exceeded my expectations.

There also seems to be an elitist tendency among Haskell users to only consider a certain style of functional programming to be true FP. But anyone who's written a single higher-order function is by definition a functional programmer, even if they barely understand any of the concepts in fantasy land (like me). So anyone who would label TC39 as anti-FP is really accusing them of being anti-algebraic FP, I guess.

jedwards1211 commented 6 years ago

I'd definitely like to see you guys use Flow or TypeScript syntax rather than Haskell syntax for describing types. That's one thing that smacks of wanting to stick with your current way of thinking and communicate amongst yourselves instead of with the JS community as a whole.

The longer a programmer sticks with any given style of programming, the more they develop a bias toward it and forget how to justify it from first principles and how applicable it is to concrete end goals.

jedwards1211 commented 6 years ago

@gilbert actually I think the pipeline operator is an example of something many JS devs will see as eminently practical. I certainly want it even though I'm skeptical of Fantasy Land in general.

I think it's a huge misconception that the JS community is biased toward OOP. None of the popular libraries I'm aware of use crazy inheritance hierarchies the way languages like Java or C++ that I came from did.

But whereas some people think the papp syntax is more elegant (though I do think it's a good improvement on bind), I prefer plain old lambdas because I think they're more obvious:

import {flatMap} from 'lodash'
[1, 2]
  |> (_ => _.map(x => x + 1))
  |> (_ => flatMap(_, x => [x, x])) // [2, 2, 3, 3]

Or an API like lodash/fp that does the partial application for you:

import {map, flatMap} from 'lodash/fp'
[1, 2]
  |> map(x => x + 1)
  |> flatMap(x => [x, x])

Sometimes it seems to me that there's a tendency for hardcore functional programmers not to want to express themselves in lambdas. For instance I know some would prefer to write:

[1, 2]
  |> map(add(1))
  |> flatMap(double)

I know in RamdaJS people seem to prefer to write always(42) rather than () => 42. But maybe that's just because some people are sticking to ES5 until lambdas are supported everywhere.

paldepind commented 6 years ago

@jedwards1211

I'd definitely like to see you guys use Flow or TypeScript syntax rather than Haskell syntax for describing types. That's one thing that smacks of wanting to stick with your current way of thinking and communicate amongst yourselves instead of with the JS community as a whole.

I think that is a very good idea. But, until TypeScript gets higher-kinded types it's unfortunately not powerful enough to express all of the concepts in Fantasy Land. We could invent our own syntax for higher-kinded types on top of TS/Flow and roll with that though.

joneshf commented 6 years ago

I think that is a very good idea. But, until TypeScript gets higher-kinded types it's unfortunately not powerful enough to express all of the concepts in Fantasy Land. We could invent our own syntax for higher-kinded types on top of TS/Flow and roll with that though.

Exactly! At first glance it seems like we're spitting in the face of flow/TS. But, that's not the case at all. They just can't encode the concepts we want to express.

If there were a JS type system that was expressive enough, I'm sure we'd gladly switch to it.

CrossEye commented 6 years ago

@jedwards1211:

I know in RamdaJS people seem to prefer to write always(42) rather than () => 42. But maybe that's just because some people are sticking to ES5 until lambdas are supported everywhere.

I think that's part of it. Another part is simply habit; that's the style promoted by Ramda for anything that smacks of partial application.

And I think part of it is also that words more easily translate to mental concepts than does punctuation. (While it's pretty easy to find where square is in map(n => n * n, vals), it's much easier to find it in map(square, vals).) The trade-off is not being sure which invocations are new function and which are final values. I usually find this trade-off worthwhile.

JAForbes commented 6 years ago

If there were a JS type system that was expressive enough, I'm sure we'd gladly switch to it.

Absolutely! I've tried many times to get Flow and Typescript to work for my projects and I'll keep trying. I'm confident we'll get there.

Also for the record I never said tc39 was anti-fp. I said it's safer if we assume antagonism, as in, let's be diplomatic, polite, and assume no prior knowledge and work within the existing framework that exists which may include certain assumptions about the FP community. It was an attempt at pragmatic advice based on prior experiences that we've had.

And honestly, I'm pretty disappointed with parts of the FP community beyond JS. I think there's a lot to improve. A lot of self defensive snarky jokes that don't explicitly mean harm but send a very clear message that if you aren't in on the joke you are a joke. There's all kind of things we can improve. I wish that stuff wouldn't happen because it makes this process more difficult.

I think the JS FP community is pretty great though. I've certainly felt extremely welcome. And I appreciate all the help and support I've had. I just really want this pointless divide to end and assumptions of bad faith to dissolve. OO vs FP is such a needless waste of energy and from my experience the FP devs in this community have almost no interest in that dichotomy.

I'm optimistic.

dead-claudia commented 6 years ago

@JAForbes I'm actually inclined to agree with you in the FP world sentiment outside JS. In my experience, there's only two exceptions I've found so far:

dfdgsdfg commented 6 years ago

If there were a JS type system that was expressive enough, I'm sure we'd gladly switch to it.

What about reasonML? It's ML family, not really just type system but familiar with javascript syntax.

https://reasonml.github.io

dead-claudia commented 6 years ago

@dfdgsdfg I'll note that the ML family does not have type classes - it works more like Static Land than Fantasy Land.

futpib commented 6 years ago

Here is something I'd like to see in JS and would like to get feedback on. This is basically a do-notation like syntax and a generalization of existing async/await syntax (actually, I think this is how it should have been implemented in the first place).

// Number -> Maybe String
const findFullName = chain (id_) => {
  const { firstName, lastName } = from S.find(({ id }) => id === id_, users);
  return `${firstName} ${lastName}`;
};

findFullName(2);
// -> Just('Bob Marley')

findFullName(3);
// -> Nothing

The above function declaration would be equivalent to calling .chain.

More examples: https://github.com/futpib/es-monadic-chain

What do you think?

EDIT: related #282