Should I write #? and $ manually or use default operator?
← Operators and Expressions · Ref: Q1055
Use 'default operator' when field-by-field behaviour is correct. Write manual operators only when you need custom logic.
DEFAULT OPERATOR (recommended for most types):
defines record Address street as String: String() city as String: String() default operator
This generates #?, $, ==, <>, <=>, and ? automatically from all fields. The generated $ concatenates field strings. The generated #? combines field hashes.
MANUAL OPERATORS (when you need custom behaviour):
defines class Account accountId as Integer: 0 balance as Float: 0.0
operator #? as pure <- rtn as Integer: #? accountId
operator $ as pure <- rtn as String: `Account-${accountId}`
Here #? hashes only the accountId (not balance), and $ shows a formatted string. Default operator would hash both fields and show both in the string.
YOU CAN MIX: use 'default operator' then override specific ones:
default operator override operator $ as pure <- rtn as String: `Custom: ${name}`
The default generates all operators, then your override replaces $.
See Q116 for what default operator generates. See Q1047 for #? on records. See Q1053 for manual #? on classes. See Q949 for default operator rules.
Example
defines module qa.operators.defaultvsmanual defines record //DEFAULT: all operators generated from fields Address street as String: String() city as String: String() Address() -> street as String city as String this.street :=: street this.city :=: city default operator defines class //MANUAL: custom hashcode and string Account accountId as Integer: 0 balance as Float: 0.0 default private Account() Account() -> accountId as Integer balance as Float this.accountId :=: accountId this.balance :=: balance //Hash only by accountId, not balance operator #? as pure <- rtn as Integer: #? accountId //Custom display format operator $ as pure <- rtn as String: `Account-${accountId}: ${balance}` override operator ? as pure <- rtn as Boolean: accountId? and balance? defines program DefaultVsManualDemo() stdout <- Stdout() //Record with default operator addr <- Address("123 Main St", "London") stdout.println(`Address: ${addr}`) stdout.println(`Hash: ${#? addr}`) //Class with manual operators acct <- Account(1001, 5250.75) stdout.println(`${acct}`) stdout.println(`Hash: ${#? acct}`)
Common mistakes
E08180 — For simple value types, 'default operator' generates all standard operators in one line. Manual implementation is only needed for custom behaviour.
Incorrect:
operator == as pure ... operator $ as pure ... operator #? as pure ...
Correct:
default operator
Other ways to ask this
- When should I use default operator instead of writing operators by hand?
- Show the difference between default operator and manual #? and $ implementations
- Can I mix default operator with custom operator overrides?
Coming from another language?
Java: records auto-generate, classes need manual. Python: @dataclass auto-generates. EK9: 'default operator' works on both records and classes.
Keywords: string, default, operator, generate, manual, hashcode, override