carp-lang / carp-lang/Carp

Add resource handling macro(s)

Open
#559 13 comments 1 reaction 1 assignee Claimed by @sdilts View on GitHub
core-libs nice-to-have
Dominant language
Haskell
Stars
6k
Forks
187
Avg merge
8d 22h
Merged PRs (30d)
4

Description

It is a common pattern to acquire a resource, use it, then release it all within the same function/context. Quite a few languages have built-in patterns to reduce programmer overhead in such situations, but I couldn't find an equivalent in Carp. Examples from other languages include Common Lisp's `with-open-file` macro and Python's `with` statement. You can also achieve the same effect in C++ using destructors. Does Carp already have something like this? Would a generic interface for this pattern be something that falls in line with the language philosophy?

## The `with-resource` Macro

I propose that something similar to Python's `with` statement is added to Carp. You can read about Python's version here: https://docs.python.org/3/reference/datamodel.html#context-managers. Essentially, each resource that can be managed has an `__enter__` and `__exit__` function that are called at the beginning and end of the managed program block. In Carp, the same thing could be achieved using a macro, which I will call `with-resource`. Syntacitly, it would look something like this:
``` clojure
(with-resource [ ]

)
```
`` is executed when the resource is successfully acquired, and `` is executed when the resource is not acquired. `` and `` is a let binding. Like `if`, the `error-form` is required.

## Examples
Here is an example of its usage:
``` clojure
(with-resource [file (fopen "example.txt")]
(println &(str (IO.fgetc file))
(println "The file \"example.txt\" could not be opened"))
```
Like `if`, `do` is required when you want to execute multiple statements:
``` clojure
(with-resource [file (fopen "example.txt")]
(do
(println "The first character of thie file is:")
(println&(str (IO.fgetc file))))
(println "File not found"))
```
Also, `with-resource` returns the result of the last executed form just like any other form in Carp:
``` clojure
(do
(print "The first character of the file is: ")
(let [first-char (with-resource [file (IO.fopen "example.txt" "r")]
(str (IO.fgetc file))
(str ""))]
(println &first-char)))
```

## Interface
Any type can use this macro if the two functions `acquire-resource` and `release-resource` are defined on it:
``` clojure
;; Attempts to acquire the resource, returns true if sucessful, false otherwise
(definterface acquire-resource (λ [resource] Bool))
;; releases the resource
(definterface release-resource (λ [resource] ()))
```

As an example, here is how it could be implemented with the `IO.FILE`
type so that the above examples will compile:
``` clojure
(defn acquire-resource [file]
(not (null? (the (Ptr IO.FILE) file))))

(defn release-resource [file]
(IO.fclose file))
```
One minor hang-up is that `acquire-resource` won't always directly obtain the resource, like in the case of files. However, it will always check if the resource is actually available.

## Implementation
For completeness, here is a potential implementation of the macro:

``` clojure
(defmacro with-resource [lambda-list :rest body]
(if (< (length body) 2)
(macro-error "You must provide a success-form and error-form to with-resource")
(if (> (length body) 2)
(macro-error "Only a success-form and error-form should be present in with-resource")
(let [var-name (car lambda-list)
init-form (cadr lambda-list)
tmp-result (gensym)]
(list 'let (array var-name init-form)
(list 'if (list 'acquire-resource var-name)
(list 'let
(array tmp-result (car body))
(list 'do
(list 'release-resource var-name)
tmp-result))
(cadr body)))))))
```

Is there anything I am missing, or are there needed improvements to the interface? At the very least, some type of resource handing macros should be added to the standard library, and I would much prefer a generic interface to a series of macros that only work on one type.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.