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
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)"
That said, this isn't the root of the specific error message linked above
Roc if ... then else ...
blocks are expressions, not statements.
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
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)"
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
I feel like I need to figure out better wording to explain this, but hopefully the example helps
Note: for those examples above, still need to change to a when ... is
to get the value out of the tag union.
No your explanation is perfect. I actually knew that, just completely spaced on the fact that if blocks are statements.
When blocks are also statements, correct?
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.
Haha yes... dyslexic... "left = right", "expression = statement" :rolling_eyes:
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)"
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
Great point. Gonna do exactly that. :working_on_it:
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
Wow. Did not realize it would be that level of reduction. Awesome!
Haha yeah, that was a very good call! So many unnecessary branches!
Ian McLerran has marked this topic as resolved.
Last updated: Jul 06 2025 at 12:14 UTC