In Haskell, when using the FFI to bind to a function that allocates,
is it appropriate to avoid using IO
when the foreign function allocates
for and constructs some value, and that value only depends on the function arguments?
Consider the following function:
/**
* The foo_create contract: if allocation succeeds, the
* return value points to a value that depends only on 'x'
* and 'name', otherwise a null pointer is returned.
*/
foo_t *foo_create(int x, const char *name);
Would it be appropriate to import this function in the following way?
newtype Foo = Foo (Ptr Foo)
foreign import unsafe "foo.h foo_create"
foo_create :: CInt -> CString -> Ptr Foo
This low-level binding function can then be wrapped to provide a nicer API:
makeFoo :: CInt -> CString -> Maybe Foo
makeFoo x s =
let
ptr = foo_create x s
in
if ptr == nullPtr
then Nothing
else Just (Foo ptr)
Although the allocation affects the real world, and whether or not it succeeds is also dependent on the real world, the type does model the
possible outcomes. Furthermore, even pure functions and data can cause
the Haskell runtime to allocate. So, is it reasonable
to avoid the IO
monad in situations such as these?
If foo_create
returns a value that only depends on the values of x
and name
, then yes it's fine return value outside IO
. As you say, creating new values inside Haskell causes allocation, and they don't need to be in IO
because it's impossible to observe the particular memory addresses that get allocated outside of IO
, and also impossible to observe whether or not an allocation succeeds (i.e. whether the program is running out of memory) outside of IO
.
However, you say "whether or not it succeeds is also dependent on the real world". In that case, no, it is an effectful operation which should have a return type in IO
. A Haskell function with a type makeFoo :: CInt -> CString -> Maybe Foo
says that whether the Maybe Foo
is Nothing
or Just _
must depend only on the values of the CInt
and the CString
. This is every bit as important as that the value inside the Just
only depends on the arguments.
If it's possible to call a function with the same arguments and get different results depending on the state of the real world, then it isn't a function at all, and should be an IO
action.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With