Stream: beginners

Topic: Question from GH - Mutation in Roc


view this post on Zulip Luke Boswell (Jul 05 2025 at 08:38):

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.

view this post on Zulip Kiryl Dziamura (Jul 05 2025 at 09:48):

I'm not sure roc would ever have an explicit linearity feature, but maybe it's possible to provide the info via lsp?

view this post on Zulip Anton (Jul 05 2025 at 09:53):

but maybe it's possible to provide the info via lsp?

Related older discussion: #ideas > assertUnique function. @ 💬

view this post on Zulip Anton (Jul 05 2025 at 09:53):

I have replied on the github issue

view this post on Zulip Brendan Hansknecht (Jul 05 2025 at 16:18):

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

view this post on Zulip Brendan Hansknecht (Jul 05 2025 at 16:18):

That will likely help a lot

view this post on Zulip Luke Boswell (Jul 06 2025 at 04:02):

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?

view this post on Zulip Brendan Hansknecht (Jul 06 2025 at 04:41):

I think yes is the answer

view this post on Zulip Richard Feldman (Jul 06 2025 at 13:40):

yeah for sure

view this post on Zulip Richard Feldman (Jul 06 2025 at 13:41):

as soon as you have an ownership system, there immediately becomes demand for explicit borrows

view this post on Zulip Richard Feldman (Jul 06 2025 at 13:41):

because otherwise you need horrendous APIs like

List.len : List(a) -> (List(a), U64)

view this post on Zulip Richard Feldman (Jul 06 2025 at 13:42):

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

view this post on Zulip Richard Feldman (Jul 06 2025 at 13:42):

both of which are atrocious

view this post on Zulip Richard Feldman (Jul 06 2025 at 13:42):

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:

view this post on Zulip Richard Feldman (Jul 06 2025 at 13:44):

the experiment we're trying is "can you get good performance in practice without a formal ownership/borrowing system?"

view this post on Zulip Richard Feldman (Jul 06 2025 at 13:44):

and so far I think the answer is yes

view this post on Zulip Richard Feldman (Jul 06 2025 at 13:45):

although the hypothesis remains untested on large code bases (since they don't exist yet) so it would be premature to declare victory haha

view this post on Zulip Magalame (Jul 09 2025 at 08:22):

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:

view this post on Zulip Luke Boswell (Jul 09 2025 at 08:24):

There is some discussion on HKP in the FAQ https://www.roc-lang.org/faq#higher-kinded-polymorphism

view this post on Zulip Luke Boswell (Jul 09 2025 at 08:26):

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.

view this post on Zulip Luke Boswell (Jul 09 2025 at 08:34):

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.

view this post on Zulip Magalame (Jul 09 2025 at 08:41):

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?

view this post on Zulip Kiryl Dziamura (Jul 09 2025 at 09:20):

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

view this post on Zulip Magalame (Jul 09 2025 at 11:29):

that helps a lot thanks! (I was assuming it was a linked list)
Is List a malloc-ed array then? Or a third thing?

view this post on Zulip Luke Boswell (Jul 09 2025 at 11:29):

Yes, List's are heap allocated

view this post on Zulip Kiryl Dziamura (Jul 09 2025 at 11:44):

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.

view this post on Zulip Magalame (Jul 09 2025 at 12:15):

I feel like getting a Vec.new() function from the tuple constructor is uhhh not ideal lol

view this post on Zulip Anton (Jul 09 2025 at 12:22):

I feel like getting a Vec.new() function from the tuple constructor

Can you elaborate? I don't recall Roc having anything like this

view this post on Zulip Magalame (Jul 09 2025 at 12:45):

I meant that writing a hypothetical function (Vec.new()) creating a vector relying on the tuple constructor seems impractical

view this post on Zulip Anton (Jul 09 2025 at 13:33):

Right, true, let me try to find some earlier discussions on this subject

view this post on Zulip Kiryl Dziamura (Jul 09 2025 at 13:57):

I'll just leave it here. Outdated syntax, but the idea is the same: https://github.com/Hasnep/roc-linear-algebra

view this post on Zulip Anton (Jul 09 2025 at 14:01):

Anton said:

Right, true, let me try to find some earlier discussions on this subject

#ideas > builtin matrix support @ 💬
Note: this is from before static dispatch, which has changed the customization of infix operators.

view this post on Zulip Brendan Hansknecht (Jul 09 2025 at 20:24):

I still think we should figure out how to support static arrays

view this post on Zulip Brendan Hansknecht (Jul 09 2025 at 20:24):

As things stand today, I expect them to just be magic wrapping tuples though

view this post on Zulip Kiryl Dziamura (Jul 09 2025 at 20:30):

Yeah, use of tuples in such cases (static collection of items with the same type) sounds like a workaround

view this post on Zulip Richard Feldman (Jul 09 2025 at 20:56):

what's the specific ergonomic problem someone would have if they tried to use that design?

view this post on Zulip Richard Feldman (Jul 09 2025 at 20:58):

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?

view this post on Zulip Richard Feldman (Jul 09 2025 at 20:59):

e.g. "I'm building a game in Roc and tuples with static dispatch for iteration is not ergonomic for me because _______"

view this post on Zulip Brendan Hansknecht (Jul 09 2025 at 23:21):

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.

view this post on Zulip Brendan Hansknecht (Jul 09 2025 at 23:22):

And the runtime length check should optimize away in many cases

view this post on Zulip Magalame (Jul 10 2025 at 01:52):

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

view this post on Zulip Luke Boswell (Jul 10 2025 at 01:53):

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.

view this post on Zulip Luke Boswell (Jul 10 2025 at 01:54):

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.

view this post on Zulip Richard Feldman (Jul 10 2025 at 01:57):

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:

view this post on Zulip Luke Boswell (Jul 10 2025 at 02:03):

I thought our List implementation was basically the same as Rust's vectors Vec<T>, vec! etc -- or at least very similar

view this post on Zulip Luke Boswell (Jul 10 2025 at 02:04):

We have List.with_capacity : U64 -> List(a) in our builtins

view this post on Zulip Luke Boswell (Jul 10 2025 at 02:05):

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?

view this post on Zulip Richard Feldman (Jul 10 2025 at 02:07):

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:

view this post on Zulip Brendan Hansknecht (Jul 10 2025 at 02:27):

I think a good example painful case is a btree.

view this post on Zulip Brendan Hansknecht (Jul 10 2025 at 02:28):

A btree is a useful datastructure and I think would be a pain in roc

view this post on Zulip Brendan Hansknecht (Jul 10 2025 at 02:28):

Fundmentally, it is a key value map, but the datastructure as a whole is parameterized on a static array size.

view this post on Zulip Magalame (Jul 10 2025 at 02:42):

Richard Feldman said:

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:

amazing, thanks! :)

