No Operator Overloading — Now What: Custom Comparators and the transacts Effect
Lesson 13 said ordinary Blueprint instances can't be compared directly with =, and teaching a type you built yourself greater-than and less-than is even further off the table — Verse currently doesn't open up "operator overloading" (that is, defining for your own types what symbols like + and < should do). This page starts from a real compile-red on the forums, watches how the community routes around the wall, and meets a new friend along the way: the transacts effect specifier.
1. Live from the Forums: One Error, Two Questions
On the Epic developer forums, a developer wanted to write a comparison function for their CustomPlayer type, to sort a player leaderboard. The function's logic was simple — yet the moment they hit Compile it slammed into a wall, with a red message every reader of this lesson will meet sooner or later:
This invocation calls a function that has effects that are not allowed by its context: transacts no_rollback
He followed up with another question: can Verse "overload operators" the way some languages do, defining < directly for CustomPlayer (making "less than" mean something for two players)? That's exactly what makes this thread valuable — it steps on two mines at once: one about the language's boundaries (is there operator overloading?), one about the effect system (why the transacts no_rollback error?).
2. Answer One: UEFN Verse Has No Operator Overloading, for Now
The language-boundary question first: no, it can't. You will indeed spot entries like operator'+=' in the official API docs, but those are just the built-in operators' "household registrations" in the official library — it doesn't mean you can register a new household for a type you built yourself. UEFN Verse at this stage does not support defining operators for custom types.
So if you want a custom type to be "comparable", there are exactly two roads: compare the inner fields that can already be compared (whole numbers, strings, and so on), or — as covered in Lesson 13 — mark the class <unique> and use = for identity comparison, "is this the same instance?". For ordering, you write your own plain function to serve as the comparator (a little function in charge of "who's bigger").
3. Answer Two: the Accepted Refactor — the Comparator Takes Only Scores, and Wears transacts
Now the effect-system question. The accepted answer in the thread didn't brute-force operator overloading; it changed two things: first, have the comparison function skip the whole CustomPlayer and take only the already-extracted integer score — the purer the comparison logic, the better; second, explicitly hang the <transacts> effect specifier on the function:
# the accepted approach: the comparator takes only scores, annotated <transacts>
CompareScores<public>(A:int, B:int)<transacts>:int =
if (A < B) then -1 else if (A > B) then 1 else 0
Graph translation: CompareScores is a custom function taking two integers A and B and returning an integer. Its body is a chain of Branch checks: if A is less than B, return -1; if A is greater than B, return 1; otherwise (equal) return 0 — exactly the Blueprint chain of nodes that compares two Integers and outputs -1 / 1 / 0 by the result. The <transacts> after the name is a sticker on the function that reads "I can be rolled back".
Why is <transacts> the key? Think back to Lesson 12: inside Branch-style checks that "can fail to go through", the code is rollback-capable — when things don't go through, all the changes get undone as if they never happened. When you plug a function in as a "part" for that kind of rollback-capable spot (say, sorting has to call your comparator over and over in "can fail" positions), the part itself must also promise "I can be rolled back" — and that promise is <transacts>. Leave it off, and the compiler treats it as carrying a "no rollback" effect by default, then the moment you hit Compile it hurls exactly that transacts no_rollback red above.
While you're here, look at the function body: if (A < B) then -1 else if (A > B) then 1 else 0 strings several if…then…else together on one line, and the whole chain directly becomes the function's output — a neat review of two earlier points, Lesson 12's "if can be used as a value" and Lesson 13's "no ternary shorthand, use if then else (like Blueprint's Select node)". As for which sorting feature the comparator actually gets passed to, it differs across versions — defer to the Verse API Reference.
4. Pop Quiz ★
You pass a comparison function to a spot that requires "can be rolled back" (sorting, say), and it errors with transacts no_rollback. The most likely cause?
This page is adapted from a Q&A thread on the Epic developer forums: How to operator overloading in Verse — Unreal Engine Forums ↗