-
Notifications
You must be signed in to change notification settings - Fork 39
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
wip object pattern matching #296
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great progress!
As discussed in Discord, I think we should think about whether it's "right" that arrays are exact matching (require exact length instead of just "at least pattern's length") while objects are inexact matching (require at least these keys, but could have more). There are two parallels to draw:
- TypeScript types: object types are inexact lower bounds (at least these keys are available), while arrays (as tuple patterns) are exact matches. This is what you're following now.
- JS destructuring: both object types and arrays are inexact lower bounds. You can write
[a, b] = [1, 2, 3]
.
The nice thing about exact patterns is that you can always build inexact patterns out of them using a rest parameter. Given that [a, b]
forces array of length 2, you can use [a, b, ...]
to ask for array of length at least 2. But there's currently no way to specify an exact object type. Possibly related to Flow's {| ... |}
exact object type.
Exact object matching is a bit tricky to check — maybe add a test that Object.keys(obj) === n
where n
is the number of keys matched. It would definitely be useful for checking API call validity (e.g. Meteor check). I guess the question is whether it should be the default, and if you want inexact you add a ...
rest; or if it should be "opt-in" via something like {| ... |}
.
...prop, | ||
children: [ws, ...prop.children], | ||
} | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not important, but you could write this as follows (assuming we know length > 0 here?):
const [first, ...rest] = props
return [
{...first, children: [ws, ...first.children]},
...rest
]
[/^foo$/, b] | ||
console.log b | ||
--- | ||
if(Array.isArray(x) && x.length === 2 && typeof x[0] === 'string' && /^foo$/.test(x[0])) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TIL that /5/.test(5)
returns true
. Probably checking that it's a string makes sense.
Idea for future: Once we have a way to assign parts to local variables, e.g. the pattern 5 as x
, maybe we could do that for regex groups. E.g. /([+-])(\d+)/ as [match, sign, abs]
I'm leaning towards TS semantics for the defaults. An opt-in exact match does sound useful in some situations and should be simple enough to add on later. |
Sounds good. I'm really looking forward to when I can write this: interface point
x: number
y: string
switch v
point
...
---
interface point {
x: number
y: string
}
if (typeof v === "object" && v != null && typeof(v.x) === "number" && typeof(v.y) === "string") {
...
} Before we understand type references though (looking up switch v
{x:: number, y:: string} |
I was thinking of something like: switch v
{x: <? "number", y: <? "string" } making use of the expanded It would also allow thing like: switch x
<? Error
...
<? Result
... But that's slightly trickier to parse right now. |
Pattern matching is getting closer.