I'm struggling to find a beginner-friendly explanation to the (very common!) question of why a Roc application can't have multiple platforms
I think part of what I'm struggling with is the curse of knowledge - every time I start trying to explain it I end up at "well the real answer is that it's because that wouldn't make any sense" which is completely unhelpful and definitely not friendly :sweat_smile:
some things I've tried so far (but have found unsatisfying) include:
analogy to frameworks or game engines: "What would it mean to have a single game (application) that runs on both Unity and Unreal Engine (multiple platforms) at the same time?"
analogy to web server frameworks: "what would it mean to have one web app (application) that runs on both Rails and Django at the same time?"
the low level angle: "binary executables have one entry point, and the platform is in charge of that, so if there were multiple platforms, which one would be in charge of that entry point? How would that one know when to invoke the others' main()s in case they needed to do initialization? What if their initializations stomped on each other? At that level of coordination, why wouldn't one of them just be a library?" etc.
security: "one of the selling points of platforms is that they have exclusive control over I/O primitives. Multiple platforms would sacrifice that!"
answering the question with a question: "what's the motivating use case? What specific benefit would there be to having multiple platforms instead of one?"
anyway, I'm not happy with any of these answers, but I'm not sure what else to try :sweat_smile:
if anyone has any other ideas, I'd love to hear them!
I think my conceptual model for why only one platform is the effect interpreter. The effect interpreter is like "the" runtime that executes my application, but because Roc is statically compiled it all happens at build and gets detangled into a highly optimised executable.
What's the question behind the question? I'm guessing it's "Am I locked into a plattform?"
With only superficial of Roc, I assume the answer is technically yes, but practically no. Roc probably makes it pretty easy to factor out the code that can be shared between platforms, so that you can practically run the same app on multiple platforms.
Just to throw in another metaphor (I don't think it's good): Your app is some electric device and the platform is a power socket. You can only plug your device into one socket at a time. But on vacation you can put an adapter in between, so that you can connect it to differently shaped power sockets.
This metaphor can actually be extended to explain that this only makes sense for compatible platforms. For example it's generally nonsensical to write an app for a CLI and expect the same app to work as a web server. Even with an adapter, it doesn't make sense to connect a normal device to a high power socket.
On the mental model I second Luke. The platform provides you with a set of instructions you can use, you give it back something in those instructions and it then does them according to your app.
Since these instructions come from the platform, you can't generally switch out the platforms. But you can write an app and then translate between similar instruction sets and therefore platforms.
Johannes Maas said:
What's the question behind the question?
This is great point. I actually don't know! A lot of people ask it, so maybe asking this follow up question will reveal a better way to answer it. :thinking:
It may be about fragmentation concerns. “I really like platform A, but I also need this effect from platform B. Do I need to fork them to combine them?”
An answer to that can be that platforms should expose high-level effects for their niche, but have the “more general” effects like File I/O and networking remain low-level such that the specifics can be implemented in platform-agnostic packages
It wasn't long ago that I had this exact question and it felt so concerning to me that I would be locked into a particlar platform. Like I feel like my code should be transferrable and I should be able to use everywhere. The platform / app abstraction actually supports this really well in Roc, but before I understood what it actually was and how it workd that was a serious concern I had.
One thing that helped me was seeing how in VDOM there is essentially 1 "app" that is made from two different platforms to make the front end (in JS with wasm) and backend server parts. I wouldn't expect anyone to dig as deep as I have to see how you can make it work so easily.
So one Idea I have that I've been toying with is making an Advent of Code template that actually has multiple apps that all use the same code. Its not a package or anything, but literally I can run roc run aoc-cli.roc -- day1
or something and then also run be able to run roc run aoc-web.roc
and it Just Works. This would show how you can structure your code with Modules and types that make it easy to share across platforms.
The other part I was confused about was cross-platform libraries. Like I was concerned that I would write e.g. PostgreSQL and it would only work on 1 platform. But with the design for platforms to expose low level primitives that package authors then build on top of, that wont be an issue. Unfortunately, without the Module params stuff this is harder to show people.
So I see the question behind the question is mostly a concern/query like "What are the the limitations or restrictions?" or "Why cant I just write one thing that does everything?"
In my opinion the answer should be, "Yes you can!" and "It works great!"
Another anxiety people might be trying to express with this question is "what happens if I pick the wrong platform, am I stuck?"
Yeah, I think the summary is that most people are worried about lock-in and limitations.
I think this will be a lot cleaner when we have the new way to pass around effect functions and import them. Cause then we can define a primitive base that can be shared between multiple platforms. Until then, there is no guarantee that two platforms will have supporting primitives.
Last updated: Jul 06 2025 at 12:14 UTC