Stream: contributing

Topic: Migrating roc-isodate


view this post on Zulip Aurélien Geron (Jul 03 2026 at 12:58):

I'm in a migration mood, looking at migrating @Ian McLerran's roc-isodate's library. Hopefully, it shouldn't take too long, but I do have one question: how should I migrate parametrized modules? For example:

# Now.roc
module { now!, now_to_nanos } -> [date_time!, date!, time!]

and:

import dt.Now {
     now!: Utc.now!,
     now_to_nanos: Utc.to_nanos_since_epoch,
}

I guess the new approach is simply to define an opaque type like this:

Now :: { now! : ({} => Now), now_to_nanos: (Now -> U64) }.{ ... }

which can then be used like this:

import dt.Now

now = Now.create({now!: Utc.now!, now_to_nanos: Utc.to_nanos_since_epoch))
datetime = now.date_time!({})

Does this look good to you?

view this post on Zulip Anton (Jul 03 2026 at 13:10):

Seems logical to me

view this post on Zulip Aurélien Geron (Jul 03 2026 at 13:13):

Thanks @Anton

view this post on Zulip Aurélien Geron (Jul 03 2026 at 13:17):

Mmh, I just noticed that roc-isodate depends on two libraries:

They are only used for a few functions, so I think I'll just reimplement them locally rather than port two more libraries.

From rutils:

The roc-tinyparse library is only used to implement parse_frac_fmt so I think I'll use roc-parser instead since I just migrated it.

view this post on Zulip Aurélien Geron (Jul 29 2026 at 21:03):

I've almost finished migrating roc-isodate to the new compiler, all the tests pass, I just need to port the examples now. However, I don't know where to find Utc.now!, it looks like it's not in the basic-cli platform anymore (@Luke Boswell ?)

view this post on Zulip Aurélien Geron (Jul 29 2026 at 21:16):

Also, one example requires a platform that provides the Http.send! function. Does such a platform exist yet? Or maybe I should just simplify this example for now to avoid depending on Http?

Edit: the latest basic-cli 0.21.0 platform includes everything I need: Utc.now!, Sleep.millis!, and Http.send!.

view this post on Zulip Aurélien Geron (Jul 29 2026 at 21:26):

Oh and pf.Sleep is also missing.

view this post on Zulip Aurélien Geron (Jul 30 2026 at 02:15):

Finished at last! Migrating this package was particularly tricky because of all the Int * that I had to replace with concrete integer types, and all the resulting conversions I had to implement. Will someone kindly review the PR?

view this post on Zulip Aurélien Geron (Jul 30 2026 at 04:23):

If you want to try it out, I've created a temporary release.

For example, the following code queries https://timeapi.io/ to get the local time in Paris, then it parses the JSON output to get the ISO 8601 string, and roc-isodate's Datetime.from_iso_str parses that string, so the script can print the result nicely.

app [main!] {
        pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.21.0/4rAQg8kUYZ3Vksr4qMQHpaFYNiHSn9GgS7gVxghd1XYV.tar.zst",
        http: "https://github.com/roc-lang/http/releases/download/2.0.0/6ZUwqYhCS8PU9Mo6MF7oV82ET2o7KYb57CLKDq4cq4sS.tar.zst",
        dt: "https://github.com/ageron/roc-isodate/releases/download/0.8.0/B2h6tefXQtEz9VG6QukLDBoGXhRtwGLj9sZHDEJaTHkS.tar.zst",
}

import pf.Stdout
import pf.Http
import http.Method
import http.Request
import dt.DateTime as DT

main! = |_| {
        timezone = "Europe/Paris"
        request = Request.from_method(GET)
                .with_uri("https://timeapi.io/api/v1/timezone/zone?timeZone=${timezone}")
                .with_timeout(TimeoutMilliseconds(3000))
        response = Http.send!(request)?
        if response.status() >= 200 and response.status() <= 299 {
                iso_str = get_iso_str(response.body())?
                dt_now = DT.from_iso_str(iso_str)?
                date_str = dt_now.format("{YYYY}-{MM}-{DD}")
                time_str = dt_now.format("{hh}:{mm}:{ss}")
                _ = Stdout.line!("The current Zulu date is: ${date_str}")
                _ = Stdout.line!("The current Zulu time is: ${time_str}")
                Ok({})
        } else {
                Err(FailedToGetServerResponse(response.status()))
        }
}

get_iso_str : List(U8) -> Try(Str, _)
get_iso_str = |bytes| {
        str = bytes->Str.from_utf8()?
        response : { local_time : Str }
        response = Json.parse(str)?
        Ok(response.local_time)
}

Note: timeapi.io doesn't actually give the right time. :smile:

view this post on Zulip Richard Feldman (Jul 30 2026 at 11:29):

very nice! :smiley:

view this post on Zulip Richard Feldman (Jul 30 2026 at 11:31):

if you want to get fancy with it, a thing you can do with the new compiler is expose a function that takes a formatting Str and then returns a Try containing a function which formats a date string using that format

view this post on Zulip Richard Feldman (Jul 30 2026 at 11:32):

then you can do like:

Ok(format) = DT.formatter("{YYYY}-{MM}")

view this post on Zulip Richard Feldman (Jul 30 2026 at 11:32):

and if you have a syntax error in your format string, you'll find out at compile time

view this post on Zulip Richard Feldman (Jul 30 2026 at 11:34):

and if not, then you can just call format(str) and it'll be even more efficient because it doesn't need to parse the YYYY-MM string at runtime, since that work already happened at compile time! :rocket:


Last updated: Aug 12 2026 at 12:35 UTC