Stream: beginners

Topic: ✔ Missing final expression / nested definition error


view this post on Zulip Ian McLerran (Jan 15 2024 at 23:50):

I have a roc function to traverse a binary tree in order. I need to get the value of the current node, which uses a method which returns a result, so I use an if statement instead of a when block to handle the result.

Thus I have if resultVal == Ok val then, to extract the named value from the Result tag. I wonder if this syntax is incorrect, and the val is being treated as a definition, thus causing the error I am getting?

Error and code below:

── MISSING FINAL EXPRESSION ───────────────────────────── InorderTraversal.roc ─

I am partway through parsing a definition, but I got stuck here:
25│              valsListWithCurrent = valsListWithLeft |> List.append val
                                                                          ^
This definition is missing a final expression. A nested definition
must be followed by either another definition, or an expression

    x = 4
    y = 2

    x + y
inorderTraversalRecur : Tree a, Nat, List a -> List a
inorderTraversalRecur = \tree, index, valsList ->
    if hasLhs tree index then # tree with left subtree
        valsListWithLeft = inorderTraversalRecur tree (getLhsIdx index) valsList
        resultVal = getValAtIdx tree index
        if resultVal == Ok val then # should always be true
            valsListWithCurrent = valsListWithLeft |> List.append val
        else
            crash "Expected a value at index \(index)"
        if hasRhs tree index then # tree with left and right subtree
            inorderTraversalRecur tree (getRhsIdx index) valsListWithCurrent
        else
            valsListWithCurrent # tree with only left subtree
    else if hasRhs tree index then # tree with only right subtree
        resultVal = getValAtIdx tree index
        if resultVal == Ok val then # should always be true
            valsListWithCurrent = valsList |> List.append val
        else
            crash "Expected a value at index \(index)"
        inorderTraversalRecur tree (getRhsIdx index) valsListWithCurrent # finish right subtree
    else
        when getValAtIdx tree index is # leaf node
            Ok val -> valsList |> List.append val # should always be Ok
            Err _ -> crash "Expected a value at index \(index)"

Full binary tree implementation here
And full code for inorder traversal here

view this post on Zulip Brendan Hansknecht (Jan 16 2024 at 00:07):

I don't think this work: if resultVal == Ok val then.

I think you have to use a when ... is

when resultVal is
    Ok val ->
        valsListWithCurrent = valsListWithLeft |> List.append val
    Err _ ->
        crash "Expected a value at index \(index)"

view this post on Zulip Brendan Hansknecht (Jan 16 2024 at 00:07):

That said, this isn't the root of the specific error message linked above

view this post on Zulip Brendan Hansknecht (Jan 16 2024 at 00:08):

Roc if ... then else ... blocks are expressions, not statements.

view this post on Zulip Brendan Hansknecht (Jan 16 2024 at 00:08):

You can't set a value in an if and then use the value after the if. In fact, there is no "after" the if

view this post on Zulip Brendan Hansknecht (Jan 16 2024 at 00:09):

For example, this chunk of code:

    else if hasRhs tree index then # tree with only right subtree
        if resultVal == Ok val then # should always be true
            valsListWithCurrent = valsList |> List.append val
        else
            crash "Expected a value at index \(index)"
        inorderTraversalRecur tree (getRhsIdx index) valsListWithCurrent # finish right subtree

Should probably be:

    else if hasRhs tree index then # tree with only right subtree
        if resultVal == Ok val then # should always be true
            valsListWithCurrent = valsList |> List.append val
            inorderTraversalRecur tree (getRhsIdx index) valsListWithCurrent # finish right subtree
        else
            crash "Expected a value at index \(index)"

view this post on Zulip Brendan Hansknecht (Jan 16 2024 at 00:10):

Another alternative would be to make the if expression return the value directly:

    else if hasRhs tree index then # tree with only right subtree
        valsListWithCurrent =
            if resultVal == Ok val then # should always be true
                valsList |> List.append val
            else
                crash "Expected a value at index \(index)"
        inorderTraversalRecur tree (getRhsIdx index) valsListWithCurrent # finish right subtree

