implicit-context v0.0.1
Antonio
antoniocabreraperez at gmail.com
Thu Oct 5 22:38:35 UTC 2023
On Friday, 29 September 2023 at 08:33:56 UTC, Imperatorn wrote:
> On Thursday, 28 September 2023 at 23:28:02 UTC, Guillaume
> Piolat wrote:
>> Hi,
>>
>> Ever had a bit of feature-envy about Odin's "context" feature
>> [1]? It is something used to pass "contextual" parameters,
>> like a logger, an allocator, to callees. It is akin to Scala's
>> "implicit parameters", or Jai contexts [2].
>>
>> [...]
>
> Interesting, what are the benefits of using this instead of
> global variables?
Context is dynamically generated/destroyed. I developed this
Idea in 2009 with c#. We named this "functional context" (15
years ago)... I found out later something similar with AOP
(Aspects Oriented Programming) when working with Spring in java
Lets see an example
```d
long create(PersonDto person) =>
withTransaction( (auto cnx){
// Perform person creation stuff
long personId = cnx.execute(
"insert into people ... returning id",
[...]
).first!long("id");
return personId;
});
long create(CustomerDto customer) =>
withTransaction( (auto cnx){
long personId = create( customer.person );
long customerId = cnx.execute(
"insert into customers ... returning id",
[personId, ... ]
).first!long("id");
return customerId;
});
void main(){
withContext((){
CustomerDto customer = { code:"P001",
person:{name:"Peter", nif:"3442543211F"}};
long customerId = create( customer );
})
}
```
The "withTransaction" function, iternally, asks the context if
there is an opened transaction.
* If not found:
* It creates one and registers it into the context.
* calls the delegate
* commits the transaction and removes it from the context
* returns the delegate result.
* If an exception is thrown by the delegate, then the
transaction is rollbacked instead commited and the exception is
passed through to the caller.
* If found:
* Calls the delegate transparently and returns it's result
This use case of "implicit-context" works naturally in a "per
thread context".
Stackability is nice: (this example is not so real, but a "how
to" example):
```d
void createPersonAction() =>
withHttpResponse( res =>
withAuthentifiedUser( user =>
withHttpBody!Person( person =>
withLogger("createPersonAction", (logger) {
logger.info("Something to be logged");
auto id = withTransaction( cnx => cnx.execute(...) );
res.send(id) )
})))));
```
It shoud be more natural this way ...
```d
void createPersonAction() =>
with( auto res = implicitHttpResponse())
with( auto user = implicitAuthentifiedUser())
with( auto person = implicitHttpBody!Person())
with( auto logger = implicitLogger("createPersonAction") )
{
logger.info("Something to be logged");
with( auto cnx = implicitTransaction() )
{
auto id = cnx.execute(...);
res.send(id); // Bad place... there is an oppened
transaction here!!!
}
};
```
... but remember than we need to manage "exceptions" dependant
behaviours implicitly: **with(** is not an option for AOP.
As you can see, this is not an "Object oriented dependency
injection"... Each "withX" internally interacts with the context
to find or create the resource and, additionally, performs some
functional extra proccessing (before, after and exception).
i.e.: withHttpResponse:
* if res.send is called: this is the data to be serialized as a
result (status 202)
* if res.send is not called, then "404 not found" will be
generated when delegate ends.
* if an exception is raised by the delegate, it will be
transformed in an "standard" http error
As a ramarkable benefit: it is really simple to wrap with
mockups when testing
Problems?
* It is "runtime" generated/consumed without compilation time
verification (i.e.: you can call createPersonAction without an
HttpRequest in the context )... but this is a dependency
injection assumed problem.
* You are in risk to move to "implicit context" too many things
(remember that functions have parameters :-) )
It was only a possible use of "implicit context" :-)
Best regards
More information about the Digitalmars-d-announce
mailing list