@@ -127,9 +127,7 @@ defmodule PtcRunner.Lisp.Eval.Apply do
127127 converted_args = Enum . map ( args , fn arg -> closure_to_fun ( arg , eval_ctx , do_eval_fn ) end )
128128
129129 try do
130- push_side_effect_stash ( )
131- result = apply ( fun , converted_args )
132- { :ok , result , pop_side_effects ( eval_ctx ) }
130+ with_side_effect_stash ( eval_ctx , fn -> apply ( fun , converted_args ) end )
133131 rescue
134132 FunctionClauseError ->
135133 # Provide a helpful error message for type mismatches
@@ -261,12 +259,10 @@ defmodule PtcRunner.Lisp.Eval.Apply do
261259 when is_function ( fun , 1 ) do
262260 # Convert any closures/builtins in args to callable functions
263261 converted_args = Enum . map ( args , fn arg -> closure_to_fun ( arg , eval_ctx , do_eval_fn ) end )
264- push_side_effect_stash ( )
265- result = fun . ( converted_args )
266- { :ok , result , pop_side_effects ( eval_ctx ) }
262+
263+ with_side_effect_stash ( eval_ctx , fn -> fun . ( converted_args ) end )
267264 rescue
268265 e in ExecutionError ->
269- pop_side_effects ( eval_ctx )
270266 { :error , execution_error_tuple ( e ) }
271267 end
272268
@@ -286,9 +282,7 @@ defmodule PtcRunner.Lisp.Eval.Apply do
286282 fun = elem ( funs , idx )
287283
288284 try do
289- push_side_effect_stash ( )
290- result = apply ( fun , converted_args )
291- { :ok , result , pop_side_effects ( eval_ctx ) }
285+ with_side_effect_stash ( eval_ctx , fn -> apply ( fun , converted_args ) end )
292286 rescue
293287 FunctionClauseError ->
294288 # Provide a helpful error message for type mismatches
@@ -308,9 +302,7 @@ defmodule PtcRunner.Lisp.Eval.Apply do
308302
309303 defp do_apply_fun ( fun , args , % EvalContext { } = eval_ctx , _do_eval_fn )
310304 when is_function ( fun ) do
311- push_side_effect_stash ( )
312- result = apply ( fun , args )
313- { :ok , result , pop_side_effects ( eval_ctx ) }
305+ with_side_effect_stash ( eval_ctx , fn -> apply ( fun , args ) end )
314306 end
315307
316308 # Map as function: (map key) → Map.get(map, key)
@@ -598,7 +590,7 @@ defmodule PtcRunner.Lisp.Eval.Apply do
598590 body ,
599591 closure_env ,
600592 % EvalContext { } = eval_context ,
601- closure_turn_history ,
593+ _closure_turn_history ,
602594 metadata ,
603595 do_eval_fn
604596 ) do
@@ -628,7 +620,7 @@ defmodule PtcRunner.Lisp.Eval.Apply do
628620 eval_context . user_ns ,
629621 new_env ,
630622 eval_context . tool_exec ,
631- closure_turn_history ,
623+ eval_context . turn_history ,
632624 pmap_timeout: eval_context . pmap_timeout ,
633625 catalog_exec: eval_context . catalog_exec
634626 )
@@ -656,11 +648,35 @@ defmodule PtcRunner.Lisp.Eval.Apply do
656648 # Uses a stack to handle nested HOFs: each HOF pushes a fresh accumulator,
657649 # inner HOFs push/pop their own level, and the outer HOF collects everything.
658650
651+ defp with_side_effect_stash ( % EvalContext { } = eval_ctx , fun ) when is_function ( fun , 0 ) do
652+ push_side_effect_stash ( )
653+
654+ try do
655+ result = fun . ( )
656+ { :ok , result , pop_side_effects ( eval_ctx ) }
657+ rescue
658+ e ->
659+ drop_side_effect_stash ( )
660+ reraise e , __STACKTRACE__
661+ catch
662+ kind , reason ->
663+ drop_side_effect_stash ( )
664+ :erlang . raise ( kind , reason , __STACKTRACE__ )
665+ end
666+ end
667+
659668 defp push_side_effect_stash do
660669 stack = Process . get ( :__ptc_hof_stack , [ ] )
661670 Process . put ( :__ptc_hof_stack , [ % { tool_calls: [ ] , prints: [ ] , catalog_ops: [ ] } | stack ] )
662671 end
663672
673+ defp drop_side_effect_stash do
674+ case Process . get ( :__ptc_hof_stack , [ ] ) do
675+ [ _top | rest ] -> Process . put ( :__ptc_hof_stack , rest )
676+ [ ] -> :ok
677+ end
678+ end
679+
664680 defp stash_side_effects ( % EvalContext { } = ctx ) do
665681 case Process . get ( :__ptc_hof_stack , [ ] ) do
666682 [ top | rest ] ->
@@ -737,7 +753,7 @@ defmodule PtcRunner.Lisp.Eval.Apply do
737753 end
738754
739755 defp do_execute_closure (
740- { :closure , _closure_patterns , body , closure_env , closure_turn_history , meta } = closure ,
756+ { :closure , _closure_patterns , body , closure_env , _closure_turn_history , meta } = closure ,
741757 binding_patterns ,
742758 args ,
743759 % EvalContext { ctx: ctx , user_ns: user_ns , tool_exec: tool_exec } = caller_ctx ,
@@ -754,7 +770,7 @@ defmodule PtcRunner.Lisp.Eval.Apply do
754770 _ -> new_env
755771 end
756772
757- closure_ctx = EvalContext . new ( ctx , user_ns , new_env , tool_exec , closure_turn_history )
773+ closure_ctx = EvalContext . new ( ctx , user_ns , new_env , tool_exec , caller_ctx . turn_history )
758774
759775 # Carry accumulated state from caller so tool_calls/cache aren't lost across closure calls.
760776 # `locals` is rebuilt from the closure's lexical capture + this invocation's
@@ -844,7 +860,7 @@ defmodule PtcRunner.Lisp.Eval.Apply do
844860 Enum . reduce ( user_ns , user_ns , fn
845861 { name , { :closure , ^ params , ^ body , ^ env , ^ th , old_meta } } , acc ->
846862 new_meta = Map . put ( old_meta , :return_type , return_type )
847- Map . put ( acc , name , { :closure , params , body , env , th , new_meta } )
863+ Map . put ( acc , name , { :closure , params , body , env , [ ] , new_meta } )
848864
849865 _ , acc ->
850866 acc
0 commit comments