Skip to content

Commit bc9e451

Browse files
committed
Add tests and docs for traits with required properties and methods
1 parent 3b811aa commit bc9e451

File tree

2 files changed

+110
-0
lines changed

2 files changed

+110
-0
lines changed

tests/IntegrationSpec.hs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,76 @@ spec = do
434434
end
435435
|]
436436
`shouldReturn` "Alice\n"
437+
it "Trait with required properties and methods - multiple methods" $ do
438+
compileAndRun
439+
[r|
440+
trait Describable = (name: String, age: Int) do
441+
describe :: Self -> String
442+
isAdult :: Self -> Bool
443+
end
444+
445+
struct Person = (name: String, age: Int)
446+
447+
impl Describable for Person = do
448+
describe self = self.name ++ " is " ++ (self.age as String) ++ " years old"
449+
isAdult self = self.age >= 18
450+
end
451+
452+
let main : IO = do
453+
let p = Person { name: "Alice", age: 30 }
454+
println (describe p)
455+
println (isAdult p)
456+
end
457+
|]
458+
`shouldReturn` "Alice is 30 years old\nTrue\n"
459+
it "Trait with required properties and methods - using 'is' clause" $ do
460+
compileAndRun
461+
[r|
462+
trait Describable = (name: String, age: Int) do
463+
describe :: Self -> String
464+
isAdult :: Self -> Bool
465+
end
466+
467+
struct Person = (name: String, age: Int) is Describable
468+
469+
impl Describable for Person = do
470+
describe self = self.name ++ " is " ++ (self.age as String) ++ " years old"
471+
isAdult self = self.age >= 18
472+
end
473+
474+
let main : IO = do
475+
let p = Person { name: "Bob", age: 17 }
476+
println (describe p)
477+
println (isAdult p)
478+
end
479+
|]
480+
`shouldReturn` "Bob is 17 years old\nFalse\n"
481+
it "Trait with required properties and methods - trait dispatch" $ do
482+
compileAndRun
483+
[r|
484+
trait Describable = (name: String, age: Int) do
485+
describe :: Self -> String
486+
end
487+
488+
struct Person = (name: String, age: Int)
489+
struct Employee = (name: String, age: Int, salary: Int)
490+
491+
impl Describable for Person = do
492+
describe self = self.name ++ " is " ++ (self.age as String) ++ " years old"
493+
end
494+
495+
impl Describable for Employee = do
496+
describe self = self.name ++ " is " ++ (self.age as String) ++ " years old (Employee)"
497+
end
498+
499+
let main : IO = do
500+
let p = Person { name: "Alice", age: 30 }
501+
let e = Employee { name: "Bob", age: 25, salary: 50000 }
502+
println (describe p)
503+
println (describe e)
504+
end
505+
|]
506+
`shouldReturn` "Alice is 30 years old\nBob is 25 years old (Employee)\n"
437507
it "Trait with required properties - missing property" $ do
438508
let prog =
439509
[r|

website/src/Documentation.elm

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,46 @@ struct Person = (name: String) is Printable
170170
171171
# Error: Type mismatch - trait requires Int, struct has String
172172
struct Person2 = (name: String, age: String) is Printable""" onLoadCode False "indigo"
173+
, h3 [] [ text "Required Properties and Methods" ]
174+
, p [] [ text "Traits can combine required properties with methods that perform meaningful operations using those properties:" ]
175+
, viewCodeBlock """trait Describable = (name: String, age: Int) do
176+
describe :: Self -> String
177+
isAdult :: Self -> Bool
178+
end
179+
180+
struct Person = (name: String, age: Int)
181+
182+
impl Describable for Person = do
183+
describe self = self.name ++ " is " ++ (self.age as String) ++ " years old"
184+
isAdult self = self.age >= 18
185+
end
186+
187+
let main: IO = do
188+
let p = Person { name: "Alice", age: 30 }
189+
println (describe p)
190+
println (isAdult p)
191+
end""" onLoadCode True "indigo"
192+
, p [] [ text "In this example, the Describable trait requires name and age properties, and provides methods that use these properties to compute results. The describe method formats a string using both properties, while isAdult performs a validation check." ]
193+
, p [] [ text "You can also use the is clause with traits that have both required properties and methods:" ]
194+
, viewCodeBlock """trait Describable = (name: String, age: Int) do
195+
describe :: Self -> String
196+
isAdult :: Self -> Bool
197+
end
198+
199+
struct Person = (name: String, age: Int) is Describable
200+
201+
impl Describable for Person = do
202+
describe self = self.name ++ " is " ++ (self.age as String) ++ " years old"
203+
isAdult self = self.age >= 18
204+
end
205+
206+
let main: IO = do
207+
let p = Person { name: "Bob", age: 17 }
208+
println (describe p)
209+
println (isAdult p)
210+
end""" onLoadCode True "indigo"
211+
, p [] [ text "The compiler validates that structs implementing the trait have all required properties, and you must provide implementations for all trait methods." ]
212+
, p [] [ text "Trait dispatch in Indigo requires concrete types at compile time. When you call a trait method, the compiler must be able to determine the concrete struct type to select the correct implementation. This means trait methods work best when called directly on concrete struct instances, rather than through polymorphic functions that accept Any." ]
173213
, h3 [] [ text "Trait Refinements" ]
174214
, p [] [ text "Traits can also have refinement types, similar to structs. These refinements are checked when creating struct literals that implement the trait:" ]
175215
, viewCodeBlock """trait PositiveNumber satisfies (x > 0)

0 commit comments

Comments
 (0)