Function Parameters¶
This document provides an overview of function parameter types in Sharpy. For detailed information on specific topics, see the linked pages below.
Overview¶
Sharpy supports several parameter types:
- Required parameters - Must be provided by the caller
- Default parameters - Optional with compile-time constant defaults (see Function Default Parameters)
- Named arguments - Pass arguments by name for clarity
- Variadic arguments - Accept variable number of arguments with *args (see Function Variadic Arguments)
- Flexible arguments - Positional-only, keyword-only, and kwargs support (see Flexible Arguments)
Default Parameters¶
Functions can specify default values for parameters. Parameters with defaults must come after required parameters.
Key Points:
- Default values must be compile-time constants
- Eliminates Python's "mutable default argument" pitfall
- Supports numeric, string, boolean literals, None, enums, and constants
For complete details on default parameters, including the compile-time constant requirement and patterns for optional mutable arguments, see Function Default Parameters.
Named (Keyword) Arguments¶
Sharpy supports calling functions with named arguments, allowing callers to specify parameter values by name rather than position:
def create_user(name: str, age: int, active: bool = True) -> User:
pass
# Positional arguments
user1 = create_user("Alice", 30, False)
# Named arguments
user2 = create_user(name="Bob", age=25)
user3 = create_user(age=25, name="Bob") # Order doesn't matter for named args
# Mixed: positional first, then named
user4 = create_user("Charlie", age=35, active=False)
# ❌ Invalid: named before positional
user5 = create_user(name="Dave", 40) # ERROR: positional argument follows keyword argument
Named Argument Rules¶
- Named arguments must follow all positional arguments
- Once a named argument is used, all subsequent arguments must be named
- A parameter cannot be specified both positionally and by name
Implementation - ✅ Native - Direct mapping to C# named arguments.
Variadic Arguments (*args)¶
Sharpy supports variadic arguments using the *args syntax for accepting a variable number of arguments. Unlike Python's fully dynamic *args, Sharpy's variadic arguments are homogeneously typed: all arguments must be of the same type T.
def sum_all(*numbers: int) -> int:
result = 0
for n in numbers:
result += n
return result
total = sum_all(1, 2, 3) # 6
Key Points:
- All variadic arguments must be the same type
- *args must be the last parameter
- Maps directly to C# params arrays
- Supports unpacking with * operator
For complete details on variadic arguments, including unpacking rules, C# interop, and examples, see Function Variadic Arguments.
Flexible Arguments¶
Sharpy provides positional-only (/) and keyword-only (*) parameter markers for zero-cost compile-time validation of how arguments are passed.
For complete details, see Flexible Arguments.
Quick Example¶
def search(query: str, /, limit: int = 10, *, case_sensitive: bool = False) -> list[str]:
pass
search("hello", 20, case_sensitive=True) # ✅ Valid
search(query="hello") # ❌ ERROR: 'query' is positional-only
search("hello", 20, True) # ❌ ERROR: 'case_sensitive' is keyword-only
Function and Method Overloading¶
Sharpy supports overloading following C# semantics. Multiple functions or methods can share the same name if they differ in parameter count or types. This applies to both module-level functions and class methods. For detailed examples including module-level and cross-file overloading, see Function and Method Overloading.
def process(value: int) -> str:
return f"Integer: {value}"
def process(value: str) -> str:
return f"String: {value}"
def process(value: int, multiplier: int) -> str:
return f"Result: {value * multiplier}"
Rules: - Overloads resolved by parameter count and types - Named arguments filter candidates to overloads with matching parameter names (see Named Arguments in Overload Resolution)
Implementation - ✅ Native - C# supports method overloading.
Overload Resolution Rules¶
Authoritative reference: Overload Resolution is the full specification of applicability, the betterness tie-break chain, the conversion-cost ranking, and how each resolution engine (calls, operator dunders, constructors, builtins) behaves. This section is a summary; where it and that page differ, that page governs.
When multiple overloads could match a call, the compiler picks the best one in two phases, modeled on C#'s better-function-member algorithm (Axiom 1: where Sharpy and C# betterness could disagree, Sharpy matches C#):
-
Applicability — keep the overloads whose arity fits (accounting for defaults and
*args), whose parameter names cover every keyword argument, and to whose parameter types every argument is assignable (including the documented primitive widenings such asint → long → double). -
Betterness — among applicable overloads, pick the single best using the ordered tie-break chain: exact match over conversion → better (lower-cost) implicit conversion → more specific type → fewer type parameters → non-variadic over variadic → CLR-level specificity. If no overload is strictly better than all others, the call is ambiguous (
SPY0353).
def f(x: int): ...
def f(x: float): ...
f(42) # Calls f(int) - exact match beats the int→float widening
f(3.14) # Calls f(float) - exact match
Ambiguous Overloads¶
If no single overload is better than all others, the call is ambiguous:
def f(x: int, y: float): ...
def f(x: float, y: int): ...
f(1, 2) # ERROR (SPY0353): Ambiguous - neither candidate is better at both positions
Resolution: Use explicit type conversions to disambiguate:
f(1, 2.0) # Calls f(int, float)
f(1.0, 2) # Calls f(float, int)
f(1 as! float, 2) # Explicitly calls f(float, int)
Default Parameters and Overloads¶
Default parameters widen the applicable set, which can create ambiguity:
def greet(name: str): ...
def greet(name: str, greeting: str = "Hello"): ...
greet("Alice") # Ambiguous - both overloads are applicable to a single string argument
Recommendation: Avoid overloads that differ only in having additional defaulted parameters.
Named Arguments in Overload Resolution¶
Named arguments participate in overload resolution by filtering which overloads are candidates. An overload is only considered if it has a parameter matching each named argument's name:
def do_work(num: int, message: str = "Hello") -> None:
print(f"{num}: {message}")
def do_work(count: int) -> None:
print(f"Count: {count}")
do_work(21) # Calls do_work(count) - standard resolution prefers no optional params
do_work(num=21) # Calls do_work(num, message) - only this overload has 'num' parameter
do_work(count=21) # Calls do_work(count) - only this overload has 'count' parameter
This allows named arguments to disambiguate between overloads that have the same parameter types but different parameter names.
Inheritance and Parameter Names:
When a method is overridden with different parameter names, the compiler uses the static type of the receiver to determine valid parameter names:
class Animal:
def eat(self, food_type: str = "grub") -> None:
pass
class Monkey(Animal):
def eat(self, banana_type: str = "green banana") -> None:
pass
m: Monkey = Monkey()
a: Animal = m
m.eat(banana_type="ripe banana") # OK - Monkey has 'banana_type'
a.eat(food_type="yummy grub") # OK - Animal has 'food_type'
m.eat(food_type="grub") # ERROR - Monkey doesn't have 'food_type'
Implementation - ✅ Native - Direct mapping to C# named argument resolution.
Reference¶
For the complete resolution algorithm, see Overload Resolution. For the C# rules it mirrors, see the C# Language Specification §12.6.4: Overload Resolution.
See Also¶
- Overload Resolution - Authoritative applicability and betterness rules
- Flexible Arguments - Positional-only, keyword-only, and kwargs support
- Function Default Parameters - Detailed guide to default parameter values and compile-time constant requirements
- Function Variadic Arguments - Comprehensive coverage of
*argsand unpacking - Parameter Modifiers -
ref,out, andinpass-by-reference parameters - Function Definition - Basic function syntax and rules
- Function Types - Function type syntax and compatibility