Can a scalar be "lazy" ?

30 views
Skip to first unread message

Yiyi Hu

unread,
Aug 22, 2005, 4:09:29 PM8/22/05
to perl6-l...@perl.org
my( $s, $t ); $s = "value t is $t"; $t = "xyz"; print $s;
in perl 5, it will give a warning, and won't do "right" thing.
we have to use other way or eval '$s' before print to get a "correct" answer.

So I wonder, If we can make $scalar lazy also. As array now is lazy by default.

Even if making scalar lazy might cause problem sometimes, Is it
possible to add a property which is like
my $var is lazy; to handle these situation?

Thanks,
Xinming

Ingo Blechschmidt

unread,
Aug 22, 2005, 5:49:00 PM8/22/05
to perl6-l...@perl.org
Hi,

Yiyi Hu wrote:
> my( $s, $t ); $s = "value t is $t"; $t = "xyz"; print $s;
> in perl 5, it will give a warning, and won't do "right" thing.
> we have to use other way or eval '$s' before print to get a "correct"
> answer.
>
> So I wonder, If we can make $scalar lazy also. As array now is lazy by
> default.

there're at least three different ways which do want you want:

# Using a closure
my $s = { "value t is $t" };
# And then
say $s(); # Note the ()

# Using Proxy
my $s := new Proxy: FETCH => { "value t is $t" };
# And then
say $s; # Note no ()

# With nothingmuch's lazy proposal (implemented in Pugs)
my $s := lazy { "value t is $t" };
say $s; # Again, no () needed


BTW, does Proxy fill in an appropriate default if one misses STORE? I.e.

new Proxy: FETCH => { "foo" }; # same as
new Proxy:
FETCH => { "foo" },
STORE => {
die "No STORE block...";
};


--Ingo

--
Linux, the choice of a GNU | The next statement is not true.
generation on a dual AMD | The previous statement is true.
Athlon! |

Luke Palmer

unread,
Aug 22, 2005, 6:34:13 PM8/22/05
to Yiyi Hu, perl6-l...@perl.org
On 8/22/05, Yiyi Hu <yiy...@gmail.com> wrote:
> my( $s, $t ); $s = "value t is $t"; $t = "xyz"; print $s;

I have an answer for you that is much more detailed than what you want
to hear. The short answer is "yes".

This is possible to implement, provided you appropriately declare $t.
It all depends on how deep you want the lazy semantics to go. The
shallowest you can get is using the junctive hook (which I'll call
CallHook right now):

role CallHook {
# this is the interface of a role that junctions use for
# autothreading

# call this with the appropriately curried function whenver
# a value that does this role is passed to a function
method CALLHOOK (&call) {
call($?SELF);
}

# call this with the return continuation whenever this
# value is returned from a function
method RETURNHOOK (&retcont) {
retcont($?SELF);
}

# call this with the current continuation whenever this value
# is assigned to a variable
method ASSIGNHOOK (&cont) {
cont($?SELF);
}

# perhaps this role provides more hooks
}

class LazyValue does CallHook {
has &.thunk = { undef };
method CALLHOOK (&call) {
LazyValue.new( :thunk{ call(.eval) } );
}
method RETURNHOOK (&retcont) {
retcont(.eval);
}
method eval () {
.thunk()();
}
method set_thunk (&.thunk) { }
}

my $s = LazyValue.new;
my $t = "Hello, $s!";
$s = "World";
say $t.eval; # Hello World![1]

You could get that pesky eval out of there by defining print (and say)
multimethod variants yourself. You have to tell it when to evaluate
sometime.

Of course, this behavior will not be default, since it can get you
into a lot of trouble. You say that in Perl 6 arrays will behave
lazily, but this is *not* what is meant:

my @array;
my $string = "Hello, @array[]";
@array = ("World!");
say $string; # "Hello, " !!!

What we mean by a lazy array is that laziness propagates automatically:

my @array = <>; # <> returns a lazy array, so @array is now lazy
@array = (1,2,3); # (1,2,3) is not lazy, so @array becomes unlazy

Luke

[1] The well-typed version of this follows. I have hand-checked it
(fairly well, but humans do make mistakes). Hooray for strong typing
when we want it!

use traits <typed>;
role CallHook[::of] {
method CALLHOOK ( &call:(::of $value --> ::T) --> ::T ) {
call($?SELF);
}

method RETURNHOOK ( &retcont:(::of $value --> ::T) --> ::T ) {
retcont($?SELF);
}

method ASSIGNHOOK ( &cont:(::of $value --> ::T) --> ::T ) {
cont($?SELF);
}
}

# I'm Ignoring that you can only parameterize roles, because that's
# a silly policy, and nobody is executing this code, so I'm
# writing in Luke's dialect.

