Stream: beginners

Topic: ✔ Builtin types? for Module & Package types?


view this post on Zulip Scott Campbell (Sep 14 2026 at 05:34):

Edit TLDR: Nope, Module & Package info types do not exist.

So... I'm building a code generator, that takes Godot Api information produces Roc code as the output.

import Godot

GodotToRocMapper := {}.{
    to_roc : Godot.ExtensionApi -> Module
    to_roc = |bar| {
        bar
    }

    roc_module_to_str : Module -> Str
    roc_module_to_str = |module| {
        Str.inspect(module)
    }

    to_roc_str : Godot.ExtensionApi -> Str
    to_roc_str = |geapi| {
        roc_module_to_roc_str(geapi)
    }
}
── ✗ undeclared type ───────── GodotToRocMapper.roc:6:36

The type Module is not declared in this scope.

to_roc : Godot.ExtensionApi -> Module
                               ^^^^^^

What's the most high level ergonomic builtin that i can use here?

I could define a custom Module type... but I would prefer not to reinvent the wheel, and use the builtin types, if such a type exists.

CustomRocModule : {
    name: Str,
    imports: List(Str),
    type_defs: Dict(Str, CustomRocType),
    expressions: Dict(Str, CustomRocExpression)
}

view this post on Zulip Luke Boswell (Sep 14 2026 at 05:45):

I feel like you write the Roc code as a template using a multiline string and then fill in the gaps by generating that from the godot API.

I'm not sure what you mean by a builtin type here? This is putting out a Utf-8 string right, so Str would be all you need.

view this post on Zulip Luke Boswell (Sep 14 2026 at 05:45):

Do you have an example of the godot API and what the generated roc code might look like?

view this post on Zulip Luke Boswell (Sep 14 2026 at 05:50):

Oh I think I see what you are doing ... if I'm right you have Godot godot --dump-extension-api to dump the actual API for the specific engine you have, and then you want to interface with that?

view this post on Zulip Scott Campbell (Sep 14 2026 at 05:50):

I asked grok...

I’ll check the current Roc language and standard library for any built-in high-level type that represents a module or AST.There is no high-level builtin Module (or equivalent AST / module-representation type) available to user code.Roc’s builtins are limited to the usual core types (Str, List, Dict, Set, Result, Bool, numeric types, etc.). Anything that looks like a “module” or full AST lives only inside the compiler (the internal AST, ModuleEnv, CIR, etc.). Those are not exposed as a public, importable type you can use in ordinary Roc programs.

Most ergonomic practical options (ordered from highest-level to lowest)

  1. Just emit a Str (recommended for most generators)
    Keep the intermediate representation as simple records / tags of your own, then turn the whole thing into source text at the end:
    • Define a small custom intermediate type (exactly what you already sketched)
      Your CustomRocModule (or a slightly richer version of it) is the right level of abstraction. You only need the parts that matter for your generation:

I guess, perhaps just using a string template is the right approach.

I was hoping that i could do something like

module : Module
module = godot_api.to_roc()

str = module.to_str()

inspect_str = Str.inspect(module)

view this post on Zulip Luke Boswell (Sep 14 2026 at 05:51):

My intuition is that you would want to handle the godot bindings side with Zig not Roc

view this post on Zulip Scott Campbell (Sep 14 2026 at 05:52):

Luke Boswell said:

Oh I think I see what you are doing ... if I'm right you have Godot godot --dump-extension-api to dump the actual API for the specific engine you have, and then you want to interface with that?

yup... I'll need zig host bindings, but then I'll also want a matching Roc api.

The plan is to generate everything, except for some glue functions, and godot version independent host implementation.

view this post on Zulip Luke Boswell (Sep 14 2026 at 05:53):

Do you want to support any godot engine version? or is the platform going to be pinned to one version?

view this post on Zulip Scott Campbell (Sep 14 2026 at 05:54):

For now, i've got it pinned to a specific version.

And then add additional versions, as the generator matures.

view this post on Zulip Luke Boswell (Sep 14 2026 at 05:55):

I feel like the Roc platform <-> host (effects) side would be pretty stable and you shouldn't need to codegen that.

view this post on Zulip Luke Boswell (Sep 14 2026 at 05:56):

Would you mind sharing a extension_api.json in a gist or something, I'm interested to see the shape of that

view this post on Zulip Luke Boswell (Sep 14 2026 at 05:56):

I don't have godot installed

view this post on Zulip Scott Campbell (Sep 14 2026 at 05:56):

Let's say...
godot 4.6.2 & 4.7.1

Could be a new feature... "CharacterBody3D class now has 2 more methods, and we added 4D class"

