I think the ability to pattern match function signatures (like in Elixir/Erlang) would be extremely useful.
Example 1
function request({ url, header, body }) {
// Handle request
}
function request(_) {
throw "url, header, and body are all required."
}
Example 2
const app = require("express")()
app.get("/article", (req, res) => {
const a = getArticle(req.params)
if (a === null) res.status(400).send()
else res.status(200).send(a)
})
function getArticle("LATEST") {
return getLatestArticle()
}
function getArticle(id) {
// get article
}
function getArticle(_) {
return null
}
Can you show an example?
Can you show an example?
See edit.
That looks a lot like function overloading. Won't this do the same but while being a lot more concise?
const request = params => case (params) {
when { url, header, body } -> // Handle request
when _ -> throw "url, header, and body are all required."
}
With the above example matching multiple arguments would get a bit noisy though.
You can always add more space? Pattern matching function header would be what actually adds noise as you would have to write the entire function syntax every time you needed more arguments
Consider:
const request = params => case (params) {
when { url, header } -> // Handle request
when { url, header, body } -> // Handle request
when { url, header, body, cors } -> // Handle request
when _ -> throw "url, header, and body are all required."
}
Versus:
function request({ url, header }) {
// Handle request
}
function request({ url, header, body }) {
// Handle request
}
function request({ url, header, body, cors }) {
// Handle request
}
function request(_) {
throw "url, header, and body are all required."
}
This is also ignoring the fact that you would break a ridiculous amount of existing code. Function overloading is not even remotely close to being backwards compatible.
I was talking about multiple parameters, your example is still only one parameter as an object.
Yes, the example by OP is definitely not backwards compatible, but there could be some other way to do this, using a keyword or similar, I don't think it is necessary to bikeshed syntax before there isn't any agreement/motivation anyways.
Ah, yes I see. But hasn't this been already discussed in https://github.com/tc39/proposal-pattern-matching/blob/latest/CORE.md#--only-one-parameter-to-match-against ?
So something along these lines:
const request = (...params) => case (params) {
when [{ url, header }, res] -> // Handle request
when [_] -> throw "url, header, and body are all required."
}
I think my initial example was kinda weak, so here's a better one that I've actually used in Elixir.
const app = require("express")()
app.get("/article", (req, res) => {
const a = getArticle(req.params)
if (a === null) res.status(400).send()
else res.status(200).send(a)
})
function getArticle("LATEST") {
return getLatestArticle()
}
function getArticle(id) {
// get article
}
function getArticle(_) {
return null
}
const app = require("express")()
app.get("/article", (req, res) => {
const a = getArticle(req.params)
if (a === null) res.status(400).send()
else res.status(200).send(a)
})
const getArticle = params => case (params) {
when "LATEST" -> getLatestArticle()
when {id} -> // get article
when _ -> null
}
So if I have understood your issue and the proposal correctly then my example 馃憜 is equivalent to yours when it comes to functionality? In which case I find my example with the current proposal to be much more readable. Repeating function getArticle, return and having three lines of code (instead of one) for every new overload seems needlessly verbose.
@PetterSa, In @jackHedaya 's example, each function with its arity is individually testable.
If I decide to write a new function with the same function name, I'd just have to add another test to compliment that new function definition. if i were using something like Flow/typescript, i'd just have to worry about typing that new function
Lets take this example where a function can have a arity of 1 or 2:
function tail_recursive_factorial(n) {
return tail_recursive_factorial(n, 1);
}
function tail_recursive_factorial(0, acc) {
return acc;
}
function tail_recursive_factorial(n, acc) {
return tail_recursive_factorial(n-1, acc*n);
}
(I think this is how it would look) in the example below:
const tail_recursive_factorial = params => case (params) {
when {n} -> tail_recursive_factorial(n, 1)
when {0, acc} -> acc
when {n, acc} -> tail_recursive_factorial(n-1, acc*n)
}
Writing the tests & types wouldn't be as straight forward. (although the code is nice and concise.)
I see where this is coming from, but am not really sold on including it as a feature in JS.. either way, I believe it would have to be a followup proposal.
I'm not entirely clear on the semantics. As I understand it, this would be a kind of syntactic sugar for defining a single function object through several definitions (barring significant changes to how identifiers are resolved etc).
I wonder how having multiple definition locations for one binding interacts with different parts of the spec. For instance, how would it interact with scoping and hoisting? It's also not entirely clear to me in this case how non-exhaustive patters should be treated.
All in all, I think it's a rather steep change in language semantics, and rather far from the current proposal. Personally, I think I'd prefer a more minimal convenience sugar a la #133, but either way I think we should try to work on the core pattern-matching construct first before expanding the scope in such significant ways.
@joegiralt I really don't see how this would make it easier to test. In both example you would need to write tail_recursive_factorial(/*whatever params you need to test*/) in a test.
The proposal has been updated in #174, and I'm not sure that this idea still makes sense, especially given those updates.
If you disagree, taking into account the updated proposal, please file a new issue.
Most helpful comment
I see where this is coming from, but am not really sold on including it as a feature in JS.. either way, I believe it would have to be a followup proposal.
I'm not entirely clear on the semantics. As I understand it, this would be a kind of syntactic sugar for defining a single function object through several definitions (barring significant changes to how identifiers are resolved etc).
I wonder how having multiple definition locations for one binding interacts with different parts of the spec. For instance, how would it interact with scoping and hoisting? It's also not entirely clear to me in this case how non-exhaustive patters should be treated.
All in all, I think it's a rather steep change in language semantics, and rather far from the current proposal. Personally, I think I'd prefer a more minimal convenience sugar a la #133, but either way I think we should try to work on the core pattern-matching construct first before expanding the scope in such significant ways.