www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - The design of the hooks in std.experimental.checkedint

reply Jacob Carlborg <doob me.com> writes:
I've been looking a bit at the design of the hooks in 
std.experimental.checkedint. Due to all hooks being optional there's 
quite a few "static if" in the implementation of checkedint to check if 
a hook is implemented.

Wouldn't it be simpler if all hooks were required and a default 
implementation was provided instead? Currently the Abort hook is the 
default hook that defines a couple of hooks but not all of them. If a 
default hook is instead implements all hooks but is implemented as a 
template it can be mixed in into custom hooks that want to change the 
behavior. For example, I want a hook that only defines the default 
value. Today that would look like this:

struct DefaultValueHook
{
     static T defaultValue!(T)() { return 1; }
}

If all hooks were required to be implemented but a default 
implementations would be provided it would look like this instead:

mixin template DefaultHook()
{
     T defaultValue!(T)() { return T.init; }
     ... // the rest of the hooks
}

struct DefaultValueHook
{
static:

     mixin DefaultHook; // provides default implementation for all hooks

     T defaultValue!(T)() { return 1; } // "overrides" defaultValue 
defined in DefaultHook
}

One extra line of could is required, the mixin.

It should also be simpler if you want to customize an existing hook but 
just override one of the hooks. Example:

struct DefaultValueHook
{
     mixin Warn;

     Dst onBadCast(Dst, Src)(Src src) { assert(0); }
}

In the implementation of Checked it would know that a hook is always 
provided and quite a few of the existing "static if" could be removed.

Thoughts?

-- 
/Jacob Carlborg
Jun 03 2017
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 06/03/2017 11:59 AM, Jacob Carlborg wrote:
 I've been looking a bit at the design of the hooks in 
 std.experimental.checkedint. Due to all hooks being optional there's 
 quite a few "static if" in the implementation of checkedint to check if 
 a hook is implemented.
 
 Wouldn't it be simpler if all hooks were required and a default 
 implementation was provided instead?
That would be an interesting experiment. If there is a net reduction of lines of code, that would be quite nice. One question - current logic decides whether to call e.g. hookOpBinary vs. perform the default operation followed by onOverflow. How would that work if both hookOpBinary and onOverflow are defined?
 Currently the Abort hook is the 
 default hook that defines a couple of hooks but not all of them. If a 
 default hook is instead implements all hooks but is implemented as a 
 template it can be mixed in into custom hooks that want to change the 
 behavior. For example, I want a hook that only defines the default 
 value. Today that would look like this:
 
 struct DefaultValueHook
 {
      static T defaultValue!(T)() { return 1; }
 }
 
 If all hooks were required to be implemented but a default 
 implementations would be provided it would look like this instead:
 
 mixin template DefaultHook()
 {
      T defaultValue!(T)() { return T.init; }
      ... // the rest of the hooks
 }
 
 struct DefaultValueHook
 {
 static:
 
      mixin DefaultHook; // provides default implementation for all hooks
 
      T defaultValue!(T)() { return 1; } // "overrides" defaultValue 
 defined in DefaultHook
 }
 
 One extra line of could is required, the mixin.
I'm unclear whether this is a step in the right direction. Why have user code work more to provide less information to the framework? Let user code define what it can, and the framework takes care of the rest. A look at how std.experimental.allocator would work if all primitives were required would also be useful.
 It should also be simpler if you want to customize an existing hook but 
 just override one of the hooks. Example:
 
 struct DefaultValueHook
 {
      mixin Warn;
 
      Dst onBadCast(Dst, Src)(Src src) { assert(0); }
 }
 
 In the implementation of Checked it would know that a hook is always 
 provided and quite a few of the existing "static if" could be removed.
 
 Thoughts?
A look at an alternative design would definitely be interesting. Andrei
Jun 03 2017
parent reply Jacob Carlborg <doob me.com> writes:
On 2017-06-03 23:45, Andrei Alexandrescu wrote:

 One question - current logic decides whether to call e.g. hookOpBinary
 vs. perform the default operation followed by onOverflow. How would that
 work if both hookOpBinary and onOverflow are defined?
