Is it possible to provide an implementation of an ability for a type after the type has already been declared?
For example, lets say I wanted to create a library for working with polynomials and I wanted to support not only Num *
, but also potentially custom numeric types such as imaginary numbers or numbers in a Galois Field for Reed–Solomon codes. Is there any way I could define an ability for arithmetic operations and then make Num *
implement this ability so that I could use regular builtin numbers with my functions? Or is there some other way of accomplishing this behavior outside of abilities?
so for custom number types specifically (e.g. so that +
and such can work on them), it's come up a number of times but there isn't a concrete plan at the moment
for the broader question of:
Is it possible to provide an implementation of an ability for a type after the type has already been declared?
in the general case, it's a goal not to support orphan instances for abilities
but custom number types is a common recurring request, and it's definitely on the table to include something specific for numbers
we just don't have a concrete plan on it right now
Seems like maybe a custom Arithmetic ability and wrapper functions for a specific type are an okay workaround in the mean time? Not sure if this makes any sense at all
CustomNum.roc
module [
CustomNum,
toNum,
fromNum,
specialAdd,
add
]
Arithmetic implements
add : a, a -> a where a implements Arithmetic
CustomNum a := Num a
implements [
Arithmetic {
add: customNumAdd,
},
]
fromNum : Num a -> CustomNum a
fromNum = \a -> @CustomNum a
toNum : CustomNum a -> Num a
toNum = \@CustomNum a -> a
customNumAdd : CustomNum a, CustomNum a -> CustomNum a
customNumAdd = \@CustomNum a, @CustomNum b -> @CustomNum (Num.add a b)
# imagine some special math stuff
specialAdd : a, a -> a where a implements Arithmetic
specialAdd = \a, b -> add a b |> add b
main.roc
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" }
import pf.Stdout
import pf.Task
import CustomNum
specialAddPlainNum : Num a, Num a -> Num a
specialAddPlainNum = \a, b ->
CustomNum.specialAdd (CustomNum.fromNum a) (CustomNum.fromNum b)
|> CustomNum.toNum
main =
specialAddPlainNum 3 5 |> Num.toStr |> Stdout.line!
Also not really sure how custom abilities work with modules. Seems like I can't have the ability in its own module decoupled from this specific opaque type, and the only way to get the specialAdd
function to work in main.roc
was to expose add
?
You can have an ability in it's own module. All ability member functions are just required to be exposed.
This is need for the ability to specialize and be possible to implement across module boundaries
Then just import the ability and implement it in any other module the same way you implemented it on CustomNum
Oh okay cool. I might try this approach and see how it works out. For my actual use case of polynomials I don’t need to solve the general case but it definitely feels better to solve the general case. Also might be worth including a snippet of how abilities interact with modules in the abilities page on the website, unless that’s there already and I just missed it
If you have any improvements to that Abilities guide, that would be most appreciated! :smiley:
I don’t feel very qualified but if I get a better hang of how they work maybe I’ll take a stab at it :laughing:
My guess is, your about as qualified as I was when I wrote that guide (probably more since you've made something with an Ability) :sweat_smile:
I had to scratch around to find the syntax that worked, and then made that guide so I wouldn't forget it (or at least know where to look it up).
Haha fair enough
Seems another thing missing from the guide is how to specify a type variable should implement multiple abilities. Had to omit a type signature and see what the language server inferred to see that it was &
Last updated: Jul 06 2025 at 12:14 UTC