view this post on Zulip Brendan Hansknecht (Jan 16 2024 at 00:11):

I feel like I need to figure out better wording to explain this, but hopefully the example helps

view this post on Zulip Brendan Hansknecht (Jan 16 2024 at 00:12):

Note: for those examples above, still need to change to a when ... is to get the value out of the tag union.

view this post on Zulip Ian McLerran (Jan 16 2024 at 00:16):

No your explanation is perfect. I actually knew that, just completely spaced on the fact that if blocks are statements.

view this post on Zulip Ian McLerran (Jan 16 2024 at 00:16):

When blocks are also statements, correct?

view this post on Zulip Brendan Hansknecht (Jan 16 2024 at 00:17):

Neither are statements, they are both expresssions.

So neither stands alone like in an imperative language. They are always returning some sort of value from each branch.

view this post on Zulip Ian McLerran (Jan 16 2024 at 00:19):

Haha yes... dyslexic... "left = right", "expression = statement" :rolling_eyes:

view this post on Zulip Ian McLerran (Jan 16 2024 at 00:23):

All issues fixed:

inorderTraversalRecur : Tree a, Nat, List a -> List a
inorderTraversalRecur = \tree, index, valsList ->
    if hasLhs tree index then # tree with left subtree
        valsListWithLeft = inorderTraversalRecur tree (getLhsIdx index) valsList
        when getValAtIdx tree index is # should always be Ok
            Ok val ->
                valsListWithCurrent = valsListWithLeft |> List.append val
                if hasRhs tree index then # tree with left and right subtree
                    inorderTraversalRecur tree (getRhsIdx index) valsListWithCurrent
                else
                    valsListWithCurrent # tree with only left subtree
            Err _ -> crash "Expected a value at index \(Num.toStr index)"
    else if hasRhs tree index then # tree with only right subtree
        when getValAtIdx tree index is # should always be Ok
            Ok val ->
                valsListWithCurrent = valsList |> List.append val
                inorderTraversalRecur tree (getRhsIdx index) valsListWithCurrent # finish right subtree
            Err _ -> crash "Expected a value at index \(Num.toStr index)"
    else
        when getValAtIdx tree index is # leaf node
            Ok val -> valsList |> List.append val # should always be Ok
            Err _ -> crash "Expected a value at index \(Num.toStr index)"

view this post on Zulip Brendan Hansknecht (Jan 16 2024 at 00:31):

You may get simpler/cleaner code if you switch to always recursing down both the left and the right side.

Then as the first part of the recurse, you check if the current node exits. If it doesn't, early return.
I think that would remove some duplicate logic.

Of course could always just pull that logic into a helper if wanted

view this post on Zulip Ian McLerran (Jan 16 2024 at 00:33):

Great point. Gonna do exactly that. :working_on_it:

view this post on Zulip Ian McLerran (Jan 16 2024 at 01:37):

Refactored version is much cleaner:

inorderTraversalRecur : Tree a, Nat, List a -> List a where a implements Inspect.Inspect
inorderTraversalRecur = \tree, index, valsList ->
    when getValAtIdx tree index is
        Ok val ->
            valsListWithLeft = inorderTraversalRecur tree (getLhsIdx index) valsList
            valsListWithCurrent = valsListWithLeft |> List.append val
            inorderTraversalRecur tree (getRhsIdx index) valsListWithCurrent
        Err _ -> valsList

view this post on Zulip Brendan Hansknecht (Jan 16 2024 at 01:59):

Wow. Did not realize it would be that level of reduction. Awesome!

view this post on Zulip Ian McLerran (Jan 16 2024 at 02:59):

Haha yeah, that was a very good call! So many unnecessary branches!

view this post on Zulip Notification Bot (Jan 16 2024 at 02:59):

Ian McLerran has marked this topic as resolved.


Last updated: Jul 06 2025 at 12:14 UTC