How to export a deduced template type to the enclosing scope?

Ali Çehreli via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Tue Sep 23 16:12:00 PDT 2014


On 09/23/2014 03:28 PM, monarch_dodra wrote:
> On Tuesday, 23 September 2014 at 22:09:11 UTC, Ali Çehreli wrote:
>> So near and yet so far... :)
>>
>> Ali
>
> You explain "how" you want to achieve your goal, but I'm not sure I
> understand "what" the goal is. Perhaps if you explain that in more
> detail, I'd have a better understanding of the problem.
>
> I've never used core.thread.Fibre, so I don't know what "yield(42);"
> would do, so apologies if the question is stupid.

Sorry. You are right. :) I almost deleted all of the specifics to avoid 
any concrete example as I think the question is still valid.

Very briefly, fibers provide faster concurrency by executing both the 
owner and the fiber on the same thread. They are D's coroutines, without 
the much-missed 'yield' keyword. There is some language (runtime?) 
support for fibers though as the 'yield()' and 'call()' calls do involve 
context switching behind the scenes (much faster than thread context 
switching).

I am not experienced with them either but from the examples on the 
core.thread documentation it looks like a fiber cannot yield a value in 
D; it must cause side-effects first, and then yield(), so that the owner 
can observe the side-effect.

Fibers give the convenience of iteration a.la. opCall(), and InputRange 
interface comes naturally. (Example below.)

I played with the idea of enabling the yield(42) syntax from the fiber 
code just because it does not involve side-effects and is more 
intuitive. There are blog posts, thread discussions, and enhancement 
reports about the same.

So, my goal was the above: The client would make a yield(42) call, the 
data would be saved in a local variable automatically (the type of which 
is the question at hand), and then the result would be provided on an 
InputRange interface.

Additionally, although I am not aware of other examples of it, again as 
an experiment, I tried to see whether injecting user code by a template 
mixin would make any sense. Since template mixins are expanded where 
they are mixed-in, then I thought perhaps I could deduce a type from 
inside that code. I failed, you will succeed. :o)

Here is a working example with the minor inconvenience of having to 
specify ResultT from the outside, which seems redundant. The goal is to 
remove the need for that parameter.

Note that I am aware of the inheritance-based kind of fibers and the 
following may not make sense at all. I was just experimenting.

import std.stdio;
import core.thread;
import std.algorithm;

/* This is what the user provides. We will mix this in. */
mixin template Foo()
{
     void run()
     {
         /* Note how naturally the elements are produced in imperative
          * style: */
         foreach (i; 0 .. 10) {
             yield(i);    /* The element type is obviously 'int'. */
         }
     }
}

/* This is an InputRange that exposes the yielded values as
  * elements. PROBLEM: Admittedly minor, ResultT seems redundant. */

class FiberRange(alias Func, ResultT)
{
     mixin Func!();     // User code mixed-in
     ResultT result;    // The front element
     Fiber fiber;       // The fiber that does the work

     /* Supports the more natural yield(42) syntax. */
     final void yield(T : ResultT)(T result)
     {
         this.result = result;
         Fiber.yield;
     }

public:

     this()
     {
         /* He fiber is started by the user's 'run' function. */
         this.fiber = new Fiber(&run);
         this.fiber.call();
     }

     /* Trivial InputRange interface: */

     final
     @property
     bool empty() const
     {
         return fiber.state == Fiber.State.TERM;
     }

     final
     @property
     ResultT front() const
     {
         return result;
     }

     final
     void popFront()
     {
         this.fiber.call;
     }
}

/* Convenience function: */
auto fiberRange(alias Func, ResultT)()
{
     return new FiberRange!(Func, ResultT);
}

void main()
{
     /* Works like a charm! :p */
     auto result = fiberRange!(Foo, int)
                   .filter!(a => a % 2);

     assert(result.equal([1, 3, 5, 7, 9]));
}

Can we get rid of the ResultT template parameter? Tricks with variadic 
templates, the with statement, etc. come to mind but I could not manage it.

Ali



More information about the Digitalmars-d-learn mailing list