ats

How should strings from libraries hostile to BoehmGC be used?


While going through some exercises that used libedit, to quickly get something working, I came up with the following:

readline.sats:

#include "share/atspre_staload.hats"

%{#
#include <editline/readline.h>
#include <editline/history.h>
%}

fun readline: string -> Strptr0 = "mac#"
fun add_history: (!Strptr1) -> void = "mac#"
fun free_readline: Strptr0 -> void = "mac#free"

As used:

fun input_loop(): void =
  let
    val line = readline("input: ")
  in
    if strptr_isnot_null(line) then (
      add_history(line);
      println!("you entered: ", line);
      free_readline(line);
      input_loop()
    ) else free_readline(line)
  end

I have some problems with this solution:

  1. after determining that the strptr is NULL, I still have to pass it to free() as the linear value must be terminated. Is there a nicer way to handle this? Would it be possible to replace the strptr_isnot_null with a function that terminates NULL but not any other strptr ?

  2. Strptr1 is 99% what I want in this case. That's a very convenient type. However, strptr_free() is a valid terminator for Strptr1 -- but not for these values, because the editline library is doing its own allocation, outside of the GC. What's a nice way to handle this? Maybe, create a new type and reimplement everything needed for it. Maybe, create a new type but mostly use a zero-cost castfn to strptr ... which the caller might still mess up with, and try to use with strptr_free().


Solution

  • Regarding 1:

    I don't see an easy way to avoid calling 'free' on a null string. ATS2 is very explicit as of now. My hope is that more meta-programming support can be built into ATS3.

    Maybe you could try to define a macro:

    macdef if_line(x, _then) = if strptr_isnot_null(,(x)) then ,(_then) else freeline(,(x))

    Regarding 2:

    I would introduce an abstract type:

    absvtype readline(l:addr) = ptr

    fun readline(prompt: string): [l:addr] readline(l)