view this post on Zulip Magalame (Jul 10 2025 at 03:14):

Also if I understand properly ffi is considered an anti-feature (#beginners > How to call C/C++/Rust from Roc? @ 💬 ) so, calling the usual math library workhorses (e.g. LAPACK) would be impossible, and they'd have to be re-implemented in native Roc?

view this post on Zulip Brendan Hansknecht (Jul 10 2025 at 03:22):

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.

view this post on Zulip Magalame (Jul 10 2025 at 03:38):

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?

view this post on Zulip Brendan Hansknecht (Jul 10 2025 at 03:45):

Yeah, i don't think lapack/blas are reasonable to reimplement. And I don't think you could do it efficiently in roc

view this post on Zulip Magalame (Jul 10 2025 at 04:38):

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?

view this post on Zulip Luke Boswell (Jul 10 2025 at 04:38):

Yeah, will be very easy to do

view this post on Zulip Luke Boswell (Jul 10 2025 at 04:39):

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.

view this post on Zulip Brendan Hansknecht (Jul 10 2025 at 04:43):

The best solution currently is to take an existing platform, ask question here, and just start hacking.

view this post on Zulip Kiryl Dziamura (Jul 10 2025 at 09:49):

Richard Feldman said:

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:

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.

view this post on Zulip Kiryl Dziamura (Jul 10 2025 at 09:54):

In terms of gamedev, "why do I have to unroll tuples manually when I need a static array for perf reasons?"

view this post on Zulip Richard Feldman (Jul 10 2025 at 11:07):

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

view this post on Zulip Richard Feldman (Jul 10 2025 at 11:09):

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

view this post on Zulip Richard Feldman (Jul 10 2025 at 11:10):

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

view this post on Zulip Richard Feldman (Jul 10 2025 at 11:12):

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

view this post on Zulip Richard Feldman (Jul 10 2025 at 11:15):

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?

view this post on Zulip Kiryl Dziamura (Jul 10 2025 at 11:27):

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, ...

view this post on Zulip Kiryl Dziamura (Jul 10 2025 at 11:28):

Ok, I probably just don't understand the downside of (elem; 12) syntax

view this post on Zulip Luke Boswell (Jul 10 2025 at 11:47):

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.

view this post on Zulip Luke Boswell (Jul 10 2025 at 11:49):

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

view this post on Zulip Richard Feldman (Jul 10 2025 at 12:18):

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

view this post on Zulip Brendan Hansknecht (Jul 10 2025 at 15:02):

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.

view this post on Zulip Anton (Jul 11 2025 at 10:24):

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