digitalmars.D.announce - implicit-context v0.0.1
- Guillaume Piolat (25/25) Sep 28 2023 Hi,
- Imperatorn (4/10) Sep 29 2023 Interesting, what are the benefits of using this instead of
- Guillaume Piolat (19/21) Sep 29 2023 Thinking about this, it's more vs TLS variable. __gshared would
- Imperatorn (8/29) Sep 29 2023 I understand, it's more like if you mix optional parameters and
- Guillaume Piolat (25/29) Sep 29 2023 Think of it like envvars for threads. When you launch a process,
- drug007 (3/44) Sep 29 2023 But later I understand that the removing of gl has also disadvantages.
- Imperatorn (3/14) Sep 29 2023 Sounds a bit like dependency injection but for state
- Guillaume Piolat (3/4) Sep 30 2023 Possibly, I'm not familiar with dependency injection.
- evilrat (17/22) Sep 30 2023 Dependency injection is a principle of making your
- Imperatorn (5/10) Sep 30 2023 When you want to register a bunch of objects and then just use
- Max Samukha (3/4) Sep 30 2023 You can use it to troll Jonathan Blow.
- Guillaume Piolat (9/11) Oct 03 2023 OT: Apart from being marketed more like a game (streaming videos,
- jmh530 (4/9) Oct 10 2023 Do you have an example of how this would be used in practice with
- Antonio (101/113) Oct 05 2023 Context is dynamically generated/destroyed. I developed this
- Imperatorn (4/11) Oct 06 2023 Oh, I remember now.
- MrSmith33 (3/4) Oct 02 2023 I wonder if `with` statement is helpful here to reduce verbosity
- Guillaume Piolat (13/17) Oct 03 2023 Do you mean with:
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]. So I went ahead and implement a proof-of-concept library to have scope globals in D with a TLS-based stack of environments. I went ahead and implement a minimal logger, allocator, and "user pointer" API on top of that context system. It has a worse API and usability than a language solution but basically I think there is no big blocker, if TLS and C runtime are available to you. DUB: https://code.dlang.org/packages/implicit-context `implicit-context` is currently limited and suggestions/requests/forks/destroying are much welcome. As I see it, without compiler support the pluses are: - no hidden "context" parameter in function calls, no new ABI - contextual parameters are a bit like "scoped globals", they will not change that often. and the minuses are: - manual push/pop - wordy, opDispatch getter doesn't seem possible? [1] https://odin-lang.org/docs/overview/#implicit-context-system [2] https://medium.com/ christoffer_99666/a-little-context-d06dfdec79a3
Sep 28 2023
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?
Sep 29 2023
On Friday, 29 September 2023 at 08:33:56 UTC, Imperatorn wrote:Interesting, what are the benefits of using this instead of global variables?Thinking about this, it's more vs TLS variable. __gshared would require synchronization. Changing the theAllocator (a TLS variable) in std.experimental.allocator looks like this: auto save = theAllocator; theAllocator = myAllocator; // do stuff with custom allocator theAllocator = save; Changing the allocator in implicit-context looks like this context.push; context.allocator = myAlloc; // do stuff with custom allocator context.pop; so now that I think about it I'm not sure if there is an substantial advantage over simply having TLS variables. I had the goal of allowing .alloca on that secondary stack. If there is many context variables, push and pop will be a bit faster to write than all the temporaries.
Sep 29 2023
On Friday, 29 September 2023 at 11:00:05 UTC, Guillaume Piolat wrote:On Friday, 29 September 2023 at 08:33:56 UTC, Imperatorn wrote:I understand, it's more like if you mix optional parameters and dependency injection? I think for this to be truly valuable, it would require being part of the language. I admit I haven't really thought about implicit parameters before your post, so I might be missing something.Interesting, what are the benefits of using this instead of global variables?Thinking about this, it's more vs TLS variable. __gshared would require synchronization. Changing the theAllocator (a TLS variable) in std.experimental.allocator looks like this: auto save = theAllocator; theAllocator = myAllocator; // do stuff with custom allocator theAllocator = save; Changing the allocator in implicit-context looks like this context.push; context.allocator = myAlloc; // do stuff with custom allocator context.pop; so now that I think about it I'm not sure if there is an substantial advantage over simply having TLS variables. I had the goal of allowing .alloca on that secondary stack. If there is many context variables, push and pop will be a bit faster to write than all the temporaries.
Sep 29 2023
On Friday, 29 September 2023 at 15:00:33 UTC, Imperatorn wrote:I think for this to be truly valuable, it would require being part of the language.Only if proven on DUB.I admit I haven't really thought about implicit parameters before your post, so I might be missing something.Think of it like envvars for threads. When you launch a process, the launcher knows to copy the environment variables. With scattered TLS variables, no new thread can get a copy of all the "context" it may have. But with a centralized place for context, you will be able to do that (not implemented yet), which kinda improves encapsulation. **Example 1: Context variables are scoped** In a UI library, every new widget typically get a "UI context" (that, or factory functions only) polluting all the constructors there is. Solution: Just append "uiContext" variable in the context => less parameters. But it could be a TLS variable, right? Yes indeed, but then you may want to scope that, to remove the global from being accessed outside a particular scope. Globals can be accessed at any time, which doesn't improve a public API. **Example 2** When my "gfm" package was forked to "gfm7", the first thing that was done is: - remove all the _gl members that would only there to confirm a particular shared library was used => it is part of the context - and removed all logger interface passing => also ugly and kinda never changes
Sep 29 2023
On 29.09.2023 18:30, Guillaume Piolat wrote:On Friday, 29 September 2023 at 15:00:33 UTC, Imperatorn wrote:But later I understand that the removing of gl has also disadvantages. In general implicit context is really the great ideaI think for this to be truly valuable, it would require being part of the language.Only if proven on DUB.I admit I haven't really thought about implicit parameters before your post, so I might be missing something.Think of it like envvars for threads. When you launch a process, the launcher knows to copy the environment variables. With scattered TLS variables, no new thread can get a copy of all the "context" it may have. But with a centralized place for context, you will be able to do that (not implemented yet), which kinda improves encapsulation. **Example 1: Context variables are scoped** In a UI library, every new widget typically get a "UI context" (that, or factory functions only) polluting all the constructors there is. Solution: Just append "uiContext" variable in the context => less parameters. But it could be a TLS variable, right? Yes indeed, but then you may want to scope that, to remove the global from being accessed outside a particular scope. Globals can be accessed at any time, which doesn't improve a public API. **Example 2** When my "gfm" package was forked to "gfm7", the first thing that was done is: - remove all the _gl members that would only there to confirm a particular shared library was used => it is part of the context - and removed all logger interface passing => also ugly and kinda never changes
Sep 29 2023
On Friday, 29 September 2023 at 15:30:30 UTC, Guillaume Piolat wrote:On Friday, 29 September 2023 at 15:00:33 UTC, Imperatorn wrote:Sounds a bit like dependency injection but for state[...]Only if proven on DUB.[...]Think of it like envvars for threads. When you launch a process, the launcher knows to copy the environment variables. With scattered TLS variables, no new thread can get a copy of all the "context" it may have. But with a centralized place for context, you will be able to do that (not implemented yet), which kinda improves encapsulation. [...]
Sep 29 2023
On Friday, 29 September 2023 at 16:56:47 UTC, Imperatorn wrote:Sounds a bit like dependency injection but for statePossibly, I'm not familiar with dependency injection. When is it useful?
Sep 30 2023
On Saturday, 30 September 2023 at 12:40:29 UTC, Guillaume Piolat wrote:On Friday, 29 September 2023 at 16:56:47 UTC, Imperatorn wrote:Dependency injection is a principle of making your classes/functions self-contained and isolated, it means that when your code might need to create a resource (such as open a file to write data) it is instead up to the caller to provide that resource, but your code never does that by itself because a library can't possibly know the environment and restrictions of the target system/machine. Simply put, you can't possibly know how to open a file in that system, you can't possibly know what allocator is used in the caller environment (think about very low-level or bare metal program), and so on, so instead caller must provide everything that your function/method/class might need to do the work. In the most complex situations where the entire program graph is about to be created in the main function there is so called DI containers that configures all this stuff in one central place.Sounds a bit like dependency injection but for statePossibly, I'm not familiar with dependency injection. When is it useful?
Sep 30 2023
On Saturday, 30 September 2023 at 12:40:29 UTC, Guillaume Piolat wrote:On Friday, 29 September 2023 at 16:56:47 UTC, Imperatorn wrote:When you want to register a bunch of objects and then just use it from various places, you just state in your ctor that you want to use it and it will be provided by the framework.Sounds a bit like dependency injection but for statePossibly, I'm not familiar with dependency injection. When is it useful?
Sep 30 2023
On Saturday, 30 September 2023 at 12:40:29 UTC, Guillaume Piolat wrote:When is it useful?You can use it to troll Jonathan Blow.
Sep 30 2023
On Saturday, 30 September 2023 at 15:02:16 UTC, Max Samukha wrote:OT: Apart from being marketed more like a game (streaming videos, and similarly "finished" at launch?) I was striked that Jai has already many... perlisms in the syntax. As for the other features that makes me pause, it's SoA. If you have SoA types you might also need SoA slices, for example in Odin: https://odin-lang.org/docs/overview/#soa-data-types Is this composable?When is it useful?You can use it to troll Jonathan Blow.
Oct 03 2023
On Friday, 29 September 2023 at 11:00:05 UTC, Guillaume Piolat wrote:On Friday, 29 September 2023 at 08:33:56 UTC, Imperatorn wrote:Do you have an example of how this would be used in practice with allocators?[...]Thinking about this, it's more vs TLS variable. __gshared would require synchronization. [...]
Oct 10 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:Context is dynamically generated/destroyed. I developed this 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 regardsHi, 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?
Oct 05 2023
On Thursday, 5 October 2023 at 22:38:35 UTC, Antonio wrote:On Friday, 29 September 2023 at 08:33:56 UTC, Imperatorn wrote:Oh, I remember now. Long time since I heard anyone speak about AOP. But I think it was a valid idea.[...]Context is dynamically generated/destroyed. I developed this years ago)... I found out later something similar with AOP (Aspects Oriented Programming) when working with Spring in java [...]
Oct 06 2023
On Thursday, 28 September 2023 at 23:28:02 UTC, Guillaume Piolat wrote:- manual push/popI wonder if `with` statement is helpful here to reduce verbosity
Oct 02 2023
On Monday, 2 October 2023 at 19:04:19 UTC, MrSmith33 wrote:On Thursday, 28 September 2023 at 23:28:02 UTC, Guillaume Piolat wrote:Do you mean with: with(scopedContext()) { set!int("myVar", 5); } ScopedContext scopedContext() { /* blah */ } struct ScopedContext { ~this() { context.pop; } } Because at a point there was such a RAII context wrapper. I didn't realize, `with` extends its lifetime to the scope?- manual push/popI wonder if `with` statement is helpful here to reduce verbosity
Oct 03 2023