Compare the decisions a maintainer must understand
Equivalent results do not require identical statement sequences. Compare an idiomatic implementation in each language, including the library machinery it needs. The question is whether a reviewer can find the business priority, the changing state and the lifetime boundary. More lines alone do not make code clearer; compact code is useful when its meaning remains obvious.
A review policy with several priorities
The policy uses whole-unit, nonnegative Integer amounts. Approved transactions stay approved; cancelled or unknown states are not processed. A pending transaction first needs verified identity, then manual review at 10,000 or more. All other pending transactions follow standard review.
DirectSharp: read the priority in order
enum ReviewStatus
Pending
Approved
Cancelled
end enum
public action ReviewRoute(status as ReviewStatus, verified as Boolean, amount as Integer)
returns String
description "Choose a review route without modifying the transaction."
require amount >= 0 otherwise new Global.System.ArgumentOutOfRangeException("amount")
if status = ReviewStatus.Approved then
return "Already reviewed"
end if
if status <> ReviewStatus.Pending then
return "Do not process"
end if
// Identity is checked before the amount can qualify for standard review.
if not verified then
return "Identity check"
end if
return select amount
case >= 10000
take "Manual review"
else
take "Standard review"
end select
end action
C#: a modern tuple-pattern switch
public enum ReviewStatus { Pending, Approved, Cancelled }
public static string ReviewRoute(ReviewStatus status, bool verified, int amount)
{
ArgumentOutOfRangeException.ThrowIfNegative(amount);
// Arm order expresses priority; the tuple combines three inputs.
return (status, verified, amount) switch
{
(ReviewStatus.Approved, _, _) => "Already reviewed",
(ReviewStatus.Pending, false, _) => "Identity check",
(ReviewStatus.Pending, true, >= 10000) => "Manual review",
(ReviewStatus.Pending, true, _) => "Standard review",
_ => "Do not process"
};
}
This is concise, idiomatic C#. A reader comfortable with tuple patterns can scan it quickly. DirectSharp chooses separate named steps and explicit endings; its benefit is the visibility of priority to readers who do not regularly work with pattern syntax. There is no reason to replace this C# switch with a deliberately verbose version just to make a comparison look favorable.
C++: a status switch with nested checks
#include <cstdint>
#include <stdexcept>
#include <string_view>
enum class ReviewStatus { Pending, Approved, Cancelled };
std::string_view review_route(ReviewStatus status, bool verified, std::int32_t amount)
{
if (amount < 0) throw std::out_of_range("amount");
switch (status) {
case ReviewStatus::Approved:
return "Already reviewed";
case ReviewStatus::Pending:
// These returns terminate the case; there is no fall-through.
if (!verified) return "Identity check";
if (amount >= 10000) return "Manual review";
return "Standard review";
default:
return "Do not process";
}
}
C++ uses its ordinary switch and early returns. The reviewer must connect each return to its case and the closing brace to its scope. DirectSharp's end if, end select and end action make those boundaries explicit. Neither version requires a rule engine or a framework.
| Input | Expected route in all three versions |
|---|---|
| Approved, unverified, 10,000 | Already reviewed |
| Pending, unverified, 10,000 | Identity check |
| Pending, verified, 9,999 | Standard review |
| Pending, verified, 10,000 | Manual review |
| Cancelled, verified, 100 | Do not process |
| Any state with a negative amount | Argument/range failure before routing |
Fresh mutable state: DirectSharp and Python
An import starts with independent empty row/error lists and a zero total. No list should leak rows into the next import.
import System.Collections.Generic
public class ImportBatch
public properties
Rows as List <of String> = default
Errors as List <of String> = default
Total as Integer = default
end properties
end class
let first = new ImportBatch()
let second = new ImportBatch()
first.Rows.Add("A")
// The locked List default creates a fresh list: second.Rows.Count is zero.
from dataclasses import dataclass, field
@dataclass
class ImportBatch:
# Factories create fresh mutable lists for each constructed instance.
rows: list[str] = field(default_factory=list)
errors: list[str] = field(default_factory=list)
total: int = 0
first = ImportBatch()
second = ImportBatch()
first.rows.append("A")
assert second.rows == []
Python's default_factory is an ordinary, useful idiom. A mutable class-level list or a mutable function default would have a different sharing contract; neither is an equivalent replacement. DirectSharp's locked built-in List default expresses fresh initialization with one language rule. It does not promise that arbitrary custom reference types construct themselves: their default can still be nothing. Python annotations and dataclasses do not by themselves enforce runtime input types or freeze the lists, and DirectSharp's writable properties do not freeze them either.
Coordination is more than starting two tasks
The command handler must read customer and risk together, race interchangeable customer replicas, bound active transaction workers, protect shared report/totals updates, and retry only transient GET failures. Its comments identify when capacity is held and when it is released. Compare that outcome with the complete async/resource examples, not just the spelling of await.
| Language | Idiomatic machinery to inspect | What DirectSharp makes explicit |
|---|---|---|
| C# | Tasks, a shared SemaphoreSlim, try/finally release, CancellationTokenSource and task settlement; Task.WhenAny selects completion, not first success | Gate scopes, owned joins and first-success selection are named language contracts |
| VB.NET | The same CLR task/token/semaphore primitives, with Try/Finally and explicit blocks | Familiar block endings plus concurrency ownership and the selected cleanup route |
| Python | asyncio.TaskGroup for owned tasks, Semaphore with async with, a deliberate first-success selection and cancellation policy | first selects success; end concurrent joins owned children and publishes its result |
| C++ | RAII owners, locks/permits and an executor/task library; the library defines cancellation and how tasks are joined | Managed resource scopes and a defined Gate/structured-concurrency contract; native-memory control is a different requirement |
| Go | Goroutines, channels and a shared context; the sender/receiver design must drain or cancel losing work without blocking senders | Ownership and losing-producer settlement are part of the scope contract |
| Java | The selected executor/future or structured-task API, interruption support and cleanup; winning future selection alone does not settle all work | Selection, cancellation requests and settlement are specified together |
| Rust | Ownership and RAII plus the chosen async runtime's spawn/select/join and cancellation behavior | The business source names the scope policy; it does not provide Rust's ownership model or native execution guarantees |
| JavaScript | Promise.any for first fulfillment, explicit AbortController wiring, and settlement of losers when the operation owns them | first requests cooperative loser cancellation and joins; a resolved Promise.any alone supplies neither guarantee |
| TypeScript | JavaScript's runtime primitives plus static interface/result types; typing a promise does not change cancellation or ownership | Strongly typed results with the same explicit scope obligations |
These are API/language responsibilities, not universal equivalent snippets. An alternative implementation must preserve the same failure, cancellation, result-order and ownership contracts before it can be called equivalent. A lock protects shared state; a capacity gate limits admission. Neither automatically replaces the other. Avoid manufacturing lock-heavy code where an ordinary library already provides the needed policy.
Read the complete tasks with these questions
For every language, ask: which input is rejected, which branch wins, which state changes, what happens after a partial failure, and who waits for cleanup? The task index links the complete versions. Use the workload comparison when native integration, a browser runtime or Python's data ecosystem matters more than the syntax of one rule.