Something about combinators

It seems Rust combinators are not short circuited.

Is it possible to replace them with operators, and make them circuited?

i.e. a().or(b()) will always calculate b(), but a() | b(), if a is some, then b() is not calculated.

a().or_else(|_| b())

7 Likes

I know. but nor as elegant as a() | b(), don't you think?

Some javascript syntax sugar is really nice. :slight_smile:

What do you mean by "some" here? Also, this is already bitwise-or and making operators mean completely different things based on their argument types sounds distinctly C++ in nature, not Rust-like.

2 Likes

The operator you want is ||, not |. The former is a logical or, which doesn't evaluate the right hand side if the left hand side evaluates to true, while the latter is a bitwise or, which always evaluates both sides. Thus it is expected for | to not shortcircuit.

Currently you can only overload the | operator because nobody has proposed a good design for a shortcircuiting overloadable || yet.

8 Likes

If you want a concise version: a().or(b) where b is a function item type and or takes functions for the right hand side.

In Rust's typical API "dialect" or takes values; or_else takes deferred values (usually a closure).

Sure, but if someone wants to write something optimized for conciseness AND shortcircuiting than they can do it that way.

+ being used for addition as well as concatenation is a pet peeve of mine! :expressionless:

2 Likes

I think we have at least one that could reasonably be used. There are some from when this was previously discussed, for example.

In some ways, ? is sugar for &&, but we have zero sugar for ||-like things, so I think it would make sense for Rust to eventually have something along those lines.

But I agree we don't yet know what that should be.

1 Like

Well, there are ¿, , , or available for those :stuck_out_tongue: .

2 Likes

I suspect you say that at least partially in jest, but it raises something I think is important:

The or-sugar's structure should probably be very different from that of the and-sugar.

Option::zip works great with ?: it's just try { (a?, b?) }.

But ignoring whether the specific token is good for now, what would it mean to write an "or form" of that? How do I think about (a¿, b¿)? Is there any semantics we could give that that make any sense at all?

Just doing the opposite of ?, and short-circuiting with the some value but having the expression give None is weird, since the None is straight-up useless, and might as well be () instead. But then making that tuple is also pointless, since you're getting a ((), ()) that you need to handle.

The or-equivalent of zip is something like (Option<A>, Option<B>)Option<EitherOrBoth<A, B>>, I guess? (Using https://docs.rs/itertools/latest/itertools/enum.EitherOrBoth.html.)

Or, from a more philosophical view, ? works great because you can read through for the "happy" path first, then investigate all those error returns in a second pass. Whereas if you were to try to read something ignoring the ¿s, it'd make no sense.

But while that has me convinced that postfix-operator isn't the best form for it, I'm still not sure what is. Maybe it's just a macro, like

coalesce![
    first() + attempt(),
    some_other().approach,
    get_the.fallback(),
]

Or the other things that's conceptually a "try stuff in order until one works" construct is match, so maybe that can offer some kind of inspiration?

Indeed.

This only works if it is lazy (probably wrapped up in lambdas though that certainly introduces "fun" hidden moves or sharing inside the macro). I certainly wouldn't want get_the.fallback() to be called unless the other two have already failed.

Since it's a macro no lambdas would be needed, it can expand to something like

if let Some(v) = first() + attempt() {
  v
} else if let Some(v) = some_other().approach {
  v
} else {
  get_the.fallback()
}
4 Likes