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 from it ==, <>, <, <=, >, >=), #?, $ 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 -> other as Account <- rtn as Integer: accountId <=> other.accountId
operator #? as pure <- rtn as Integer: #? accountId
operator $ as pure <- rtn as String: `Account-${accountId}`
override operator ? as pure <- rtn as Boolean: accountId? and balance?
Here the account's identity is its accountId (not balance): <=> compares it and #? hashes it, and $ shows a formatted string. Default operator would compare and hash both fields and show both in the string.
#? GOES WITH SAMENESS: a hand-written #? needs a hand-written sameness (<=>, or == for a type that cannot be ordered) that compares what it hashes, and vice versa - a #? on its own, or 'default operator #?' beside a hand-written <=>, is E07184. ==, <>, <, <=, >, >= are generated from the sameness, never written (E07181).
YOU CAN MIX: write the operators you want custom FIRST, then 'default operator' LAST:
operator $ as pure <- rtn as String: `Custom: ${name}` default operator
'default operator' must be the last item (anything after it is E01086), and it generates only the operators not already written - here everything except $. Write 'operator $', not 'override operator $': there is no parent $ to override (E05110).
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 sameness, 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 //Sameness by accountId only, not balance: == and <> are generated from <=> operator <=> as pure -> other as Account <- rtn as Integer: accountId <=> other.accountId //A hand-written sameness needs a hand-written #? hashing the same field 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
E07181 — For simple value types, 'default operator' generates all standard operators in one line. Mixing in a hand-written < does not work: < is generated from the type's <=> (here the defaulted one), so writing it by hand is E07181 and could only disagree with <=>. Manual implementation is for custom behaviour - and then the sameness (<=>) and #? are written together. See ek9 -h E07181 for details.
Incorrect:
operator < as pure -> other as Address <- rtn as Boolean: street < other.street default operator
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: operator, default, string, override, hashcode, manual, generate