ERROR: attempt to clone a non-affine global reference
Reproducing the behavior
I'm getting the error ERROR: attempt to clone a non-affine global reference. when I call a function to create a Map or List and then pass it to another function. Example:
empty = 0
white = 1
black = 2
def get_initial_board():
return { 0: black, 1: white, 2: empty }
def get_empty_positions(board):
# We don't even need to use the board variable, just passing it to this function raises the error
return [2]
def get_possible_moves(board, pieces):
fold pieces with pieces_with_possible_moves = []:
case List/Cons:
return pieces.tail(List/Cons((pieces.head, get_empty_positions(board)), pieces_with_possible_moves))
case List/Nil:
return pieces_with_possible_moves
def main():
board = get_initial_board() # If I call any function to create the board I get the error
# board = { 0: black, 1: white, 2: empty } # If I create the board here it works
pieces = [0]
possible_moves = get_possible_moves(board, pieces)
return possible_moves
bend run test.bend -s gives this error:
ERROR: attempt to clone a non-affine global reference.
Errors:
Failed to parse result from HVM.
bend run-c test.bend -s works fine:
Result: [(0, [2])]
- ITRS: 369
- TIME: 0.00s
- MIPS: 0.16
System Settings
Example:
- HVM: 2.0.22
- Bend: 0.2.36
- OS: Apple M2 Max, Sonoma 14.5
Additional context
No response
get_initial_board gets passed as the argument board and then cloned inside get_possible_moves.
This is one of the cases where the heuristic for finding the problematic cases is too strict. This function has some duplications inside it, but not in its normalized form.
The function desugars to Map/set(Map/set(Map/set(Map/empty, 0, black), 1, white), 2, empty), and Map/set duplicates the key (0, 1 and 2 in this example). However, after normalizing, get_initial_board reduces to just an ADT value and not a function so the duplications are not a problem.
Unfortunately this check happens directly at runtime with no way of disabling it for your specific function. Ideally we'd have a type system that can assertain whether or not a value will have problematic duplications, but that's not planned for the immediate future.
For now, you can circumvent this issue by applying a dummy value to the function that forces its evaluation:
def get_initial_board(dummy):
return { 0: black, 1: white, 2: empty }
...
def main():
board = get_initial_board(*)