MiniD 2 - Tentative Release Candidate 1

Jarrett Billingsley jarrett.billingsley at gmail.com
Wed Dec 10 05:52:01 PST 2008


On Wed, Dec 10, 2008 at 3:15 AM, davidl <davidl at 126.com> wrote:
> 在 Wed, 10 Dec 2008 13:52:18 +0800,Jarrett Billingsley <jarrett.billingsley at gmail.com> 写道:
>
> It's cool it could be used in that syntax. But I find nowhere of docs describing this feature?
> Do I miss something?

Yeah, you do.  I gave you a link in my last post.  See at the end?  Go
there and read all about it.

> Actually I tried something like:
> module test
> global v=coroutine function()
> {
> local opApply=coroutine function opApply(m)
> {
> local i=0
>  if (i<3) {
>  i++
>  yield(i)
> }
> return null
> }
> yield()
> }
> v()
> foreach(m;v)writefln(m)

No wonder it didn't work, it's completely the wrong way to do it.  ;)

> In the case I wrote, the coroutine foreach just conflicts with the coroutine foreach way you illustrate
> in your example. As I didn't have that information, I tried to perform my coroutine foreach in a standardized
> opApply way. That's somewhat confusing for people not having the knowledge of coroutine foreach.
> Intuitively, the foreach is presumably working in the way of calling instance's opApply func and passing the arg
> of reverse or not to it. While the truth is I'm wrong.

The thing is, it _does_ work by calling the coroutine's opApply.  It's
just that you don't define opApply yourself.  Think about it - you
don't put an opApply in an array to foreach over it.  How can you
opApply over it?  Because array.opApply is defined by the standard
library.  The same thing for coroutines.

http://www.dsource.org/projects/minid/wiki/StdLib2/ThreadLib#Threadmetamethods

That shows where opApply is defined for thread (coroutine) objects.
Which again links to the page I gave you in my last post.

> The inconsistency seems to be a tradeoff. Yes, without opApply it looks clean. While it's inconsistent. That means
> developers who use minid need to learn more.

Again, it's not inconsistent, as coroutines _do_ have an opApply.

> The following case illustrates a easily misread code for me at least. In minid, I thought the parentheses
> were compulsive, cause the coroutinefunc.state won't work, but coroutinefunc.state(). So in such case, I guess the
> "count" refers to the function object, while the result shows it just endless loop and results stack overflow.
> Even that "count" actually a function call without the arg, the calling is performed. that's somehow misleading.
>
> function count(x)
> {
>    function opApply(m) = coroutine function opApply(m)
>    {
>        for(i: 1 .. x + 1)
>            yield(i) // yield values after that
>        return null
>    }
>    foreach(v;count)
>        writeln(v) // prints 1 through 4
> }
> count(4)
> object.Exception: Stack Overflow

No, it's just that you don't understand MiniD's iteration protocol.
I'm not going to explain it here because I've already explained it -
two or three times - in the documentation.  Long story short, 'count'
refers to the global function 'count' which you already defined.
Functions are first-class values, so "count" refers to the function
object itself, like doing "&count" in D.  Foreach loops are based on
an iterator function, and so when you do foreach(v; count) it thinks
"count" is your iterator function.  It calls it, which makes a
recursive call, which calls itself, and so on.

For information on the iteration protocol, read this:
http://www.dsource.org/projects/minid/wiki/LanguageSpec2/Statements#ForeachStatements

> MDCL stack overflowed out, maybe mdcl should do something to protect itself from stack overflowing.

Maybe it should ;)

> I'm trying to use coroutine foreach in a class.
>
> class count
> {
>    x =3
>    function opApply(m) = coroutine function opApply(m)
>    {
>        yield()
>        for(i: 1 .. m.x + 1)
>            yield(i)
>    }
>
> }
> global c= count()
>    foreach(v;c.opApply(c))
>        writeln(v) // prints 1 through 4
>
> it causes of runtime error: Error: opApply(7): Attempting to access field 'x' from a value of type 'null'

Well yeah.  Where is your coroutine getting the value 'm'?  It's not.
So it gets 'null'.

> The problem here is I misuseing the function definition.
> function opApply(m) = coroutine function opApply() works OK.

Yes, because of upvalues.  When you take the parameter off the
coroutine, 'm' then refers to the local variable in the outer
function.

> But that's inconsistent from a strong type user's view. Because the left part is function with 1 arg, right part
> is a coroutine function without any arg. That's pretty error-prone for newbies.

And coroutines and iterators are pretty advanced.  So I'm not so sure
I much mind what the newbies think ;)

Actually you're again doing it wrong.  You don't have to manually pass
the object to be iterated to opApply, it's already passed as 'this'.
The following:

class count
{
	x = 3

	function opApply()
	{
		local m = this

		return (coroutine function()
		{
			yield()

			for(i: 1 .. m.x + 1)
				yield(i)
		}).opApply() // hee hee
	}
}

global c = count()

foreach(v; c)
	writeln(v) // prints 1 through 4

Works without having to call c.opApply directly.

What I did in the method there is a trick - if you want one opApply to
actually iterate over another object, you can just return the values
from _its_ opApply.  I put 'this' in m so that the coroutine could
access it (because functions can't access their outer function's
'this').  I then create the coroutine function and call opApply on it,
returning those values, so the foreach loop ends up iterating over the
coroutine.

> Also the class coroutine foreach tries as follows failed:
>
> class count
> {
>    x =3
>    function opApply(m) = coroutine function opApply()
>    {
>        writefln(m) // prints null, that's pretty astonishing for me.

Again, where is 'm' coming from?  It's null because you didn't pass
anything.  The object on which opApply is called is in 'this'.

>        for(i: 1 .. x + 1)
>            yield(i)
>    }
>
> }
> global c= count()
>    foreach(v;c)
>        writeln(v) // prints 1 through 4
> the result is printing nothing, and no runtime error.
>
>
> If I modify the loop to
>    for(i: 1 .. :x + 1)
>            yield(i)
> runtime error shows up:
> Error: opApply(7): Attempting to access field 'x' from a value of type 'null'

That's because 'this' in the opApply is null.

> Seems there's no easy way of using coroutine foreach with class instance?

No, you're just doing it wrong ;)

>> http://www.dsource.org/projects/minid/wiki/LanguageSpec2/Functions#Coroutines

And again, there's the link I gave you last time.  Please read up on it.


More information about the Digitalmars-d-announce mailing list