class LazyValue[::of] {
does CallHook of ::of;

# is mock() tells the type checker to trust that I conform to
# this interface without checking[2].
is mock(::of);

has &.thunk = { undef };
method CALLHOOK (&call(::of $value --> ::T) --> ::T) {
LazyValue[::T].new( :thunk{ call(.eval) } );
}
method RETURNHOOK (&retcont(::of $value --> ::T) --> ::T) {
retcont(.eval);
}
method eval (--> ::of) {
.thunk()();
}
method set_thunk (&.thunk:(--> ::of)) { }
}

[2] Which is safe to do, because I intercept all method calls on this
object and replace then with something that takes what the methods
take and returns what the methods return[3].

Also, such an "is mock" declaration must turn off optimizations
associated with the type in question. So all you get is the type
checking.

[3] Unless that method is eval() or set_thunk(), in which case I don't
really conform to ::of. That probably means that eval() and
set_thunk() should be out-of-band functions, not methods.

Larry Wall

unread,
Aug 22, 2005, 4:25:57 PM8/22/05
to perl6-l...@perl.org
On Tue, Aug 23, 2005 at 04:09:29AM +0800, Yiyi Hu wrote:
: my( $s, $t ); $s = "value t is $t"; $t = "xyz"; print $s;

In Perl 6 you make lazy scalars by putting curlies around them:

my( $s, $t ); $s = { "value t is $t" }; $t = "xyz"; print $s();

Currently we also require the de-lazifying context to supply a
postfix .() marker, but possibly that could be assumed in a string
or numeric context.

I really don't see much benefit in making it easier than that.

Larry

Andrew Rodland

unread,
Aug 22, 2005, 8:02:44 PM8/22/05
to perl6-l...@perl.org
On Monday 22 August 2005 04:25 pm, Larry Wall wrote:
> On Tue, Aug 23, 2005 at 04:09:29AM +0800, Yiyi Hu wrote:
> [stuff]

> : Even if making scalar lazy might cause problem sometimes, Is it
> : possible to add a property which is like
> : my $var is lazy; to handle these situation?
>
> In Perl 6 you make lazy scalars by putting curlies around them:
>
> my( $s, $t ); $s = { "value t is $t" }; $t = "xyz"; print $s();
>
> Currently we also require the de-lazifying context to supply a
> postfix .() marker, but possibly that could be assumed in a string
> or numeric context.
>
> I really don't see much benefit in making it easier than that.
>
Agreed, especially since it would be a sort of false laziness (sorry to
confuse the term). It's easy to understand the rule that double quotes
interpolate when they're evaluated, once you learn the rule. Something like
the grandparent's "is lazy" would introduce a whole mess of rules regarding
what gets evaluated when, and in what scope. But there already exists a set
of such rules for curlies, and it only seems sane to use them rather than
adding more complexity.

Yuval Kogman

unread,
Aug 23, 2005, 4:03:52 AM8/23/05
to perl6-l...@perl.org

I see one:

class Object {
has $.expensive_to_compute_but_cachable = delay {
...
};
}

OR

class Object {
has $.expensive_to_compute_but_cachable is delayed = ...;
}

And no one has to know that it's lazy.

With explicit code refs, you need to make an accessor:

class Object {
has $.expensive_to_compute_but_cachable;

method expensive_to_compute_but_cachable (
$.expensive_to_compute_but_cachable # i forget how
# autrijus's "returned" attr on params works
) {
$.expensive_to_compute_but_cachable //= ...;
}
}

Which is just as much headache that we had to do in perl 5.

Also, lazifying semantics are consistent with

sub &infix:<||> ($left, $right is delayed) {
$left ?? $left :: ** $right; # can you steamroll a scalar?
}

--
() Yuval Kogman <nothi...@woobling.org> 0xEBD27418 perl hacker &
/\ kung foo master: /me spreads pj3Ar using 0wnage: neeyah!!!!!!!!!!!

Larry Wall

unread,
Aug 23, 2005, 1:56:08 PM8/23/05
to perl6-l...@perl.org
On Tue, Aug 23, 2005 at 11:03:52AM +0300, Yuval Kogman wrote:

: On Mon, Aug 22, 2005 at 13:25:57 -0700, Larry Wall wrote:
: > On Tue, Aug 23, 2005 at 04:09:29AM +0800, Yiyi Hu wrote:
: > : my( $s, $t ); $s = "value t is $t"; $t = "xyz"; print $s;
: > : in perl 5, it will give a warning, and won't do "right" thing.
: > : we have to use other way or eval '$s' before print to get a "correct" answer.
: > :
: > : So I wonder, If we can make $scalar lazy also. As array now is lazy by default.
: > :
: > : Even if making scalar lazy might cause problem sometimes, Is it
: > : possible to add a property which is like
: > : my $var is lazy; to handle these situation?
: >
: > In Perl 6 you make lazy scalars by putting curlies around them:
: >
: > my( $s, $t ); $s = { "value t is $t" }; $t = "xyz"; print $s();
: >
: > Currently we also require the de-lazifying context to supply a
: > postfix .() marker, but possibly that could be assumed in a string
: > or numeric context.
: >
: > I really don't see much benefit in making it easier than that.
:
: I see one:
:
: class Object {
: has $.expensive_to_compute_but_cachable = delay {
: ...
: };
: }

