Stream: beginners

Topic: library depending on platform types


view this post on Zulip Romain Lepert (Jun 24 2026 at 17:43):

Suppose I am developing a plotting library that depends on roc-ray rr.Draw for rendering.

now the user code will also depend on roc-roc for things like rr.Color.

how do it write the imports for that ?

should the library be the single source of truth, declare the platform dependency and reexport roc-ray types ?

Should both declare platform dependency ?

how does that looks like in the following directory structure

/examples
    /demo.roc
/src
   /MyLibrary.roc

I tried to dig into https://github.com/roc-lang/roc/blob/main/docs/langref/modules.md but still not sure how it is supposed to fit together

view this post on Zulip Romain Lepert (Jun 24 2026 at 22:36):

# examples/demo.roc
app [Model, program] {
    rr: platform "https://github.com/lukewilliamboswell/roc-ray/releases/download/0.6/YsrMnLJw2ahDsyFXNEpipwWQfiM5DSxq5Ve6SyHczN7.tar.zst",
    plotlib: "../src/MyLibrary.roc"
}

import rr.Color
import plotlib.MyLibrary

xs = [0.0, 1.0, 2.0]
ys = [1.0, 0.0, 3.0]
color = Color.from_hex(0xFFFFFF)
MyLibrary.scatter_plot!(xs, ys, color)

...
# src/MyLibrary.roc
import rr.Draw  # roc does not know what rr is
import rr.Color # roc does not know what rr is

MyLib :: [].{
    scatter_plot! : List(F32), List(F32), Color => {}
    scatter_plot! = |x, y, color| {
        Draw.begin_frame!()
        for i in 0..<x.len() {
            pos = Draw.Vector2(x[i], y[i])
            Draw.circle_raw(pos, 1.0)
        }
        Draw.end_frame!()
        {}
    }
}

view this post on Zulip Luke Boswell (Jun 24 2026 at 23:17):

I feel like I would shift the begin/end frame out and have that be a caller responsibility. And then for the draw fn's I'd try accepting them as arguments to the function.

view this post on Zulip Luke Boswell (Jun 24 2026 at 23:18):

Like we could eliminate the dependency on the platform types that would be good

view this post on Zulip Luke Boswell (Jun 24 2026 at 23:18):

We might need to think about changing the API for roc-ray a little to make this more ergonomic... so this exploration is very helpful I think

view this post on Zulip Romain Lepert (Jun 25 2026 at 08:11):

Actually my question is more far reaching than this toy example.

How can a project standardize an interface that a platform will satisfy. The goal is to allow a Roc ecosystem to grow around the interface.

You can swap the implementation of the interface by swapping platform, but at the same time the ecosystem can build on the interface on its own.

For example, suppose I develop a UI library. I would want to standardize some form of Element, Event and so that:

Is it doable if the interface is pure Roc. Is it doable if the interface is pure and effectful Roc ?

view this post on Zulip Romain Lepert (Jun 25 2026 at 08:49):

In Roc you have the platform module that exposes a lists of types the platform provides to the application.

So I suppose my question is how can a library work with the platform exports without being an application.

Or alternatively, how can a library define an interface it depends on that will be satisfied by something else (likely a plateform's exposes).

PS: this is the same problem that WIT is solving for WASM Component Model.
https://youtu.be/phodPLY8zNE?si=NoKyFaFrXuGzvCBe&t=946

view this post on Zulip Romain Lepert (Jun 25 2026 at 12:42):

I know that there are discussion to standardize Path/Http types in the stdlib but other ecosystem will want specialized standard for libraries to sit in between applications and platforms.

view this post on Zulip Tobias Steckenborn (Jun 25 2026 at 19:44):

Could also fit into https://github.com/roc-lang/www.roc-lang.org/issues/77, to enhance such questions.

view this post on Zulip Luke Boswell (Jun 26 2026 at 06:10):

@Romain Lepert I think the plan loosely is that the platforms will depend on packages in the ecosystem that define common types

view this post on Zulip Luke Boswell (Jun 26 2026 at 06:10):

Where the effects dont match up you can manually wrap them

view this post on Zulip Luke Boswell (Jun 26 2026 at 06:11):

I really want to bring roc-lang/http and roc-lang/path online with a release so we can test this out in practice with basic-cli and basic-webserver and basic-ssg etc

view this post on Zulip Luke Boswell (Jun 26 2026 at 06:12):

We are still working our way up the stack so I don't have an example to point to yet.

view this post on Zulip Romain Lepert (Jun 26 2026 at 08:41):

right, roc-lang/http and roc-lang/path will help picture this :)

