Stream: show and tell

Topic: Roc Iced


view this post on Zulip tarkah (Jul 05 2024 at 18:27):

I've been working on a platform to develop iced applications in Roc. For those not familiar, Iced is an elm inspired GUI framework for Rust.

There's a bunch of missing widgets & features I haven't mapped over yet, but I'd at least like to expose all builtin widgets with their configuration options and styling. I also want to expose some basic IO effects that can be used to define closures that the platform can run as iced tasks for producing application messages.

Repo link: https://github.com/tarkah/roc-iced

Here's a basic example of how it looks:

app [program, Model, Message] {
    iced: platform "../platform/main.roc",
}

import iced.Element exposing [Element]
import iced.Element.Container as Container
import iced.Element.Container exposing [container]
import Box exposing [box]

program = { init, update, view }

Model : { count : U64, isFooChecked : Bool, isBarChecked : Bool, input : Str }

Message : [
    IncrementCount,
    FooToggled Bool,
    BarToggled Bool,
    Input Str,
    Submitted,
]

init : Model
init = { count: 0, isFooChecked: Bool.false, isBarChecked: Bool.true, input: "" }

update : Model, Message -> Model
update = \model, message ->
    when message is
        IncrementCount -> { model & count: model.count + 1 }
        FooToggled isFooChecked -> { model & isFooChecked }
        BarToggled isBarChecked -> { model & isBarChecked }
        Input input -> { model & input }
        Submitted -> { model & input: "" }

view : Model -> Element Message
view = \model ->
    Column [
        Text "Roc + Iced <3",
        Button {
            content: Text "Pressed $(Num.toStr model.count) times",
            onPress: Active IncrementCount,
        },
        Checkbox {
            label: "Foo",
            isChecked: model.isFooChecked,
            onToggle: Active (box FooToggled),
        },
        Checkbox {
            label: "Bar",
            isChecked: model.isBarChecked,
            onToggle: Active (box BarToggled),
        },
        Checkbox {
            label: "Baz",
            isChecked: Bool.false,
            onToggle: Disabled,
        },
        TextInput {
            value: model.input,
            width: Fixed 150,
            onInput: Active (box Input),
            onSubmit: Active Submitted,
        },
    ]
    |> body

body = \elem ->
    elem
    |> container
    |> Container.center Fill

view this post on Zulip Brendan Hansknecht (Jul 05 2024 at 18:30):

Awesome!

view this post on Zulip Jasper Woudenberg (Jul 05 2024 at 19:17):

Very cool. This really does look super familiar to Elm, just based on that example I feel I could build a basic GUI app with this, something I've otherwise never done before. Super excited!

view this post on Zulip Richard Feldman (Jul 05 2024 at 19:50):

very cool, tarkah! :smiley:

view this post on Zulip Richard Feldman (Jul 05 2024 at 19:50):

does Iced have an equivalent of Elm's lazy?

view this post on Zulip tarkah (Jul 05 2024 at 20:07):

Thanks everyone!

Richard Feldman said:

does Iced have an equivalent of Elm's lazy?

It does, see https://docs.rs/iced/latest/iced/widget/fn.lazy.html. I'm curious how this could be implemented so that we could leverage the Roc Hash ability output for use w/ this widgets Rust Hash boundary.

view this post on Zulip Richard Feldman (Jul 05 2024 at 21:19):

should be doable

view this post on Zulip Richard Feldman (Jul 05 2024 at 21:20):

hashing is an interesting strategy for this…I wonder how it’s been working for Iced in practice!

view this post on Zulip tarkah (Jul 05 2024 at 22:21):

I'm assuming it's quite a naive implementation. Iced hasn't matured to the point where those optimizations have really been looked at. There's also no view diff that occurs before render. The good news is there's generally lots of overhead since it's Rust

view this post on Zulip Richard Feldman (Jul 05 2024 at 22:46):

yeah this exact scenario is something that really worries me about using Elm Architecture in the absence of persistent data structures (which neither Rust nor Roc use)

view this post on Zulip Richard Feldman (Jul 05 2024 at 22:47):

because the super cheap reference equality check that Elm relies on to make lazy as fast as it is…only works with persistent data structures :sweat_smile:

view this post on Zulip Richard Feldman (Jul 05 2024 at 22:47):

and lazy has been the key to fixing nearly every performance problem in every Elm application I’ve ever worked on

view this post on Zulip Richard Feldman (Jul 05 2024 at 22:48):

so lazy being significantly less effective is a concern…but hard to say what it would mean in practice without trying it in the real world!

view this post on Zulip Richard Feldman (Jul 05 2024 at 22:49):

which is why I was hoping Iced had some experience reports on this approach

view this post on Zulip tarkah (Jul 05 2024 at 22:54):

I maintain a few large iced applications and I've never had to reach for lazy. I've actually never used it :sweat_smile:

view this post on Zulip Luke Boswell (Jul 05 2024 at 23:55):

I'm interested to know if you have seen the Xilem work, and what your thoughts are there. I don't really understand it, but have been interested to know if that will be a good fit.

view this post on Zulip Richard Feldman (Jul 06 2024 at 01:12):

tarkah said:

I maintain a few large iced applications and I've never had to reach for lazy. I've actually never used it :sweat_smile:

wow, very different from the situation in the browser!

view this post on Zulip Richard Feldman (Jul 06 2024 at 01:12):

I would not have guessed that

view this post on Zulip Richard Feldman (Jul 06 2024 at 01:12):

but that's really good to know!

view this post on Zulip Richard Feldman (Jul 06 2024 at 01:12):

have you run into performance problems that you had to apply optimizations to fix?

view this post on Zulip tarkah (Jul 06 2024 at 03:29):

@Luke Boswell I'm aware of Xilem but haven't looked into it much yet.

Richard Feldman said:

have you run into performance problems that you had to apply optimizations to fix?

Definitely with the scrollable widget. The current implementation doesn't virtualize the viewport, so layout can be very expensive if there's a lot of widgets outside the viewport. I've used both pagination and lazy loading techniques to get around this when there's lots of data. However hecrj is working on a List widget that'll mostly solve this.

Outside of that just adding caching when necessary for costly computations during view, but this is rare. The canvas widget has a cache API since things can get costly when producing geometry.

Text is probably the most expensive thing to layout, but with the move cosmic text and the Paragraph API which handles caching, it's been pretty good. For example in Halloy we build a scrollable with up to 10k messages and still get reasonable performance in release mode. I use lazy loading here so we only hit 10k messages if someone scrolls for quite a while... lol

view this post on Zulip Hannes (Jul 06 2024 at 04:11):

I find this particularly exciting because the upcoming Cosmic desktop environment uses Iced, so Roc could be very well placed to write new native Cosmic apps :)

I'll be trying roc-iced soon and if you want @tarkah I can leave feedback on GitHub issues or in Zulip?

view this post on Zulip tarkah (Jul 06 2024 at 04:15):

That sounds great @Hannes. Feel free to open issues on GH, though keep in mind it's very incomplete today.

I'd love to achieve full coverage on the widget API including styling, but mapping everything and adding the glue is pretty slow going and I don't have tons of time to throw at this. Hopefully someone else familiar with roc and iced can help me chip away at it!


Last updated: Jul 06 2025 at 12:14 UTC