https://github.com/roc-lang/roc/issues/7946
Roc has a lot of features I'm keen to try out. I'm also curious to know practical it is for dealing with linear algebra so I was wondering if vectors/matrices are supported?
My second question concerns how Roc deals with mutation. As I understand it, the optimisation allowing for in-place mutation relies on the mutated variable not being referenced to later in the program. In programs dealing with larfe matrices, it'd be very convenient to ensure that mutation is always in place, so are there any features of the language to prevent a variable from being re-used? For example Rust has the notion of ownership.
I'm not sure roc would ever have an explicit linearity feature, but maybe it's possible to provide the info via lsp?
but maybe it's possible to provide the info via lsp?
Related older discussion:
I have replied on the github issue
Oh.... Now that we plan to have an interpreter, it is probably even easier to make a integrated tool that creates a report like scalene has. Essentially, report on, time, bytes copied, uniqueness failures, and such per line of code
That will likely help a lot
Follow up question
Thanks for the quick answer! I note the explicit desire not to reproduce Rust's ownership system, is it for the sake of simplicity?
I think yes is the answer
yeah for sure
as soon as you have an ownership system, there immediately becomes demand for explicit borrows
because otherwise you need horrendous APIs like
List.len : List(a) -> (List(a), U64)
because if everything is owned, then either you have to deep-clone the list before passing it to a function to get its length, or else that function has to also return the list it was given
both of which are atrocious
the solution is having a system for borrowing, at which point you have a borrow checker, and at which point I think we have to delete "friendly" from "fast, friendly, functional" :sweat_smile:
the experiment we're trying is "can you get good performance in practice without a formal ownership/borrowing system?"
and so far I think the answer is yes
although the hypothesis remains untested on large code bases (since they don't exist yet) so it would be premature to declare victory haha
hi! i'm the person who opened the issue on github, thank you for taking the time to entertain my question, I have some more if I havent bored you yet:
do
notation would be a nice feature has to have been discussed, try
seems equivalent to the >>=
bind operator, and it could avoid a lot of ?
boilerplate. So I wonder where I could read about what guided you to go the ?
path in the end.There is some discussion on HKP in the FAQ https://www.roc-lang.org/faq#higher-kinded-polymorphism
- the tutorial makes a very eloquent case for the use of Result + extensible types. It looks very elegant and fun to use. I also imagine that the question of whether
do
notation would be a nice feature has to have been discussed,try
seems equivalent to the>>=
bind operator, and it could avoid a lot of?
boilerplate. So I wonder where I could read about what guided you to go the?
path in the end.
The conversation is spread over many threads and thousands of comments... in short the try
keyword is no longer in the new 0.1 language design, we just have ?
which short circuits with an Result.Err(..)
or ??
which gives a "default" value.
- looking through the documentation there does not seem to be an exposed type for static arrays/non-list arrays, did I miss it? And if no, what's the best way to add them to the language?
I think the plan is to never support this. You could implement something similar in userland using tuples etc if you need the performance e.g. for vectors or multi-dimensional matices.
I think the plan is to never support this. You could implement something similar in userland using tuples etc if you need the performance e.g. for vectors or multi-dimensional matices.
oh ok so there are no plans for vectors/arrays of any sort?
You will be able to create a nominal type for a matrix that would keep a List of fixed size which you never change. Lists are basically arrays (not static tho. but they aren't linked lists as well). Then it would be possible to write your own logic for different operators on this type such as sum or mul
that helps a lot thanks! (I was assuming it was a linked list)
Is List
a malloc
-ed array then? Or a third thing?
Yes, List
's are heap allocated
In the other thread I was told that tuples are more suitable for the task. They live on stack and their size is known at compile time.
I feel like getting a Vec.new()
function from the tuple constructor is uhhh not ideal lol
I feel like getting a
Vec.new()
function from the tuple constructor
Can you elaborate? I don't recall Roc having anything like this
I meant that writing a hypothetical function (Vec.new()
) creating a vector relying on the tuple constructor seems impractical
Right, true, let me try to find some earlier discussions on this subject
I'll just leave it here. Outdated syntax, but the idea is the same: https://github.com/Hasnep/roc-linear-algebra
Anton said:
Right, true, let me try to find some earlier discussions on this subject
Note: this is from before static dispatch, which has changed the customization of infix operators.
I still think we should figure out how to support static arrays
As things stand today, I expect them to just be magic wrapping tuples though
Yeah, use of tuples in such cases (static collection of items with the same type) sounds like a workaround
what's the specific ergonomic problem someone would have if they tried to use that design?
like what application (not library; libraries exist to serve applications, so there needs to be a motivating application problem before we talk about libraries) that someone is trying to build in Roc, and what is the specific pain point they are encountering with this design?
e.g. "I'm building a game in Roc and tuples with static dispatch for iteration is not ergonomic for me because _______"
I mean if we add essentially List.get
on homogeneous tuples along with some other helpers for initialization and what not, I think we have static arrays that work.
And the runtime length check should optimize away in many cases
Richard Feldman said:
what's the specific ergonomic problem someone would have if they tried to use that design?
It seems like writing the equivalent of Rust's Vector.with_capacity(500)
would require the user to manually write (0,0,0,...496 times 0..., 0)
which seems impractical
This is what I meant by code-gen. I imagine in future there will be simple scripts (or editor plugins) that can make this super trivial... "hey give me a MxN shaped thing with these features". Who knows, maybe even AI tooling can assist.
So while it's not builtin into the standard library, it's very easy to get one. It compiles very fast and runs very fast.
Magalame said:
Richard Feldman said:
what's the specific ergonomic problem someone would have if they tried to use that design?
It seems like writing the equivalent of Rust's
Vector.with_capacity(500)
would require the user to manually write(0,0,0,...496 times 0..., 0)
which seems impractical
sorry, what's Vector
in Rust? :sweat_smile:
I thought our List
implementation was basically the same as Rust's vectors Vec<T>
, vec!
etc -- or at least very similar
We have List.with_capacity : U64 -> List(a)
in our builtins
It might be helpful to add a brief explanation in the top of our List
builtin docs about what lists are. I thought we had something written up somewhere but I haven't found it yet or can't recall where I saw it.
It might be helpful also to add a FAQ entry.
@Anton WDYT?
yeah if we're talking about Rust's Vec
, our List
is essentially the same, so yeah - you can already use that for a Matrix if you'd be ok using Rust's Vec
for that purpose :smile:
I think a good example painful case is a btree.
A btree is a useful datastructure and I think would be a pain in roc
Fundmentally, it is a key value map, but the datastructure as a whole is parameterized on a static array size.
Richard Feldman said:
yeah if we're talking about Rust's
Vec
, ourList
is essentially the same, so yeah - you can already use that for a Matrix if you'd be ok using Rust'sVec
for that purpose :smile:
amazing, thanks! :)
Also if I understand properly ffi is considered an anti-feature (
) so, calling the usual math library workhorses (e.g. LAPACK) would be impossible, and they'd have to be re-implemented in native Roc?ffi is considered an anti-feature
Pretty much
calling the usual math library workhorses (e.g. LAPACK) would be impossible
Either the platform has to support is or the platform has to support primitives for interacting with libffi
to load any aribitrary library.
they'd have to be re-implemented in native Roc?
This is the way for most things.
I think re-implementing BLAS or LAPACK in Roc (or any language) would require a prohibitive amount of work hours. So I imagine the best way to incorporate these functionalities might be to have a platform that could call them. Is there a standard guide on how to create one?
Yeah, i don't think lapack/blas are reasonable to reimplement. And I don't think you could do it efficiently in roc
And could it be realistic to build a platform that supports them? And if so is there a guide on how to build platforms in roc?
Yeah, will be very easy to do
There isn't a guide yet.
Platform development is currently quite challenging. The design used for calling Roc in our Rust compiler is less mature, and there is a bunch of strangeness to get it all working together.
The new zig compiler will be simple by comparison. Roc just builds a static library and the platform links that and calls into Roc.
The best solution currently is to take an existing platform, ask question here, and just start hacking.
Richard Feldman said:
yeah if we're talking about Rust's
Vec
, ourList
is essentially the same, so yeah - you can already use that for a Matrix if you'd be ok using Rust'sVec
for that purpose :smile:
Yes, but we're rather talking about static vs dynamic arrays. Both List in roc and Vec in rust are dynamic so the comparison isn't relevant. Array allocates on stack if memory consumption is known at comptime, right? But Vec (or List) always allocates on heap. So if you want to have better performance (eliminating the reference resolution and potentially improving locality), in roc you have to use tuples. But you can't do arr: [u32; 20]
like in rust. You have to generate code for tuples. Or roc can have syntax sugar for the same experience.
In terms of gamedev, "why do I have to unroll tuples manually when I need a static array for perf reasons?"
when I've talked to game devs, I've heard they use a very small number of vector and matrix sizes, and they don't get bigger than 4x3
so making a 4x3 matrix abstraction would just mean writing like
Mat4x3 : ((F32, F32, ...etc
...and then from then on you just use Mat4x3
everywhere
so the ergonomics cost to game devs seems trivial, especially since they can just get a library off the shelf that has made those few specific type aliases
as for non-gamedev use cases, past a certain size you don't want stack allocations anymore bc you'll get stack overflows, so at that point you switch to Vec/List anyway
also regarding btrees, if I'm making a library for that I'm picking a specific hardcoded stack size for my arrays anyway, right? At which point I'm making a type alias for it, and maybe that one type alias looks long and silly, but the ergonomics impact is limited to one line of code in my whole library, right?
Not sure how a long line where you need to leave a comment that this tuple contains N elements (because otherwise you would count it one by one) is better than describing it in type system. I anticipate a popular package that exports aliases:
Vec2(elem) : (elem, elem)
Vec3(elem) : (elem, elem, elem)
...
Vec12(elem) : (elem, elem, elem, elem, ...
Ok, I probably just don't understand the downside of (elem; 12)
syntax
We'll be able to try it out really soon :grinning_face_with_smiling_eyes:
I'm looking forward to experimenting with lots of API ideas and making packages.
A concrete example of how I plan on using these tuples is Multivectors for a 2D PGA package, basically improve on https://github.com/lukewilliamboswell/roc-pga2d
Kiryl Dziamura said:
Ok, I probably just don't understand the downside of
(elem; 12)
syntax
hm, I guess if that were just syntax sugar for the same tuple elem type repeated that many times, that would be fine
Yeah, I think sugar on top of homogeneous tuples could be enough. We don't need that much. Creation with default value, querying with a runtime determined index, copying to/from lists, being able to query the size at runtime, iteration.
At the same time, I still think we could force everything through an allocating list until a few users come to us with performance problems we can't resolve without stack allocated lists.
Luke Boswell said:
It might be helpful to add a brief explanation in the top of our
List
builtin docs about what lists are. I thought we had something written up somewhere but I haven't found it yet or can't recall where I saw it.It might be helpful also to add a FAQ entry.
Anton WDYT?
List builtin docs sounds like the right place for that
Last updated: Jul 26 2025 at 12:14 UTC