Finally, I'm able to release roc-pg 0.4.0 with compile-time checking of SQL!
Building this has uncovered a number of issues and performance improvements in the Roc compiler, so I'm very grateful to @Richard Feldman for having the patience to make this work!
Here's a demo: https://www.youtube.com/watch?v=B3AdM5wmu60
Amazing demo, both for roc-pg and for Roc's compile-time capabilities! :tada:
Thanks for commenting and liking @Aurélien Geron ! This is the first video I post, so I need the algo boost :P
Hopefully creating videos like this will be one more way to spread Roc!
You have a nice setup for video recording: good audio and lighting, and an engaging tone, and obviously great content. I predict a rapid channel growth! :smile:
Perhaps add a video description with links to roc-pg, Roc, PostgreSQL? This definitely helps the video classification system. You can also include a table of contents in the description, for example:
0:00 Intro
1:30 Getting Started
4:15 Advanced Tips
8:00 Conclusion
It must start at 0:00. It also helps with sub-topic discovery.
-
Since Roc is not widely known yet, the term "Roc" may be misunderstood by YouTube's classification system, so you want to give it a lot of disambiguation opportunities.
Adding a channel description mentioning Roc and pointing to roc-lang.org will also help get your channel classified as Roc-related, which then helps classify your videos as Roc. If you want to attract other crowds, you'll want to mention functional programming, etc.
And apparently the more memes you can insert in your video, the better, although I personally find this trend exhausting. :sweat_smile:
I agree with all the comments, an amazingly well presented video! I'm tempted to use Neovim again just for the nice type errors :sweat_smile:
Thanks @Aurélien Geron I've added the timestamps, more links, trigger a "Verification" so that I'm allowed to have clickable links etc. Also a channel description!
I'm new to all this (incl. social media, my wife is disappointed if I don't Like the photos she shares, even if I was the one that took them :P) so any help is appreciated :sweat_smile:
@Niclas Ahden, this is amazing!
I would like to see a video where you explain the internals of the library, and some tricks you utilized. The most interesting part for me is: how you achieved typed results from a string parameter.
I like Zig comptime, and Roc feels limited, so I mostly experiment with problems that don't require comptime to solve it. But, based on this project, I guess Roc comptime is also quite powerful, just requires more skills than Zig.
Maybe because I'm new to FP. To create a struct type in Zig, @struct() is very useful. Parse the string, create the fields, then make a new type, based on them. But Roc does not have a builtin like this. I guess you don't create the type first, but immediately the value, and let the type inference do the typing?
I'm guessing the magic is based on from_quote. Say query! takes a Sql value, and that type has a from_quote function, then query!("SELECT ...") will actually call query!(Sql.from_quote("SELECT ...")). The Sql.from_quote part will run at compile time, and if there's no error the resulting Sql value will be passed to query! at runtime.
Check out the Custom Literals example.
You can make it more explicit by writing "SELECT ...".Sql or Ok(sql) = Sql.from_quote("SELECT ..."): these are equivalent and will also run at compile time.
Exactly, @Aurélien Geron put it really well! And yes, Roc’s comptime is really powerful and I think we’ll see all sorts of uses! I’ve done HTML,
XML, GraphQL, OpenAPI specs, and now SQL, so you can really do a lot with it!
We should experiment freely and see where we end up with some great and ergonomic solutions :)
Luke is also making use of comptime checking with roc-blueprint.
I understand the .from_quote part. But how does the Sql.from_quote create a struct from the string? Roc does not have a @struct() builtin like Zig.
The string is a constant, Roc will evaluate constants completely at compile time.
I understand this part too. What I don't understand: I have a parser, that parses a string according to some grammar. It's easy to extract the values from that string and put them into e.g. AST nodes, because my AST node type stores them at predefined fields, e.g. name: Str, and it creates a new instance of an already defined type, not a completely new type. But how to create a struct that has its Roc field names extracted from the string?
I would do this in Zig:
field_names of @Struct when defining the type.The signature in Zig 0.17.0 is this:
@Struct(
comptime layout: std.lang.Type.ContainerLayout,
comptime BackingInt: ?type,
// How to do this in Roc?
comptime field_names: []const []const u8,
comptime field_types: *const [field_names.len]type,
comptime field_attrs: *const [field_names.len]std.lang.Type.Struct.FieldAttributes,
) type
Then fill in the values into those fields using @field().
Right, I see, let me do a quick search
From my search, compile time evaluation can not produce types, only values. So you could:
Yeah, this is what I found also. That's why I was so surprised when I saw the demo in the video. Niclas probably applied some tricks, and I want to learn those.
I checked Niclas' roc-pg too, he uses a regular type variable row. The compiler fills it in from how the result is used like "${planet.name} (ID ${I32.to_str(planet.id)}) or from an annotation. So the type is set before comptime eval.
Thanks @Anton.
This community is so fast, I can't keep up :joy: @Anton's explanation is of course on point!
Roc will figure out the types one way or another: either based on how you use the values, or through explicit type annotations.
It helps to think like this: first Roc does its type-checking, so that it knows all of the types in your program, then it does the compile-time string parsing, and can validate that against your types.
As @Anton mentioned, you could therefore generate code to "materialize" the types of whatever string you're parsing. I'm opting not to do that, because I don't want extra steps. I want it all to "just work" right when you've typed out your SQL query :)
Regarding type annotations: I usually work without types to start and then add type annotations when I want to cement what I've written. In this example I would probably extract a get_planets function and annotate that, so that I have said "I expect this function to return exactly this type of record" as close to the outside world as possible, just like you'd want with an external API or such. That way you get flexibility while experimenting, and then you tighten it up before deploying. This probably isn't news to Roc fans, but perhaps someone new to the community reads this :)
When the types are figured out from later usage, the error messages are usually not that great. How do you manage to get these very clear and useful errors?
The errors you see in the video are mostly hand-written by me in the parsing of the string. That's how roc-pg can do "did you mean" based on the SQL schema.
Think of it like this: Roc finishes the type-checking happily and reports no errors. Then it does the comp-time string parsing and I get to decide what errors that can return. In my case I want friendly error messages that help you figure out your SQL issue, which aren't about Roc types at all, really. For the SQL syntax errors I generally just say what Postgres would, to help with familiarity.
So sky's the limit in how good your error messages could be!
Niclas Ahden schrieb:
This community is so fast, I can't keep up :joy: Anton's explanation is of course on point!
... yes, I know — in the time it took you to ponder your answer, @Luke Boswell has already published three new platforms. :laughing:
The errors you see in the video are mostly hand-written by me in the parsing of the string.
Source code here for the interested reader.
Last updated: Nov 04 2026 at 18:54 UTC