view this post on Zulip Luke Boswell (Jun 26 2026 at 08:45):

Yeah hopefully https://github.com/roc-lang/http/pull/3 passes CI -- and we can cut a release and begin to experiment with that

view this post on Zulip Richard Feldman (Jun 26 2026 at 13:33):

Romain Lepert said:

how can a library define an interface it depends on that will be satisfied by something else (likely a plateform's exposes).

PS: this is the same problem that WIT is solving for WASM Component Model.
https://youtu.be/phodPLY8zNE?si=NoKyFaFrXuGzvCBe&t=946

yeah so this should already be doable with static dispatch

view this post on Zulip Richard Feldman (Jun 26 2026 at 13:34):

so if you look at https://github.com/lukewilliamboswell/roc-ray/blob/4c08f773f982ace0ab81f8ea4aefeb4162cd96e5/platform/Draw.roc#L396

view this post on Zulip Richard Feldman (Jun 26 2026 at 13:40):

you can do this inside your package:

Chart(draw) :: { data_points : ChartDataPointsOrWhatever }.{
    init : ChartDataPointsOrWhatever -> Chart(draw)
    init = |points| { data_points: points }

    render! : Chart(draw) => {} where [
        line_raw! : LineRaw => {},
        text_raw! : TextRaw => {},
        # ...whatever else it needs from roc-ray's Draw
    ]
    render! = |self| {
        Draw : draw

        Draw.text_raw!(self.title)
        Draw.line_raw!(self.whatever)
    }
]

view this post on Zulip Richard Feldman (Jun 26 2026 at 13:43):

...and then the user of the package can do something like:

Chart : chart.Chart(pf.Draw)

my_chart : Chart
my_chart = Chart.init(my_data_points)

my_chart.render!()

view this post on Zulip Richard Feldman (Jun 26 2026 at 13:45):

basically what's happening here is that the package is saying "if you want me to support rendering, I need you to tell me about some type that has the methods line_raw! and text_raw! and they need to have these types: ..."

view this post on Zulip Richard Feldman (Jun 26 2026 at 13:45):

and then it delegates to that type

view this post on Zulip Richard Feldman (Jun 26 2026 at 13:46):

so this means your package can work not only with roc-ray itself, but also with any type-compatible platform, e.g. someone can make a "drop-in replacement for roc-ray" and it should Just Work with your charting package

view this post on Zulip Richard Feldman (Jun 26 2026 at 13:50):

and if you're granular with the methods you require (e.g. if you don't need draw_texture_raw! you don't mention it in your where constraints), you can be compatible with platforms that don't implement everything roc-ray implements, as long as they implement what you need

view this post on Zulip Richard Feldman (Jun 26 2026 at 13:51):

also, if someone is using a platform that doesn't fit roc-ray's API exactly, but there's some way they can polyfill it (make an implementation in terms of their platform's APIs) then they can do that - so instead of doing Chart : chart.Chart(pf.Draw) they do Chart : chart.Chart(MyPolyfilledChart) and they're all set

view this post on Zulip Richard Feldman (Jun 26 2026 at 13:52):

also this of course means they can swap in simulated drawing implementations for testing etc.

view this post on Zulip Romain Lepert (Jun 26 2026 at 14:17):

Thanks ! I’ll try that out

view this post on Zulip Eric Rogstad (Jun 26 2026 at 19:54):

Richard Feldman said:

and if you're granular with the methods you require (e.g. if you don't need draw_texture_raw! you don't mention it in your where constraints), you can be compatible with platforms that don't implement everything roc-ray implements, as long as they implement what you need

Can the compiler warn you if you have where constraints that aren't actually needed? Would be cool if there was a way to automatically prune those, so you don't accumulate spurious requirements as you change your implementation.

view this post on Zulip Richard Feldman (Jun 26 2026 at 20:00):

I'm not sure if it's a good idea though...you may want to over-constrain because you anticipate wanting them in the future and don't want to have to publish a breaking change

view this post on Zulip Eric Rogstad (Jun 27 2026 at 15:57):

Fair point. Seems like ideally there'd be a way to automatically detect them, but not have it bug you if you don't want to prune them.

view this post on Zulip Romain Lepert (Jul 10 2026 at 10:25):

@Richard Feldman sorry it took a while to get to this but I found out the syntax segfault currently

https://github.com/roc-lang/roc/issues/10062


Last updated: Jul 23 2026 at 13:15 UTC