[m-users.] Ignoring !IO in an FFI binding, aceptable or hideously bad?

Sean Charles (emacstheviking) objitsu at gmail.com
Sun Feb 16 22:38:48 AEDT 2025


Hello,

I have a not insignificant binding to Raylib now, and during a recent bit of experimental coding, I decided on trying a different way to manage assets, in this case fonts.

:- import_module raylib.
:- type font_state
    --->    font_state(
                default :: rfont,
                main    :: rfont
            ).
:- mutable(fstate, maybe(font_state), no, ground, [untrailed]).

I am forced to initialise to `no` because until the Raylib library is initialised, loading fonts doesn't work, however, I have experimented with a second version of load_font in my binding that does not use !IO:

   1162
   1163 :- pragma foreign_proc(
   1164     "C", load_font2(FontName::in, Out::out),
   1165     [ promise_pure, will_not_call_mercury, will_not_throw_exception
   1166     , will_not_modify_trail, thread_safe, does_not_affect_liveness
   1167     ],
   1168     "
   1169         void* p = MR_GC_malloc_uncollectable(sizeof(Font));
   1170         Out = p;
   1171         Font font = LoadFont(FontName);
   1172         memcpy(p, &font, sizeof(Font));
   1173     ").

They both work, and it means that I can now load fonts by adding an initialise call to the module and then loading fonts without having an !IO context. I think I might change the mutable to be a `univ` on the grounds that after module initialisation, I know it will contain a fully populated `font_state`. I envisaged the new call looking something like this:

:- mutable(fstate, font_state,
       font_state(
           load_default_font,
           load_a_font("path/to/main/font.ttf")
       ),
       ground,
       [untrailed]
).

...but I would have to write a function shim as well

:- func load_a_font(string::in) = (rfont::out) is det.
load_a_font(Path) = F :-
    load_font(Path, F).  % No !IO context!


Sorry for rambling thus far, but my question is this: if I remove "!IO" from all of my binding to Raylib, what price might I pay in my code? The Raylib environment is hidden inside the library, any allocations it does are not known to Mercury, so what possible problems might I face if Mercury sees the same bunch of `det` calls but they don't need IO state in and out? Is that "lying" to Mercury in such a way as it could cause subtle issues, I am writing an IDE so it's a non-trivial project.

Or should I only remove IO from the asset loading functions?

I am just looking for ideas / advice etc on how to proceed such that I don;t inadvertently cause myself issues by lying to the compiler.

Thanks,
Sean.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.mercurylang.org/archives/users/attachments/20250216/30858000/attachment.html>


More information about the users mailing list