Stream: beginners

Topic: if expression


view this post on Zulip Albert (Jun 15 2024 at 06:13):

addAndStringify = \num1, num2 ->
    sum = num1 + num2
    x = (
        if sum == 0 then
            ""
        else if sum < 0 then
            "negative"
        else
            Num.toStr (num1 + num2)
    )
    Str.concat x "!"

going through the Roc tutorial
if else as expressions are amazing. Control flow can be simplified a lot this way.
Does the concept of control flow exist in pure functional language?

view this post on Zulip Albert (Jun 15 2024 at 06:16):

The concept of control flow implies steps and is imperative. However, because Roc is eager, not lazy, it feels like steps as well, just better.

view this post on Zulip Norbert Hajagos (Jun 15 2024 at 06:37):

I think you can even delete the parens around the if expr :smiley:.
If by control flow, you mean branches and loops, yes they exist. You have just demonstrated branches. Loops are harder to find tho. You would need to look past the functional api, into the generated machine code.
Im pretty sure List.walk is implemented as a Tail Recursive function. With tail call optimisation, those specially constrained recursive fn-s compile directly to loops!

view this post on Zulip Albert (Jun 15 2024 at 06:38):

I added the () around if to test if it's an expression

view this post on Zulip Brendan Hansknecht (Jun 15 2024 at 06:57):

if is only ever an expression in roc

view this post on Zulip Norbert Hajagos (Jun 15 2024 at 07:25):

Yes, and you can't have it be without an else branch. That is more wierd to wrap you head around at first. But if you think about it, since you cannot have side effects or mutation of local variables, and there is no "null" or undefined type, it would be incorrect code. The clever use of keywords for if...else makes this clear, but suppose Roc used braces, just for the example:

x = 2
if 3 == 4 {
  x = 3 // x can't be mutated, this if expression would be useless
}

x =
  if 3 == 4 {
    3
  }
  /* without an else branch x is not defined to be any value,
  but it must be a number, since there isn't a null value in Roc
  and the if expression would return a number */

view this post on Zulip Albert (Jun 15 2024 at 14:39):

@Norbert Hajagos Is the concept of nothing just a Tag with no payload? Users can define their own concept of nothing but the language does not make decision on it. Am I understanding it correctly?

view this post on Zulip Norbert Hajagos (Jun 16 2024 at 13:11):

A 'void returning' function is valid in non pure languages, because they can do side effects. So the function actually does something, it's just not observable from the language's perspective (changing global vars, writing to disk, ... ). There is no such fn is roc. Everything returns something, otherwise it would have no point of existing. Even doing effects (like writing to disk) is about returning a Task that will get executed by the platform.
You rarely use the concept of nothing in Roc. For that, there is the empty record {}. You use that in like Task.ok {} for basic-cli as the return value of main. Buuut, you usually do that because you are using the dbg statement in your program, which is a side effect. Or you are measuring the performance of running a fure function. You wouldn't really use it in production.

A payloadless tag still has a tag, so that would be a byte at least. When talking about nothin values, you usually think of the absence of something. At least when we are talking about expression based languages like Roc. For that, you would use an Option / Maybe type. Or just like how we do that in Roc: just use the Result type.
Look at List.get at roc-lang.org/builtins. The return value is either an Ok value or an Err OutOfBounds. The tag inside the Err tag is there to describe the error, but could have been empty as well. You would know that when you get back an Err, it is the absence of the value you were looking for, aka: a better version of Null.

view this post on Zulip Brendan Hansknecht (Jun 16 2024 at 16:19):

I think we optimize a single tag variant to nothing as well

SomeTag: [OnlyOneVariantNoData]
x = OnlyOneVariantNoData

x will be 0 bytes just like an empty record.

view this post on Zulip Brendan Hansknecht (Jun 16 2024 at 16:20):

Not particularly useful, but just a note. I guess a more useful variant is an empty opaque type cause it can at least opt into abilities.

view this post on Zulip Albert (Jun 16 2024 at 16:54):

If it takes 0 bytes, how in the runtime it exists?

view this post on Zulip Albert (Jun 16 2024 at 16:54):

Is it compiled away and doesn't exist in the runtime?

view this post on Zulip Brendan Hansknecht (Jun 16 2024 at 16:58):

Yeah, doesn't exist at runtime


Last updated: Jul 05 2025 at 12:14 UTC