I thought that it works kinda like UFCS(from Nim or D)
Person : {name: Str, age: I64}
have_birthday : Person -> Person
have_birthday = |person| {
{ ..person, age: person.age + 1}
}
...
person: Person
person = {name: "String", age: 24}
p2 = person.have_birthday()
But with this code Im getting
This have_birthday method is being called on a value whose type doesn't have that method:
┌─ main.roc:35:15
│
35 │ p2 = person.have_birthday()
│ ^^^^^^^^^^^^^
The value's type, which does not have a method named have_birthday, is:
{ age: I64, name: Str }
This tutorial only mentions method calling, but not method definition.
https://github.com/roc-lang/roc/blob/main/docs/mini-tutorial-new-compiler.md#method-calling
And here https://github.com/lukewilliamboswell/roc-platform-template-zig/blob/main/examples/all_roc_syntax.roc I see
# Define a nominal type with a custom is_eq method
Animal := [Dog(Str), Cat(Str)].{
is_eq = |a, b| match (a, b) {
(Dog(name1), Dog(name2)) => name1 == name2
(Cat(name1), Cat(name2)) => name1 == name2
_ => Bool.False
}
}
Is that possible to define a method for a structural type like my Person?
Or for a third party type defined in other package that I can't change (from this example it seams that its that u should define methods inside type itself like in java, and I dont believe its impossible to define them outside)
You can define a method like this:
main! = |_args| {
person: Person
person = {name: "String", age: 24}
p2 = person.have_birthday()
echo!(Str.inspect(p2))
Ok({})
}
Person := {name: Str, age: I64}.{
have_birthday : Person -> Person
have_birthday = |person| {
{ ..person, age: person.age + 1}
}
}
But I'm pretty sure you cannot add your own method to Person outside the module where it is defined.
You can:
Person and define your method therePerson as input and use the arrow syntax instead of a .Does that affect static dispatch for types I don't "own", e.g. adding a new arithmetic static dispatch method to numeric types in the library?
You can't define methods for types that you don't own, i.e. in a module other than where the type was defined (at least that was my last understanding). The "dot" syntax searches the module of the type of the first arg for the function, and so won't find your function.
You can use "->" to dispatch using the first arg for functions in general, like a pipe operator (1->myAdd(2)). Or you can always wrap the type.
So if I wanted to model say additive semigroups where types could meet that expectation by having a semi_add(a, b) function I could still create my own semi_add() for say integers of some kind. But how would I create a function that expects parameters that comply with additive semigroups?
I realize this is a pretty esoteric example and may be outside Roc's planned scope. Either way it still looks like a really fun and friendly language.
Afaik you can accept "all things that implement semi_add" but semi_add would have to be implemented by the type owner. Outside of just passing in the required semi_add function as an argument to whatever needs it, you can always do this at the value level ("All you need is data and functions", "Scrap your type classes") and create records of functions {value: a, semi_add: a, a -> a} but I think it'll be circuitous and probably (can't speak for the Roc devs) not idiomatic.
Edit: Just saw your comment in another thread referencing the above article :smile:
Thanks for the clarification. Really looking forward to creating something in Roc.
Last updated: Apr 10 2026 at 12:38 UTC