Which already seems to be there with

lazy {...}

which is, I presume, mostly syntactic sugar for something like:

sub is cached {...}

along with the notion of implicit .() on "value" use, as I already
conceded above. Plus we can assume "is cached" on any function
that can be proven "pure". So I think maybe you're responding to a
different position than the one I'm actually taking.

: OR


:
: class Object {
: has $.expensive_to_compute_but_cachable is delayed = ...;
: }
:
: And no one has to know that it's lazy.
:
: With explicit code refs, you need to make an accessor:
:
: class Object {
: has $.expensive_to_compute_but_cachable;
:
: method expensive_to_compute_but_cachable (
: $.expensive_to_compute_but_cachable # i forget how
: # autrijus's "returned" attr on params works
: ) {
: $.expensive_to_compute_but_cachable //= ...;
: }
: }
:
: Which is just as much headache that we had to do in perl 5.

We could probably extend "is cached" to attributes (and their implied
accessors) if lazy blocks aren't sufficient.

: Also, lazifying semantics are consistent with


:
: sub &infix:<||> ($left, $right is delayed) {
: $left ?? $left :: ** $right; # can you steamroll a scalar?

: }

I'd really like to discourage people from writing thunks without curlies.

I don't think steamrollering a scalar works--it would just take it
as an operation on a list rather than on the elements of the list.
You can't really delazify until you know what kind of a scalar you
want, I suspect, because the lazy closure itself might need to know
in what context to run. Otherwise you just have to return a proxy
object that might turn into a string or number or int or whatever
value is eventually wanted. Of course, presumably infix:<||> has
that context already, so maybe we're looking for something like

$right.want

to force value-finding behavior iff it's called for. After all, you
probably want it to stay lazy if you call it in undifferentiated scalar
context:

$x = $y || lazy { z() }

But maybe I'm confusing levels there. Wouldn't be the first time...

Larry

Yuval Kogman

unread,
Aug 23, 2005, 2:18:04 PM8/23/05
to perl6-l...@perl.org
On Tue, Aug 23, 2005 at 10:56:08 -0700, Larry Wall wrote:

> We could probably extend "is cached" to attributes (and their implied
> accessors) if lazy blocks aren't sufficient.

Hmm... With the whole distinction of &foo as a value and &foo() as a
application of the value, maybe we can sometimes assign a sub as an
rvalue by making a "weak" call.

sub lazy (Code &f) {
return &f.weak_call;
}

I like the relation to is cached, but I think it might be incorrect,
if the lazy block is reused.

BTW, I'm the "nothingmuch" who initially lobbied for lazy { }... =)

> : sub &infix:<||> ($left, $right is delayed) {
> : $left ?? $left :: ** $right; # can you steamroll a scalar?
> : }
>
> I'd really like to discourage people from writing thunks without curlies.

Then how do we define &infix:<||>'s prototype?

> I don't think steamrollering a scalar works--it would just take it
> as an operation on a list rather than on the elements of the list.
> You can't really delazify until you know what kind of a scalar you
> want, I suspect, because the lazy closure itself might need to know
> in what context to run. Otherwise you just have to return a proxy
> object that might turn into a string or number or int or whatever
> value is eventually wanted. Of course, presumably infix:<||> has
> that context already, so maybe we're looking for something like
>
> $right.want

This makes sense, but shuts off the 'want' behavior.

I think we should have an implicit $?CONTEXT object in each sub, and
then you can say to it

$?CONTEXT.unwrap($right);

or something along that metaphor.

This is also nice for things like

method moose (...) {
my Container $x = $?CONTEXT.eval( $?SELF.SUPER::moose ); # maybe even a bit like Sub::Uplevel
...
# you can look inside $x now, but you can't "damage" it
# anymore
...
$x.unwrap; # this serves as context passthrough
}

> to force value-finding behavior iff it's called for. After all, you
> probably want it to stay lazy if you call it in undifferentiated scalar
> context:
>
> $x = $y || lazy { z() }

Yes, I didn't think about that... This is lazyness stacking though,
I think.

--
() Yuval Kogman <nothi...@woobling.org> 0xEBD27418 perl hacker &

/\ kung foo master: /methinks long and hard, and runs away: neeyah!!!

Reply all
Reply to author
Forward
0 new messages