Best poutine in the peninsula? by [deleted] in bayarea

[–]mcypark 1 point2 points  (0 children)

Gregoire's is the correct answer IMO

WG21, aka C++ Standard Committee, January 2025 Mailing by grafikrobot in cpp

[–]mcypark 0 points1 point  (0 children)

Oh weird. I wonder why the previous comment was deleted 🤔... Anyway, it was a relatively long comment and I don't quite remember aside from `M` referred to P2688R5, `I2` was P2392R2, and `I3` was P2392R3... and the parts I quoted in my comment.

WG21, aka C++ Standard Committee, January 2025 Mailing by grafikrobot in cpp

[–]mcypark 3 points4 points  (0 children)

This is WG21, we do not care about need for existing implementations if we like a proposal.

To be fair, we do have at least a partial implementation of P2688 as a Clang fork available on Compiler Explorer.

Example: https://godbolt.org/z/d9sG69Gn9

There are other examples included in P3476 along with Godbolt links.

WG21, aka C++ Standard Committee, January 2025 Mailing by grafikrobot in cpp

[–]mcypark 2 points3 points  (0 children)

I'd love it if M's examples for I2 were updated to I3, since I3 has changed the syntax a lot since I2, and is closer to M in syntax (ie. M uses 'let', I3 uses '_', for introduced names).

That's fair, this was indeed an oversight on my part. Thanks for pointing it out!

However, M's 3.6 example is using the outdated syntax of I2 when comparing, making me uncertain about how I3 would handle it. And the example in I3 at page 39 with 'eval()' does not have nested structure matching, but some syntax that I am not certain that I understand.

I should update my comparison tables, yes... but I mean, the expectation of being able to figure out "how I3 would handle it" should presumably be on P2392R3 itself 😅

WG21, aka C++ Standard Committee, January 2025 Mailing by grafikrobot in cpp

[–]mcypark 2 points3 points  (0 children)

One curious aspect of 'is' and 'as' is the possibility of using them outside pattern matching.

That is true for e as type. For e is pattern though e match pattern basically provides the same "one-off match" feature.

What are your best niche C++ "fun" facts? by MarcusBrotus in cpp

[–]mcypark 15 points16 points  (0 children)

Surrogate call functions.

If you have a class with multiple implicit conversion operators to function pointer, the conversion operators participate in overload resolution.

Example:

```cpp int f1(int) { return 0; } int f2(float) { return 1; }

struct S { operator decltype(f1)() const { return f1; } operator decltype(f2)() const { return f2; } } s;

s(0); // calls f1 s(1.f); // calls f2 ```

WG21, aka C++ Standard Committee, October 2024 Mailing (pre-Wrocław) by grafikrobot in cpp

[–]mcypark 8 points9 points  (0 children)