I'm not sure I fully understand without a code example but I would say that the default hook would implement hookOpBinary to perform the default operation and then call onOverflow.
 I'm unclear whether this is a step in the right direction. Why have user
 code work more to provide less information to the framework?
I don't see how it would provide less information to the framework.
 Let user code define what it can, and the framework takes care of the rest.
Well, the default hook is part of the framework.
 A look at how std.experimental.allocator would work if all primitives
 were required would also be useful.
Yes. I haven't looked that carefully on how DbI is used in the allocators yet.
 A look at an alternative design would definitely be interesting.
Note that it doesn't need to be an either or case. Some of hooks can be required while other are optional. This suggestion is perfect when the logic is: if there's a hook, call that, otherwise perform a default operation. It's less ideal when there are multiple conditional branches. -- /Jacob Carlborg
Jun 04 2017
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 06/04/2017 03:25 PM, Jacob Carlborg wrote:
 On 2017-06-03 23:45, Andrei Alexandrescu wrote:
 
 One question - current logic decides whether to call e.g. hookOpBinary
 vs. perform the default operation followed by onOverflow. How would that
 work if both hookOpBinary and onOverflow are defined?
I'm not sure I fully understand without a code example but I would say that the default hook would implement hookOpBinary to perform the default operation and then call onOverflow.
What would be the advantage of moving the default into a hook?
 I'm unclear whether this is a step in the right direction. Why have user
 code work more to provide less information to the framework?
I don't see how it would provide less information to the framework.
Hook function is defined: "I want to hook this entire operation." Hook function is not defined: "I am not interested in hooking this operation." If hook is always defined, the shell cannot identify what a particular hook has an interest in. Andrei
Jun 04 2017
parent reply Jacob Carlborg <doob me.com> writes:
On 2017-06-04 21:52, Andrei Alexandrescu wrote:

 What would be the advantage of moving the default into a hook?
The whole idea was to reduce the number of "static if" in the implementation.
 Hook function is defined: "I want to hook this entire operation."

 Hook function is not defined: "I am not interested in hooking this
 operation."

 If hook is always defined, the shell cannot identify what a particular
 hook has an interest in.
Right. -- /Jacob Carlborg
Jun 05 2017
parent reply Petar Kirov [ZombineDev] <petar.p.kirov gmail.com> writes:
On Monday, 5 June 2017 at 10:29:26 UTC, Jacob Carlborg wrote:
 Hook function is defined: "I want to hook this entire 
 operation."

 Hook function is not defined: "I am not interested in hooking 
 this
 operation."

 If hook is always defined, the shell cannot identify what a 
 particular
 hook has an interest in.
Right.
(As I'm sure you know) this can be solved by using UDAs. I.e. the members of the mixin template can be tagged with e.g. default indicating to the shell that the there was interest in overriding them. Of course static ifs will be needed again, but if we assume that in the common case the shell doesn't need to differentiate between user defined and default hooks, then this can be a net win in terms of LoC needed for the shell.
Jun 05 2017
parent Petar Kirov [ZombineDev] <petar.p.kirov gmail.com> writes:
On Monday, 5 June 2017 at 16:04:18 UTC, Petar Kirov [ZombineDev] 
wrote:
 On Monday, 5 June 2017 at 10:29:26 UTC, Jacob Carlborg wrote:
 Hook function is defined: "I want to hook this entire 
 operation."

 Hook function is not defined: "I am not interested in hooking 
 this
 operation."

 If hook is always defined, the shell cannot identify what a 
 particular
 hook has an interest in.
Right.
(As I'm sure you know) this can be solved by using UDAs. I.e. the members of the mixin template can be tagged with e.g. default indicating to the shell that the there was interest in overriding them. Of course static ifs will be needed again, but if we assume that in the common case the shell doesn't need to differentiate between user defined and default hooks, then this can be a net win in terms of LoC needed for the shell.
...indicating to the shell that the there was *no* interest in overriding them.
Jun 05 2017