Closed itay closed 13 years ago
This is a nicer way to write your tree traversal example, and it's valid coffeescript:
class Tree then constructor: (@left, @key, @value, @right) -> class Leaf then constructor: (@key, @value) -> find = (tree, needle) -> switch tree.constructor when Tree {left, key, value, right} = tree if needle is key value else if needle < key find left, needle else find right, needle when Leaf {key, value} = tree if key is needle return value
I love pattern matching in Haskell/Scala, and in those languages it's a killer feature. But I just don't think it works with CS/JS semantics. After seeing my alternative, do you still favour pattern matching?
I think your argument/alternative boils down to the fact that because CS already has assignment deconstruction (e.g. {left, key, value, right} = tree)
, the added benefit of having the deconstruction happen as part of the when statement is marginal. I think this is a reasonable point of view.
Personally, I prefer my syntax, for a couple of reasons:
That said, I realize it's mostly a stylistic issue. It's just something I always wish existed. One use case I have in mind is to reduce the need to have both success and error callbacks to HTTP requests. I'd just wrap it in Success(...)
and Error(...)
objects. The end result is the same, I just prefer this way of expressing it.
The main problem with your proposal is that you just can't really pattern match. Are you looking for the first "pattern" in which your object hasOwnProperty
s all of the keys? Here's another one of your examples done with current coffeescript:
reduce = (arr, f, accumulator) -> switch arr?.length when 0 accumulator when 1 f array[0], accumulator else reduce arr[1..], f, f arr[0], accumulator
or if you would prefer I pepper in some deconstruction...
reduce = (arr, f, accumulator) -> [first, rest...] = arr switch arr?.length when 0 accumulator when 1 f first, accumulator else reduce rest, f, f first, accumulator
I'd like to see a use case with your suggested style of pattern matching where the code is significantly easier to understand or the solution is significantly easier to express. Your most recently mentioned callback example is neither, since it would be better written the way I rewrote your tree traversal example. It doesn't even have to be that significant.
To me, it seems that my definition of reduce is easier to understand and more natural than your solution. Here's another one with arrays where you want to iterate over a pair of values (I saw this on the Google Group, I think):
Here is the suggested code from the group:
sum_pairs = (array) ->
sum = 0
for i in [0...array.length - 1]
sum += array[i].distance array[i+1]
return sum
And here's how I'd write it with pattern matching:
sum_pairs = (array) ->
sum = 0
match array
when [a, b, rest...]
sum += (a + b + sum_pairs(array))
else
sum = 0
return sum
Imagine we have a function for doing a POST on a URL along the following lines:
http_post = (url, params, success, error) ->
# Do the POST
response = ...
if response.error?
success(response)
else
error(response)
A caller might look like this:
get_followers = (user_id) ->
http_post("https://twitter.com/api/following", {user_id: user_id},
success = (response) =>
# do something with the response
error = (response) =>
# do something with the error
)
With pattern matching, I'd write it along the following lines:
http_post = (url, params, callbackr) ->
# Do the POST
response = ...
result = {
success: response.error?
status: response.status
response: response
error: response.error
}
callback(result)
get_followers = (user_id) ->
http_post("https://twitter.com/api/following", {user_id: user_id},
(result) =>
match result
when { success: true, status: 200, response: response }
# do something with the response
when { success: true, status: 201, response: response }
# do something else with the response
when { success: false, error: error}
# do something with the error
)
Now, I don't know which one is better. For the first example, the loop version is probably better, unless you like recursive functions. For the second, I'm not sure. I could go either way.
Like I said, I think your argument is reasonable that for the most part it's not necessary because assignment deconstruction brings us most of the way there. I still think that for the reduce
example, my pattern-matching version is cleaner.
To your question about whether the notion of pattern matching on an object is just checking if hasOwnProperty
is true for all the fields (and then subsequently doing any equality checks should I have given a constant, like in the status: 200
case above), then yes, that was my plan. It's not fool proof (and like I said, it is order dependent, but that's true for OCaml/Scala as well), but I think it's good enough.
Well, when { success: true, status: 201, response: response }
can probably be written as when { success: true, status: 201, response }
, if {a, b}
is the implicit for {a:a, b:b}
Plus, the guard expression should be supported too.
@accelware, you're right on both counts. I usually choose the more verbose and clearer version :)
I'd love to see this get in. It would be super awesome.
This has been discussed several times before -- pattern matching inspired by static languages is a particularly poor fit for JavaScript (and by extension CoffeeScript), because types are extremely weak in JS.
In addition, because if/else and switch statements are expressions in CoffeeScript, any use of match
can just as well be written in terms of them, but more clearly and powerfully, with arbitrary predicates.
Description
I noticed that this has been kind of discussed in #108, but I thought I'd bring it up again with some more examples that show why CoffeeScript is poised to make use of something like. I won't repeat the basic description from #108, and the examples should make this clear.
Examples
Here are a few examples, using a made-up syntax.
An implementation for reduce:
This is the bastardization of the Option paradigm from OCaml/Scala (Maybe in Haskell):
And some binary tree traversal:
And a random one:
Pros and Cons
Here are my thoughts on the arguments for and against
Pros:
Cons:
Anyway, I'd be happy to hear thoughts about it, or if people think it isn't worth it.