view this post on Zulip Scott Campbell (Sep 14 2026 at 05:58):

Luke Boswell said:

Would you mind sharing a extension_api.json in a gist or something, I'm interested to see the shape of that

350,000 file.

https://raw.githubusercontent.com/scottc/godot-roc/refs/heads/master/scripts/godot-api-client-generator/extension_api.json

view this post on Zulip Luke Boswell (Sep 14 2026 at 05:59):

I'm not saying you can't generate it... but if you want an idiomatic Roc API you might not want to match the hosted effects 1-1 in Roc, you might want the host to handle some things and provide a nicer experience for app authors.

view this post on Zulip Scott Campbell (Sep 14 2026 at 06:05):

Inside the godot editor, a comparable gdscript doesn't reference a pinned version...
class_name MyPlayerCharacter extends CharacterBody3D

I'd assume it'll just throw errors at runtime, for api missmatches.

So long term, would want to mimic the exact same thing, but perhaps we can give compile time errors/warnings for typemismatches.

Was thinking along the lines of:

Godot.CharacterBody3D # <-- this is what most app devs will use

Godot_4_6_2.CharacterBody3D # <-- if you want an explicit pinned version.

Godot_4_7_1.CharacterBody3D
Godot_4_7_1.NewCoolNode_v2

view this post on Zulip Luke Boswell (Sep 14 2026 at 06:08):

Is your goal here to learn and build something from scratch, or would you like to get to a working demo thing as fast as possible and happy to skip all the bindings part?

view this post on Zulip Luke Boswell (Sep 14 2026 at 06:08):

Another option you could consider is sitting on top of something mature like https://github.com/godot-rust/gdext (using their core crate and not the macros or anything) and then the bindings are already handled for you

view this post on Zulip Luke Boswell (Sep 14 2026 at 06:09):

Then you could focus on the Roc app facing API and the host facing API sides of the platform

view this post on Zulip Scott Campbell (Sep 14 2026 at 06:11):

Both... I've already implemented bootstrapping, a working prototype, a minimal 3d platformer game...

Now I'm looking at API completeness.

Yeah, perhaps leaning on existing gdexts is the way to go... or at least in the short term, to get something closer to production ready faster.

I assume people are going to want to use it, and they're going to want it to be robust & mature as possible etc.

view this post on Zulip Luke Boswell (Sep 14 2026 at 06:13):

Does godot have a "stable" version everyone uses? could you pin to that and it'd be ok

view this post on Zulip Scott Campbell (Sep 14 2026 at 06:18):

The rust gdext, does something similar, they only support versioned releases, and not the nightly releases.
https://godot-rust.github.io/book/toolchain/compatibility.html#rust-api-stability

Can go as granular as desired.

Anyway, for now... I'm using a specific pinned version

nix develop
# roc:    Roc compiler version debug-no-git (pinned in flake.lock)
# zig:    0.16.0
# godot:  4.7.2.stable.nixpkgs.ed1daf0bf
# python: Python 3.14.7
# gh:     gh version 2.100.0 (nixpkgs)

view this post on Zulip Scott Campbell (Sep 14 2026 at 06:21):

and for now... i'll just use string templates as the code / type generator, don't need a fancy Module / Package typeinfo.

And someone already made one for zig:
https://github.com/godot-zig/godot-zig/tree/master/binding_generator

view this post on Zulip Notification Bot (Sep 14 2026 at 06:22):

Scott Campbell has marked this topic as resolved.

view this post on Zulip Luke Boswell (Sep 14 2026 at 06:25):

How is memory managed in a GDExtension?

Does the host call into roc once each frame?

view this post on Zulip Scott Campbell (Sep 14 2026 at 06:31):

The way i've implemented it... there are a few globals.

The roc stuff is initialised immediately as the first line of code in the extension when it's loaded, it's called once, and the I just keep it in memory as a global indefinately.
https://github.com/scottc/godot-roc-platform/blob/main/src/host.zig#L42

Next we give godot a "scene" init and deinit handlers...

And that's when I register classes from the roc app... still playing around with the optimal timings.

So tldr, nope, it doesn't init roc every frame... just the stuff that's needed for when it's needed... appropriate lifetimes.

We just call the _process game loop tick handler, and then from there we route it to each class type.

Simple single class example:
https://github.com/scottc/godot-roc-platform/blob/main/examples/hello_godot/main.roc

Multi class example:
https://github.com/scottc/godot-roc-platform/blob/main/examples/hello_godot_complex/main.roc

view this post on Zulip Luke Boswell (Sep 14 2026 at 06:35):

