Suggestion: if-comprehensions

You would need getOrElse to support if p then a else b, and you would need orElse to support if p then a else if q then b else c.

1 Like

I would disallow it. You would write that logic as if (a <- aOpt && b.isEmpty).

You could if you have to specify the pattern like in @LPTK’s language:

Then you are also no longer limited to data structures that are isomorphic to Option.

1 Like

Wouldn’t that be closer to flow typing that it would something like a for-comprehension?

I think the magical behavior of conditions in ifs sounds atrociously difficult to understand.

About the only thing I can imagine making sense here would be to add a for comprehension piece for “else”, although I think even then the specific semantics would need some working out:

for a <- aOpt; b <- bOpt; if c yield foo(a, b) else d

For Option this would be more or less the same as:

(for a <- aOpt; b <- bOpt; if c yield foo(a, b)) orElse d

For Seq it would be more or less the same as:

(for a <- aSeq; b <- bSeq; if c yield foo(a, b)) ++ d

Or in either case it could be something like:

{
   val tmp = (for a <- aThing; b <- bThing; c yield foo(a, b))
   if ! tmp.isEmpty then tmp else d
}

(Although that one feels somehow unsatisfying to me, despite the problem that the plus in Option is orElse and the plus in Seq is ++. ++ on Option forces it to become an Iterable instead of remaining an Option.)

3 Likes

I don’t think this works for Seq, as it unconditionally appends d

Ahh. Good point. I was fixating on the “plus” part and not the “only when zero” part. Whoops. :sweat_smile: And “else” definitely implies that it only happens if the prior result was zero.

Seq doesn’t really have an operation like that. “orElse” on Seq is the orElse of PartialFunction, and not at all like orElse on Option. So if x.isEmpty then d else x is about the best you can do without adding something new.

Which is perhaps another illustration of how this isn’t really a very universal concept, and perhaps when it’s wanted it ought to be done in a way specific to the type involved.

2 Likes

I kinda agree, but to be fair, I find for difficult to understand too :sweat_smile:

I basically never use for any longer. Too much sugar in my diet is bad.

2 Likes

Same here. I found for-yield mystifying when I started with Scala. Then, once I understood what it does I loved it for a while (especially on futures). Now, I tend to write map/flatMap/filter explicitly.

2 Likes

Agreed. That still gives us signatures in Option, Try, and Either in the standard library and would allow users to create interfaces that get desugared as well. Required methods are getOrElse and orElse.

Not any closer to flow typing than regular match expressions.
IMHO for-comprehensions are not the right feature to base this on either way.

1 Like

another language that do flow analysis is java with JEP 394: Pattern Matching for instanceof (looks like nobody mentioned it in this thread yet)

jep snippet:

Rather than using a coarse approximation for the scope of pattern variables, pattern variables instead use the concept of flow scoping . A pattern variable is only in scope where the compiler can deduce that the pattern has definitely matched and the variable will have been assigned a value. This analysis is flow sensitive and works in a similar way to existing flow analyses such as definite assignment.

terminology is rather unfamiliar to me, so i won’t comment on it, but my point is that java is yet another language that already does some analysis of type or value refinements. since java is mainstream, it made flow analysis mainstream too already, so maybe it’s not that scary. note that java doesn’t do flow-typing as described in wikipedia, because it needs additional identifier that receives refinement. on the other hand that makes it more similar to the proposal in this thread.