githwxi / ATS-Postiats

ATS2: Unleashing the Potentials of Types and Templates
www.ats-lang.org
Other
352 stars 54 forks source link

relaxing ATSERRORnotenvless #269

Closed okeuday closed 3 years ago

okeuday commented 3 years ago

Isn't it possible to allow the creation of an ordinary non-closure function when the function is created using constants from the environment (i.e., when creating an ATS function in another ATS function)? When I attempt to do this, I get ATSERRORnotenvless in the C source code, though I am only passing an ATS function pointer value into the new function.

I have tried to do this within a function template, which doesn't improve the situation but appears to match what could work because it could be possible to create a unique non-closure function for each call to the function template (that is what I am attempting to do). Allowing this to work is helpful when considering the alternative, which requires manually creating a separate function for each call (resulting in more lines of source code).

I have been looking at this problem to simplify the $CLOUDI.subscribe function call in source code like https://github.com/CloudI/CloudI/blob/7e0adae04f75900acdef8db5c213a893c930b68d/src/tests/count/ats/v2/main.dats#L97 (to avoid the need to define the request function explicitly in that file). This situation really relates to using an ATS function pointer in C source code which may not be common (my goal was to do this without modifying the C source code, so I wasn't considering having a closure or an env parameter).

okeuday commented 3 years ago

I am effectively suggesting I should be able to make the $CLOUDI.subscribe function call (https://github.com/CloudI/CloudI/blob/7e0adae04f75900acdef8db5c213a893c930b68d/src/api/ats/v2/cloudi.dats#L1109-L1117) look like:

implement {s}
$CLOUDI.subscribe
    (api,
     suffix,
     f) = let
    val INSTANCE(@{api_c_ptr = (_, _ | api_c), ...}) = api
    fn
    f_wrapper
        (request_type: $CLOUDI.request_type,
         name_c: ptr,
         pattern_c: ptr,
         request_info_c: ptr,
         request_info_size_c: uint32,
         request_c: ptr,
         request_size_c: uint32,
         timeout_c: uint32,
         priority_c: int8,
         trans_id_c: ptr,
         pid_c: ptr,
         pid_size_c: uint32,
         state_c: ptr,
         f_api_c: ptr):<fun1>
        void =
        $CLOUDI.callback_attach<s>(f,
                                   request_type, name_c, pattern_c,
                                   request_info_c, request_info_size_c,
                                   request_c, request_size_c,
                                   timeout_c, priority_c, trans_id_c,
                                   pid_c, pid_size_c, state_c, f_api_c)
    val f_c: $CLOUDI.c_callback = f_wrapper
in
    result_value_unit(c_subscribe(api_c, string2ptr(suffix), f_c), api)
end

The source code above does compile because the function is a template but fails once it gets to the tests/count/ats/v2/main.dats file.

githwxi commented 3 years ago

Yes, you can create an envless function inside another function.

But f_wrapper cannot be an envless function because its body contains 'f', which is not an argument of f_wrapper itself.

githwxi commented 3 years ago

Here is a way to "fix" the issue.

HX-2021-06-13: The following code causes the "envless" problem you encountered:

fun apply ( f0 : int -> int): int = f0(0)

fun foo(x: int): int = let fun bar(y:int): int = x + y // bar is not envless! in apply(bar) end

############

HX-2021-06-13: The 'foo' can be turned into a template to circumvent the "envless" problem:

extern fun{} foo$x(): int

fun{} foo(): int = let // fun bar(y:int): int = foo$x<>() + y // in apply(bar) end

implement main0() = { // implement foo$x<>() = 50 // val foo50 = foo() val ( ) = println!("foo50 = ", foo50) }

okeuday commented 3 years ago

@githwxi Thank you for describing this solution. I changed the source code to using this approach and avoided the envless problem.