One trick I have been using is like a typed "host-owned" resource, basically a HostThing :: Box(U64).{ ... and then when I allocate that in the host I can keep track of the memory address, and when roc deallocates the Box calling roc_dealloc I can catch that and know to then release the resources the host is holding onto.

That might be helpful for making resources or opaque handles for things.

view this post on Zulip Luke Boswell (Sep 14 2026 at 06:35):

Then I can make the API such that you must have a live handle to call a particular effect because that is a method (or associated function) on that handle.

view this post on Zulip Luke Boswell (Sep 14 2026 at 06:36):

It looks like GDExtension is very OO shaped... so this might be helpful

view this post on Zulip Luke Boswell (Sep 14 2026 at 06:38):

https://github.com/lukewilliamboswell/roc-ray/blob/main/src/host_resource.zig

view this post on Zulip Scott Campbell (Sep 14 2026 at 06:38):

I'm new to manual memory management & related types, ownership and lifetimes etc.
I've always coded in high level garbage collected languages... typescript and such.

Thanks for the suggestions, I'll read into it and familiarise myself with the concepts & implementation details.

view this post on Zulip Luke Boswell (Sep 14 2026 at 06:44):

Lol, I went to fork your repo so I could try it out and I cant because it's a fork of my repo :smile:

view this post on Zulip Luke Boswell (Sep 14 2026 at 06:45):

It's fork inception

view this post on Zulip Scott Campbell (Sep 14 2026 at 06:49):

I can try unlink the fork... It's useful for pulling upstream changes, or sending pull requests.

The project is a bit awkward, because i made the project, and then dumped the platform template inside a subdirectory... so it's a git module that has independent git history.

https://github.com/scottc/godot-roc
https://github.com/scottc/godot-roc-platform

So I need to git add * && git commit -m 'foo' && git push twice, I probably need to flatten them at some point.

view this post on Zulip Luke Boswell (Sep 14 2026 at 06:51):

I was just wanting to try and setup a single handle as a demo for you

view this post on Zulip Luke Boswell (Sep 14 2026 at 06:51):

I'm actually here experimenting with some new hardware things and NixOS and your project is a convenient excuse to play with Roc at the same time :sweat_smile:

view this post on Zulip Scott Campbell (Sep 14 2026 at 07:35):

I was thinking about a nix package manager platform.

And then instead of nix expressions, one could use roc expressions instead.

Or just yeet the entire platform and start from scratch, and keep just the good parts.

flake.lock, 100% reproduceable as default, and now with amazing type inferance.

One only needs to bootstrap from bare metal... just a thin UEFI layer, as a bootloader... and 1 function call...

InstallUrl("hash", "remote-url"), or something like that.

ConvertFlakeToRocPlatform("path/flake.nix")

Start with fetching a linux kernel... or redox os microkernal.

And then can use the kernal syscalls, but won't have network device drivers yet...

So maybe need to start with minimal kernal + network driver, as the platform.

view this post on Zulip Luke Boswell (Sep 14 2026 at 08:17):

Have you seen @blu's Kai project?

https://roc.zulipchat.com/#narrow/channel/304902-show-and-tell/topic/Prototyping.20a.20generic.20frontend.20for.20determinate.20computing/near/621116056

view this post on Zulip Scott Campbell (Sep 14 2026 at 08:38):

Nope, ah nice... someone's already working on it.

view this post on Zulip Scott Campbell (Sep 14 2026 at 08:42):

This is delightful...

Roc gen:
roc check ./main.roc && cat extension_api.json | roc run ./main.roc > generated.roc && roc check ./generated.roc

Zig gen:
roc check ./main.roc && cat extension_api.json | roc run ./main.roc > generated.zig && zig test ./generated.zig

  1. check for errors (instant)
  2. run, if no errors (2s compile time, ~2s to parse json)
  3. check for errors in generated code.

And then an editor open with 3 panes:

  1. type defs
  2. the mapping implementation
  3. the generated output

Screenshot_20260914_164100.png

view this post on Zulip Scott Campbell (Sep 14 2026 at 08:45):

I should probably start with the zig host first.
I doubt the roc api, is going to need these details, maybe some...

But will need to see what zig host will need to expose?

view this post on Zulip Scott Campbell (Sep 14 2026 at 10:05):

Yeah just doing... GodotThingy -> Str

is simple enough, and probably better for performance.

No intermediate mapping, which is another memory allocation.

So probably twice as fast as GodotThingy -> RocThingy -> Str

Although not quite as composable... f1(f2())

Another layer of abstraction & indirection... is actually counter productive for readability... when trying to understand the entire process from start to finish it's better if it's just direct from A -> B then all concerns are immediately apparent, instead of having yet another mental model and map in back of mind.


Last updated: Sep 24 2026 at 15:59 UTC