Stream: ideas

Topic: ✔ 1 line pattern match + `when is`-like bool structure


view this post on Zulip Matthias Toepp (May 10 2023 at 01:41):

Summary:
This proposal intends to make roc simpler, more powerful, more readable, more consistent, with enhanced diff and refactorability.

The idea is to upgrade roc's decision structures:

  1. Replace the current single-line if with single-line pattern matching, which has a more powerful superset of functionality.

  2. Introduce a boolean branching structure with when..is-like layout to take the place of our current multi-line if, then, else.

Examples of the syntax with different suggested options for names are shown in posts below.

view this post on Zulip Matthias Toepp (May 10 2023 at 01:50):

Advantages:

  1. This proposal simplifies roc. The else and then keywords are eliminated.

    The proposed multiline branching syntax is proposed for its own advantages, however it seems to require less syntactic sugar given that if then else is implemented in terms of pattern matching (as I'm told). The proposed syntax more closely resembles the current pattern matching syntax and is less of a special case in terms of indentation.

  2. This proposal makes roc more powerful. Single line pattern matching is more powerful then single line if then else. Single line pattern matching can successfully provide a superset of the functionality of one-line boolean branching. If we can have this then why pass on it? If we are going to have one line pattern matching then having a one line if becomes redundant as pattern matching is able to effectively express the equivalent of a single line if.

  3. This makes roc more readable by eliminating unnecessary boilerplate/syntactic noise. It provides a more elegant multi-line boolean branching structure which follows the layout of the existing when..is. That means it doesn't obscure conditions with three keywords as we currently have with else if x then.

    In other words, this proposal addresses what Python and F# address with elif, Kotlin with when, Elixir and Lisp with cond and Haskell with multiway if (and, at the same time the proposal simplifies and increases the power of the language.)

  4. Roc also becomes more readable as the proposed multi-line branching structure enables the horizontal alignment of conditions, for enhanced vertical scanning.

  5. Roc becomes more consistent by following the same layout for its two branching structures which can make the language feel more unified and elegant.
  6. It may be easier for tools to facilitate editing code given the enhanced consistency between the two branching structures, along with the enhanced simplicity of syntax.
  7. This proposal provides the desired (but unnecessary) equivalent of single line if. Single-line pattern matching is arguably unusual when used as an if statement while at the same time it is clearly readable and easily understandable even to someone who has never read roc. It is just single line pattern matching with syntax consistent with the multi-line pattern matching syntax.

    Since single line boolean branching and pattern matching are unnecessary (one can use mult-line syntax) there is not really a need to teach/learn "single-line branching on bools or general single-line pattern-matching".

    Given that single line pattern matching is highly consistent with multi-line pattern matching, and clearly understandable even when used as a one line if statement, there is no real problem if one comes across it without having had an introduction to the "one line if" syntax.

  8. It provides enhanced diff.

  9. It enables simplified refactoring. The top lines of the proposed multi-line branching structure are symmetrical so they can be more easily re-ordered
  10. This proposal would likely make the compiler simpler.
  11. It does not overload a keyword as was objected to in previous proposals.
  12. It does not add syntactic sugar on syntactic sugar as has been objected to in previous proposals (with replacing else if with elif, el, ef)
  13. It does not create redundant decision structures as was objected to with proposals to have if then else along side a muli-line boolean when-like structure.

view this post on Zulip Matthias Toepp (May 10 2023 at 02:08):

I do realize that roc uses Bool.true and Bool.false but that seems like an undesirable feature in itself. Could we define True = Bool.true?

view this post on Zulip Brendan Hansknecht (May 10 2023 at 02:22):

roc used to have True and False. The issue is that it either had to be a built-in language type or would have some unintuitive issues due to how tag unions work. We chose to change to Bool.true/false instead of making it a special built-in type.

view this post on Zulip Brendan Hansknecht (May 10 2023 at 02:24):

If you define bool directly as the non-opaque tag Bool: [ True, False ], you have the issue that it can have new tags merged into it. So you can end up with a [ True, False, SomethingElse ]. Pretty easily.

view this post on Zulip Brendan Hansknecht (May 10 2023 at 02:25):

To avoid that, we made Bool opaque. Thus, you need to use the re-exported Bool.true

view this post on Zulip Brendan Hansknecht (May 10 2023 at 02:26):

So far, the raw constant version of true and false are used infrequently enough that, though people find it a little weird, it tends to not really be an issue.

view this post on Zulip Brendan Hansknecht (May 10 2023 at 02:27):

Generally you don't compare to true or false. Just use the values in a conditional, so they aren't needed explicitly.

view this post on Zulip Matthias Toepp (May 10 2023 at 02:28):

Wouldn't it be possible to have a keyword True that shadows the tag and re-directs to Bool.true?

view this post on Zulip Matthias Toepp (May 10 2023 at 02:29):

...or something like that.

view this post on Zulip Brendan Hansknecht (May 10 2023 at 02:30):

I guess, that could be done. Though then no other tag could contain True.

view this post on Zulip Matthias Toepp (May 10 2023 at 02:30):

right

view this post on Zulip Brendan Hansknecht (May 10 2023 at 02:30):

Maybe not bad, but a whole separate discussion.

view this post on Zulip Brendan Hansknecht (May 10 2023 at 02:31):

As in first you probably would want a proposal on that feature then you would want to come back to something like this.

view this post on Zulip Agus Zubiaga (May 10 2023 at 02:31):

I think not having true/false keywords also encourages people to use tags which is generally a better way to model things.

There's a talk about this: https://www.youtube.com/watch?v=NNKm97vihKc

view this post on Zulip Matthias Toepp (May 10 2023 at 02:33):

Well I'm fine with that and I think in haskell True and False are tags. But I hear that they would be extendable in roc and that would cause problems.

view this post on Zulip Matthias Toepp (May 10 2023 at 02:37):

Interesting and unfortunate.

view this post on Zulip Matthias Toepp (May 10 2023 at 02:55):

started: https://roc.zulipchat.com/#narrow/stream/304641-ideas/topic/expose.20Bool.2Etrue.20as.20true/near/357143676

view this post on Zulip Richard Feldman (May 10 2023 at 11:20):

size = if x > 5 is (True -> "greater") (False -> "smaller")

this is an interesting syntax idea for single-line pattern matches I haven't seen before - maybe it's worth a separate discussion? (e.g. current when ... is has no single-line form)

view this post on Zulip Richard Feldman (May 10 2023 at 11:22):

incidentally, the idea of "what if we always pattern matched on booleans" has been discussed in Elm Discourse

view this post on Zulip Matthias Toepp (May 10 2023 at 11:29):

I updated this and made a more complete case for it in the initial two posts of this thread.

view this post on Zulip Matthias Toepp (May 10 2023 at 11:49):

Richard Feldman said:

size = if x > 5 is (True -> "greater") (False -> "smaller")

this is an interesting syntax idea for single-line pattern matches I haven't seen before - maybe it's worth a separate discussion? (e.g. current when ... is has no single-line form)

Sure! Great! I'll rename this thread from "pattern matching if" to "1 line pattern match + when..is-like bool structure" and I'll start a new thread called "single-line pattern matching":
https://roc.zulipchat.com/#narrow/stream/304641-ideas/topic/single.20line.20pattern.20matching

view this post on Zulip Matthias Toepp (May 11 2023 at 08:06):

The names of the structures are not very important to me.
The naming options that seem obvious to me are:

view this post on Zulip Matthias Toepp (May 11 2023 at 08:12):

Naming scheme 1.

view this post on Zulip Matthias Toepp (May 11 2023 at 08:15):

size =
    when x is
        Small ->
            "small"
        Medium ->
            "medium"
        _ ->
            "large"


val = when res is (Ok v -> v) (_ -> crash "reason")


size = when x > 5 is (True -> "greater") (False -> "smaller")


size =
    cond
        y < 10 ->
            "small"
        y < 100 ->
            "medium"
        _ ->
            "large"

view this post on Zulip Matthias Toepp (May 11 2023 at 08:17):

Naming scheme 2.

view this post on Zulip Matthias Toepp (May 11 2023 at 08:18):

size =
    when x is
        Small ->
            "small"
        Medium ->
            "medium"
        _ ->
            "large"


val = when res is (Ok v -> v) (_ -> crash "reason")


size = when x > 5 is (True -> "greater") (False -> "smaller")


size =
    if
        y < 10 ->
            "small"
        y < 100 ->
            "medium"
        _ ->
            "large"

view this post on Zulip Matthias Toepp (May 11 2023 at 08:24):

I have a preference for a word like cond for multi-line bool branching compared to if as it looks a bit more substantial/robust (less wispy), but the names are not very important to me.

view this post on Zulip Brendan Hansknecht (May 12 2023 at 00:11):

If we are to go with a proposal like this and accept that we are essentially getting rid of if as it currently exists, I would suggest switching fully to when for both. So when instead of cond. Though that is just a naming suggestion.

As for the rest, single line is a tad verbose, but not that bad. Of course, it depends on being able to use True, gets noisy if you have to use Bool.true. at least you can use _ -> for the false case.

It also would enable things like this, which is kinda nice:

when res is (Ok v -> v) (_ -> crash "reason")

Basically an inline equivalent to .expect or .unwrap in Rust. Though probably would be best to just create the helper function: (EDIT: actually, I change my mind. I think inline is best because it exposes the fact that crash could be called. That is important info)

expect res "reason"

view this post on Zulip Brendan Hansknecht (May 12 2023 at 00:14):

I think overall, it is nice and consistent.

I think the big question is if we are ok with making the language feel stranger to new users. It may hurt adoption due to being jarring in syntax. So i think it is mostly a question of weirdness budget and expectations of people thinking about migrating to the language.

view this post on Zulip Brendan Hansknecht (May 12 2023 at 00:14):

Cause yeah, i think weirdness and minor verbosity are really the main drawbacks, but i think it would be trivial for people who decide to use roc to get used to.

view this post on Zulip Brendan Hansknecht (May 12 2023 at 00:15):

Just may reduce how many people decide to try roc when they look at it and basic if statements don't exist or have a weird syntax.

view this post on Zulip Luke Boswell (May 12 2023 at 00:27):

Would it be valid to write without the additional newline?

size =
    when x is
        Small -> "small"
        Medium -> "medium"
        _ -> "large"

size = when x > 5 is (True -> "greater") (False -> "smaller")

view this post on Zulip Brendan Hansknecht (May 12 2023 at 00:29):

It is currently, so I would assume that feature stays.

view this post on Zulip Georges Boris (May 12 2023 at 01:44):

I don't see myself using this one-line but imo it is a syntax that makes sense so :shrug:‍♂️👌

view this post on Zulip Matthias Toepp (May 12 2023 at 08:14):

Brendan Hansknecht said:

Cause yeah, i think weirdness and minor verbosity are really the main drawbacks, but i think it would be trivial for people who decide to use roc to get used to.

@Brendan Hansknecht & @Georges Boris

As far as I'm concerned I would be delighted if this proposal was accepted but the language was further simplified so that the single line was dropped all together but I believe RF has said that he missed a one line 'if' in elm...

I attempted to at least partially address the unusual nature of the one line if in the second post of the thread:

Since single line boolean branching and pattern matching are unnecessary (one can use mult-line syntax) there is not really a need to teach/learn "single-line branching on bools or general single-line pattern-matching".

Given that single line pattern matching is highly consistent with multi-line pattern matching, and clearly understandable even when used as a one line if statement, there is no real problem if one comes across it without having had an introduction to the "one line if" syntax.

The fact that this would leave us without a more "normal" one line if is perhaps a good reason to name the multi-line if :if rather than cond even though I prefer cond or when because they look less wispy.

@Brendan Hansknecht

switching fully to when for both. So when instead of cond.

On the other hand, choosing when really kind of suggests why we don't have a regular if, it's because we have the more powerful when i.e pattern matching.

However, I didn't list that because I believe @Richard Feldman prefers not to have that. Given that when would be very light sugar over when is it seems more than acceptable to me, but I must be overlooking some of the disadvantages of doing that. RF refers to it as "making the grammar of the language larger" and refers to the impact on newcomers.

view this post on Zulip Matthias Toepp (May 12 2023 at 08:42):

Since @Brendan Hansknecht mentioned it...
(I believe R.F. opposes this as making the grammar of the language larger)
Naming scheme 3.

view this post on Zulip Matthias Toepp (May 12 2023 at 08:43):

size =
    when x is
        Small ->
            "small"
        Medium ->
            "medium"
        _ ->
            "large"


val = when res is (Ok v -> v) (_ -> crash "reason")


size = when x > 5 is (True -> "greater") (False -> "smaller")


size =
    when
        y < 10 ->
            "small"
        y < 100 ->
            "medium"
        _ ->
            "large"

view this post on Zulip Matthias Toepp (May 12 2023 at 08:58):

I'm not sure if that naming generates actual confusion but it might at least generate fear of confusion for newcomers

view this post on Zulip Matthias Toepp (May 12 2023 at 09:04):

That would be my preferred naming if the one line syntax was eliminated all together. The naming is not the most important thing though for me. I'd take if when cond... I even played with calling the pattern match if earlier. It looked/worked surprisingly good...

view this post on Zulip Matthias Toepp (May 12 2023 at 09:16):

Naming scheme 4.

view this post on Zulip Matthias Toepp (May 12 2023 at 09:17):

size =
    if x is
        Small ->
            "small"
        Medium ->
            "medium"
        _ ->
            "large"


val = if res is (Ok v -> v) (_ -> crash "reason")


size = if x > 5 is (True -> "greater") (False -> "smaller")


size =
    when
        y < 10 ->
            "small"
        y < 100 ->
            "medium"
        _ ->
            "large"

view this post on Zulip Matthias Toepp (May 12 2023 at 09:18):

Something about that naming makes me very happy :smile:

view this post on Zulip Matthias Toepp (May 12 2023 at 09:22):

With that naming: "we don't have (one line) if" becomes "we have if and its more powerful !" (it can do boolean branching and pattern matching)...plus that gives us "Kotlin's when".

I think it just works. I guess because it reads sufficiently like english.

view this post on Zulip Matthias Toepp (May 12 2023 at 09:32):

That naming might require pattern match guards to be renamed.

view this post on Zulip Matthias Toepp (May 12 2023 at 10:32):

@Richard Feldman
Thanks for being so patient and generous about us exploring the whole branching thing. I really do appreciate that its more important for you to be happy with the language then me, even though that doesn't necessarily come across with my persistence on this issue. Ultimately it really does come from my appreciation for what you have built with roc and wanting it to be even better. I would just hate for roc to be outdone by Kotlin in terms of having its when!

I do also appreciate that what clearly seems better to me won't necessarily seem sufficiently better for you, or even what is better for us all could be too progressive and cause people to reject roc. I know that my biggest regret would have been that I had not tried to make the case to improve roc in this way, not that I hadn't succeeded. Thanks for letting me have the space to attempt to develop and make that case!

view this post on Zulip Brendan Hansknecht (May 12 2023 at 13:40):

I think this clearly has reached a fundamental proposal. Each of the recent variations are the same except for naming (naming can easily change/be decided later). This proposal is bolder in that it completely removes the current if then else structure in order to make the language more consistent while avoiding duplicate syntaxes for things.

I do think this is cleaner than the earlier proposals. So I think people should look either of the last two examples and ignore naming.

I do get that there can be fatigue around seeing many small variations and similar proposals, but I would advise people to give this a serious consideration.

view this post on Zulip Brendan Hansknecht (May 12 2023 at 13:40):

/poll What is your general thought/opinion on removing if then else in order to make the language more consistent?
Sounds Great
Minor Gain
Indifferent
Minor Loss
Bad Idea

view this post on Zulip Brendan Hansknecht (May 12 2023 at 13:41):

Please add any thoughts or comments below.

I think the main discussion from people on this thread should pause for a short bit to let other's opinions roll in.

view this post on Zulip Matthias Toepp (May 12 2023 at 14:18):

I hope folks check out the summary and list of "advantages" which I've been updating.

The wording of the survey question "to make the language more consistent" doesn't fully capture the 10 advantages I've listed.

view this post on Zulip Matthias Toepp (May 12 2023 at 14:28):

We should be clear that the vote covers the introduction of one line pattern matching. If then else could be removed without introducing one-line pattern matching. Presumably since it says "see suggested change above" it also includes the introduction of one line pattern matching.

view this post on Zulip Brendan Hansknecht (May 12 2023 at 14:47):

Yeah, I was assuming it would include that, but I think that specific could be changed later. I think the bigger question of the general change to if then else is what is most important here.

view this post on Zulip Richard Feldman (May 13 2023 at 17:41):

I think I should maybe try a different approach to convey my feelings about this.

I think if you imagine the perfect syntax for conditionals in Roc, like the Platonic ideal, there is no possible room for improvement, no one could possibly argue otherwise...the benefit to the language to switching to that syntax would be microscopic

view this post on Zulip Richard Feldman (May 13 2023 at 17:41):

my main preoccupation with this topic is that it feels unhealthy that we're spending this much time and energy discussing something in this category

view this post on Zulip Richard Feldman (May 13 2023 at 17:43):

I definitely think syntax is important, but there seems to be a persistent mismatch here between how big of a delta I think is even possibly achievable here and how big others think it is

view this post on Zulip Richard Feldman (May 13 2023 at 17:44):

there are clearly advantages to this proposal (although I'm pretty surprised nobody has brought up the downside of greater indentation, since that's come up in a lot of past syntax discussions)

view this post on Zulip Richard Feldman (May 13 2023 at 17:45):

but also clearly downsides, such as either when meaning two different things or else needing to introduce a new keyword (at which point you have when, which is necessary for pattern matching, and either cond or if)

view this post on Zulip Richard Feldman (May 13 2023 at 17:45):

among others

view this post on Zulip Richard Feldman (May 13 2023 at 17:45):

but at the end of the day, I just don't think this question is the right thing to spend so much energy on

view this post on Zulip Richard Feldman (May 13 2023 at 17:51):

for example, I'm fine with revisiting the design of Bool for reasons such as beginners finding it confusing, or some of the original assumptions behind that experiment turning out to be invalid, etc. - but it definitely seems like the main reason we have another entire thread revisiting Bool's design is because changing it might facilitate this syntax proposal

view this post on Zulip Richard Feldman (May 13 2023 at 17:51):

and I don't think that's a good reason to reconsider Bool's design

view this post on Zulip Matthias Toepp (May 13 2023 at 20:33):

@Richard Feldman @Ayaz Hafiz @Ayaz Hafiz @Anton @Brendan Hansknecht @Bryce Miller @Georges Boris @David Mell @XeroOl @Brian Carroll @Luke Boswell

Thanks for sharing your opinion everyone!
Clearly there is not tremendous support for the proposal.

It was kind of trivial. Sorry to have taken up so much space on this.

view this post on Zulip Notification Bot (May 13 2023 at 20:39):

Matthias Toepp has marked this topic as resolved.


Last updated: Jun 16 2026 at 16:19 UTC