Can an interface import anything from the app's platform?
I want to use interfaces as a tool for breaking up complex apps into multiple files, but that often requires referencing platform-defined types. Is this a sign that I'm misusing interfaces?
I'm pretty sure the answer is no. :/
Importing effects would be very nice, too... though I suppose I can always wrap interface functions in a wrapper function and pass in effects that way...
I've tried this, and it works, but there are caveats.
I'm working on the Advent of Code, and the way they structure their puzzles is that there are two daily puzzles. They share the same input, so I wrote a main.roc
interface that reads, parses, and outputs the data and another mainA.roc
and mainB.roc
apps that did the pure puzzle-solving. Given this structure, most of my code interacting with the platform lives in main.roc
.
When I run roc test mainA.roc
or roc dev mainB.roc
everything works well. If I run roc test
or roc dev main.roc
, I get errors. By running roc check
by itself, I get the error that the file has an unexpected name. If I change the name according to the error, roc check
runs seemingly forever.
I don't know if the expectation is to have all the code interacting with the platform in one file and only have code that solely depends on Roc's Standard Library in the interface file?
Sample code:
main.roc
mainA.roc
Hello, I have a platform that requires a type called Model
that the user specifies.
This is in a file called Platform.roc
platform "platform"
requires { Model } { init : App.Application }
exposes [ Model ]
packages {}
imports [ App ]
provides [mainForHost]
mainForHost = init
My App.roc
file needs to import the type from Platform.roc
interface App
exposes [...]
imports [Platform]
When I try to run this code I get an error saying that the App.roc
file does not exist. I think this is because of a circular import.
Is there any way to avoid this problem?
Seems to be the same sort of issue as the other thing in this thread but the fix doesn't seem possible, or I am misunderstanding.
I don't think you need to import your app there
the model type is already passed along. I think you can use { init : _ }
(the underscore is a type hole, meaning the compiler will figure out that type)
Now I don't get an error roc check is hanging.
hmm wait. is App
not your app module?
you need some module that has a header similar to this
app "breakout"
packages { pf: "platform/main.roc" }
imports [pf.Game.{ Bounds, Elem, Event }]
provides [program] { Model } to pf
program = ...
which is then used in the platform as
platform "gui"
requires { Model } { program : _ }
exposes [Game]
packages {}
imports [Game.{ Bounds, Elem, Event }]
provides [programForHost]
My file strucure is:
main.roc
/platform
/App.roc # imports Platform
/Platform.roc # Named `Platform.roc` instead of `main.roc` like the examples
I see
Im trying to get the { Model }
in the second code block from the gui example to be accessible from a different file
to be clear: it is available in Platform.roc
but you want it to be available in App.roc
?
Yes
I want the user to import things from the App.roc
file that will be typed with the Model
type they are providing to me. Namely a record with a function with Model
in the type signature.
right, that does not work (today). The problem is circular dependencies. The app uses the platform, so then it would be circular if the platform could use types defined in the app. The Platform.roc
module is special, because it is really the highest module. the app module (and other user modules) are in the middle, and then the other modules of the platform are below that in the hierarchy
the workaround here is to use type variables I think
so your App.roc
would define an Application model := ...
, so it is generic over whatever model the user provides
ty for the explanation.
thanks i think i get it
i think
I ended up needed to do a record like this
Application model : {
routes : List (Route.Route model),
}
works perfectly :smile:
Last updated: Jul 05 2025 at 12:14 UTC