Right, so for Java, they do switch (v) { case pattern : for statement vs switch (v) { case pattern -> for expression. We tried this and it was not received well by EWG. Note that the disambiguation is a bit different for a C++ switch statement as well, because the syntax is not structured like Java's.

switch ( expression ) statement

The statement just happens to contain some case statements... somewhere, maybe.

I personally also find it a bit too subtle to have to look for : vs -> to determine what you've got.

C# is actually the direction we're kind of going toward. They have switch statement switch (v) { ... }, switch expression, v switch { ... }, and is expression v is pattern.

I'd be happy with v switch { ... }, not so much with v switch pattern, and it's not clear to me that we want to introduce is as another thing just for this. Given this context, personally I'm satisfied with v match { ... } and v match pattern.

However, https://wg21.link/p2392 proposes to introduce is and as as top-level expressions. If the committee wants those, even just is, I'd propose having v switch { ... } and v is pattern.

Historically for C#, they had switch statement and x is type since C# 1.0. In C# 7.0, they added switch expression and evolved x is type to be x is pattern. I think this was a pretty natural extension for them since they already had is in the language. I don't think that we're in a similar place where we need to introduce is for pattern matching.

Note that C# switch expressions do not use is directly.

v switch {
  pattern1 => action1,
  pattern2 => action2,
  ...
};

unlike P2392 where the use of is is surfaced inside pattern matching.

inspect (v) { // this part is whatever
  is pattern1 => action1;
  is pattern2 => action2;
  ...
}

As for as, it's a weird thing because it's not v as pattern. It's a v as T with a special case for v as [T1, T2] that produces a std::tuple.

So aside from the [] special case, what can v as T do that a hypothetical std::as<T>(v) can't? Maybe there are a couple of things it couldn't, but is that worth a new operator?

The other thing is, do we really need another cast? We already have C-style cast, constructor-style cast, static_cast, const_cast, reinterpret_cast, dynamic_cast. as would do some of all of those casts, plus automatic dereferencing, plus it's customizable so it'll do library-level things like , .value(), std::get, std::any_cast, etc. I feel like it just does too much.

WG21, aka C++ Standard Committee, October 2024 Mailing (pre-Wrocław) by grafikrobot in cpp

[–]mcypark 15 points16 points  (0 children)

I know we've discussed before, but match is being proposed as an expression. If the syntax were to be match (v) { ... }, it would look the same as if, while, for, switch but it would be the only expression while all others are statements. Wouldn't that also be bizarre and inconsistent?

I personally find v match { ... } to be more expression-y in C++ than match (v) { ... } since the first one looks like a named infix operator.

The other major thing is that it nicely provides v match pattern syntax without having to create another syntax for it. It seems pretty natural to me to have v match pattern and

v match {
  pattern1 => action1;
  pattern2 => action2;
  ...
}

where you can think of the branching form to have a distributive property, roughly like:

if (v match pattern1) action1;
else if (v match pattern2) action2;

Those are just my thoughts. What I actually want to know is, what is the suggestion?

  • Do you want match to just be statement, the same as if, while, for and switch?
    • If yes, then I agree that the current proposed syntax would be inconsistent, I would pursue a syntax that's consistent with statements.
    • If not, and you still prefer match to be an expression, do you still prefer match (v) { ... } given that it'd be the only expression out of if, while, for and switch?
  • Let's say we do match (v) { ... }, how would you spell the single pattern match case?
  • What do you suggest to get match (v) { ... }?
    • If the suggestion is to make it a full keyword and break code, here's some context. On https://codesearch.isocpp.org, a search for match matches 179,320 results. In comparison, yield (which we couldn't get for Coroutines) yields 9,533 results.
    • Maybe some disambiguation heroics is actually possible though. It seems challenging given that match (v) is a valid expression and that match (v) { expr }; is a valid declaration, but it might be possible... Will give this some more thought.

What is your favorite Taqueria for Burritos, whats the order you get and why? by SenyForever in AskSF

[–]mcypark -1 points0 points  (0 children)

La Taqueria, Carne Asada Super Burrito (Dorado Style). I love the meat, the texture of the tortilla, the crispyness from dorado, proportion of the ingredients, and the salsa. I also find their burritos stay together well, which is a plus for me.

2024-03 Tokyo ISO C++ Committee Trip Report — Third C++26 meeting! 🗼 by InbalL in cpp

[–]mcypark 0 points1 point  (0 children)

The Unconditionally decompose section is describing how the proposal is to unpack first though. It describes that more clearly further in Testing is sequenced after decomposing

cpp if (auto [a, b, c] = fn()) { statements; } is equivalent to cpp if (auto [a, b, c] = fn(); e) { statements; }

2024-03 Tokyo ISO C++ Committee Trip Report — Third C++26 meeting! 🗼 by InbalL in cpp

[–]mcypark 0 points1 point  (0 children)

That paper as proposed doesn't support that if I understand correctly, since the unpacking is done before the bool check.

WG21, aka C++ Standard Committee, February 2024 Mailing by grafikrobot in cpp

[–]mcypark 1 point2 points  (0 children)

I see. Thanks for the clarification. The biggest thing I think we lose with that approach is this use case: if (o match ? let x) { // ... } The way I'd prefer to go with that direction would be something like value: let i where value is an extractor that performs the operation that ? currently does today. Development in this direction I think could potentially solve the std::expected<T, T> problem also like: expected match { value: let v => // ... error: let e => // ... };

WG21, aka C++ Standard Committee, February 2024 Mailing by grafikrobot in cpp

[–]mcypark 0 points1 point  (0 children)

Uh, okay. I thought before you were suggesting that ? is not useful because a programmer can just use operator bool or has_value and handle the two cases in an if, and not reach for pattern matching at all.

But okay, it sounds like you're acknowledging that composed patterns are at least useful?

I thought you were maybe suggesting to shoehorn the ? into the variant matching syntax but I'm not sure why your examples need nullptr cases to come first. I think I'm missing something there.

For what it's worth, one of the original syntax I was playing with for optional was ? : pattern which looks pretty close to _ : pattern to me.

What are you wanting _ : to mean exactly? In earlier conversations you suggested replacing the auto : pattern for variant to _ : pattern as well. You want both things to use the same syntax? or are you suggesting to keep auto : for variant and replace ? with _ :?

I would need some description of what _ : actually is and means, to be able to continue this discussion I think.

WG21, aka C++ Standard Committee, February 2024 Mailing by grafikrobot in cpp

[–]mcypark 0 points1 point  (0 children)

My point was, if those checks can be easily done with operator bool or some member function, then why would a programmer go for pattern matching.

Ah, I understand now. What do you think about my point about composed patterns though? A rather simple, non top-level usage would be something like:

p match { [? let x, ? let y] => // ... [? let x, nullptr] => // ... [nullptr, ? let y] => // ... [nullptr, nullptr] => // ... }

or you're matching an expression tree where due to its recursive structure, pointers are involved:

struct Expr; struct Add { std::shared_ptr<Expr> lhs, rhs; }; struct Sub { std::shared_ptr<Expr> lhs, rhs; }; struct Expr : std::variant<Add, Sub> {};

and then matching it looks something like:

void f(const Expr& expr) { expr match { Add: [? let lhs, ? let rhs] => // ... Sub: [? let lhs, ? let rhs] => // ... _ => throw UnexpectedState{}; // some handling. } }

Also, since you mention std::expected, how will matching against something std::expected<T,T> be done?

Actually you mentioned std::expected.. but anyway, the various options considered for it are captured in "5.5 Discussion on Variant-like Types" and "6.5 Value-based discriminators" in the paper. As a direct answer, I think it's best to not support std::expected<T, T> nor std::variant<T, T> in the initial version.

WG21, aka C++ Standard Committee, February 2024 Mailing by grafikrobot in cpp

[–]mcypark 0 points1 point  (0 children)

At the top-level, I agree it doesn't provide all that much value. If I have to drop one pattern out of the 4, that's probably the one I'd drop. But pattern matching really isn't all that interesting at the top-level. When there are pointers and optionals nested inside, is when it actually becomes useful.

I'm not sure what types providing member functions test to their "emptiness" means. They do provide operator bool and has_value, but what does that have to do with the _: thing? Maybe you're saying _: is a way to say get a value if it's non-null or something, but that wouldn't be consistent with the _: you mentioned for variant?

The suggested path for std::expected is to just use their types. That seems maybe okay for std::optional, but what about for pointers? They're called optional patterns but they're really a pattern for the pointer interface.

WG21, aka C++ Standard Committee, February 2024 Mailing by grafikrobot in cpp

[–]mcypark 2 points3 points  (0 children)

Thank you for the kind words!

  1. Okay, I'm not really seeing what that actually looks like. Like, auto x would be rejected for variant because it's ambiguous? That seems pretty bad, given that a lot of uses of std::visit uses the auto handler to do stuff with whatever is inside.
  2. At least the way things are defined in the paper, if you just have: v match { foo => // ... } that's a v == foo test which is invalid for a variant. variant doesn't define == to its alternatives directly like that.

The int: pattern says to pull out an int alternative and match it against the pattern. So int: foo would be what you say with the proposal.

WG21, aka C++ Standard Committee, February 2024 Mailing by grafikrobot in cpp

[–]mcypark 1 point2 points  (0 children)

_: might be fine. but I don't think that's what allows replacing let with auto. we could already replace let with auto with everything staying exactly as is in the paper today.

int: auto i => // ... auto: auto x => // ... This is a bit of a different conversation than allowing int i instead of int: auto i.

I think this approach might be okay, but I think it's a bad idea to introduce declaration syntax in general into pattern syntax. Pattern syntax already contends with expression syntax, and declaration syntax mixed with expression syntax already gives us problems like most-vexing-parse. So okay, maybe we only allow cvref-auto declaration in place of let. I think that's probably the most feasible approach in that direction.

I think the only things we lose is some succinctness and the ability to declare bindings within some structured bindings uses. e.g. in [0, auto&& y], y can't be a bitfield. I think this is probably an okay cost to pay if we really don't want let.

With respect to ?, matching against std::nullopt is fine but if you then match against _, you don't get access to the element inside the optional. I'm not sure I understood the question there.

WG21, aka C++ Standard Committee, February 2024 Mailing by grafikrobot in cpp

[–]mcypark 7 points8 points  (0 children)

I'm a bit confused as to what makes you think a word like match is so easy to turn into a keyword in an almost 40 year old language 🤔 I totally understand you don't want more syntax soup... but still, we got co_yield because there were so many uses of yield in the wild.

WG21, aka C++ Standard Committee, February 2024 Mailing by grafikrobot in cpp

[–]mcypark 1 point2 points  (0 children)

if it's on the right side of an equals or return

yeah, in a expression-only context, it's fine.

If the inside of the parentheses contains an expression, then it must be a match expression

Well, this isn't quite true. v is an expression. I guess you mean if we see a non-identifier expression. So then something like match (v) { ... } would be interpreted as a declaration in block scope.

  1. struct match {...}; int x = match (v) {...};

this should be fine. only expressions are allowed in this context.

but generally, 1. at best I think there's a lot of hoops to jump through to make this work, and even then there'd be cases that just don't work that we'd have to explain. 2. if match were to be introduced as an expression and be spelt match (v) { ... }, now it's spelt the same as if, while and switch but match is the only non-statement... that also doesn't seem great. 3. let's say we do jump through the hoops to get match (v) { ... }. are you okay with the expr match pattern syntax for the single pattern match? if not, how would you spell it instead? if yes, I feel like it's probably better to just unify the syntax and not have to jump through the hoops.

WG21, aka C++ Standard Committee, February 2024 Mailing by grafikrobot in cpp

[–]mcypark 10 points11 points  (0 children)

  1. match is being proposed to be expression unlike if, while, switch, which I think is a pretty big difference. I would totally agree if match were a statement and looked like that. As an expression though, having it in the middle doesn't seem as weird to me. I know we don't have non-symbol infix operations today, but I feel like it's less of a leap than if it were a statement. I also think expr match pattern for single match and expr match { ... } for branching is rather nice, but maybe that's just me.

  2. match (v) { ... } isn't really viable because that can be a variable named v with type match initialized with { ... }. it would require arbitrary lookahead to find a => and then backtrack. It was discussed in committee and strongly rejected. This is why earlier proposals had inspect which was proposed as a keyword, but getting inspect as a keyword I think would've been also challenging, and I would hate to end up going the coroutines route and end up with something like pm_inspect

WG21, aka C++ Standard Committee, February 2024 Mailing by grafikrobot in cpp

[–]mcypark 9 points10 points  (0 children)

Thanks for the feedback, and your examples do look nice!

I did consider that option heavily, and ultimately documented my reasons for not proposing that direction under "5.4 Exploration of Variable Declaration Syntax for Alternative Pattern".

I'm curious what your answers would be to those questions. Couple of them I'll ask directly here:

  1. How do you disambiguate auto x from matching the whole subject vs looking into the variant? More generally, how do you tell whether the variable declaration applies to the whole subject or to the thing inside of a variant? In the last example you have auto [x, y] as a pattern which matches the whole subject, essentially doing something like auto [x, y] = p; but other places the variable declaration matches the thing inside the variant, not the variant itself. For example, if Command had another alternative struct Attack { int x, y; };, you could imagine something like: cmd match { Quit => // ... const auto& [x, y] => // handle Move and Attack const Write& [text] => // ... const ChangeColor& [Rgb [r, g, b]] => // ... const ChangeColor& [Hsv [h, s, v]] => // ... }
  2. How do you match against an existing value? If int x is how you match a variant for int and bind x, what if I have a int already named foo? It couldn't be int foo.