Re: Tweaking junctions

2010-10-29 Thread Martin D Kealey
On Thu, 28 Oct 2010, Damian Conway wrote:
 The apparent paradox ... is due to the assumption (employed in the
 second interpretation) that  is identical to !=. Certainly that is
 true for simple scalar numbers, but not always for vector types such
 as tuples, sets, bags, complex numbers...or junctions.

 That doesn't make either  or != intrinsically invalid on vector types
 (though they obviously are inappropriate for *some* vector types); it just
 means you can't reasonably treat the two operators as universally
 interchangeable, just because they sometimes are.

Well, I think returning or throwing an Unordered exception would be
the appropriate way to handle those, both for complex numbers and for
junctions.

Anyone who thinks they're dealing with real numbers will reasonably
expect  and != to be the same, and a junction can masquerade as
anything (within each thread), including a real number.

And what about when that difference is wrapped up inside a function? In
other words, what's wrong when I expect the following two snippets to
work the same way?

A:

sub anything_is_broken($subject) {
grep { ! .test() } $subject.parts()
}

if anything_is_broken($subject) {
fail($subject)
}
else {
pass($subject)
}

B:

sub everything_is_working($subject) {
! grep { ! .test() } $subject.parts()
}

if everything_is_working($subject) {
pass($subject)
}
else {
fail($subject)
}

Why should it make a difference whether $subject is a junction?

 In summary, the problem here seems to be that, algebraically,
 junctions don't behave exactly like non-junctions. Which is true, but no
 more a problem than the fact that, algebraically, complex numbers don't
 behave exactly like non-complex numbers, or that sets don't behave
 exactly like non-sets, or that Rats don't behave exactly like Nums,
 which don't behave exactly like Ints, which don't behave exactly like
 ints either.

When you're talking about built-in operators, that's plausible, because
by looking for ! in the operator name there are ways to
DWIM-or-throw-an-exception.

That's not true for user-defined functions, so I think the real problem
is that the parallelizing of the expression that contains a junction may
not be obvious at the point where it happens.

Hmmm  maybe one way to improve that might be to say that you can't
put a junction into an untyped scalar, or indeed in any variable that
isn't explicitly marked as this thing might contain a junction. That
would apply, as now, to function parameters and returns, but also to
variables and aggregate members; indeed, *everywhere*.

 And, of course, that's why Perl 6 has strong typing. So that, when
 these differences in behaviour do matter, we can specify what kind(s)
 of data we want to allow in particular variables, parameters or return
 slots...and thereby prevent unexpected kinds of data from sneaking in
 and violating (un)reasonable expectations or inducing (apparent)
 paradoxes. :-)

I don't think strong typing is enough, because we explicitly mask the
application of type constraints by autothreading. Each thread sees a
thing with the type it expects, but the problem comes when the threads
are recombined; the result often won't be what's expected.

Don't get me wrong, I think Junctions are a really clever way of writing
concise conditional expressions, but I think algebraic consistency is
more important than clever conciseness.

-Martin


Re: Tweaking junctions

2010-10-29 Thread Damian Conway
Martin D Kealey suggested:

 Well, I think returning or throwing an Unordered exception would be
 the appropriate way to handle those, both for complex numbers and for
 junctions.

For complex numbers that might be true, because the order relationship
between two complex numbers isn't expressible in-band.

But for junctions, the relationship of sometimes , somtimes = is
entirely expressible. It's just: any(True, False).


 And what about when that difference is wrapped up inside a function? In
 other words, what's wrong when I expect the following two snippets to
 work the same way?

sub anything_is_broken($subject) {
grep { ! .test() } $subject.parts()
}


sub everything_is_working($subject) {
! grep { ! .test() } $subject.parts()
}

 Why should it make a difference whether $subject is a junction?

Because, although the two subroutines seem like they're complementary,
they actually only partition the universe when the universe is strictly
one-dimensional. And sometimes not even then.

For example, here's a *non-junctive* scalar subject for which the two
don't provide consistent answers either:

class Part {
has $.value;
method test { $.value != 0 }
}

class Subject {
has Part @.parts;
method parts { @.parts.pick(2) }
}

my Subject $subject .= new(0..9);

say anything_is_broken($subject);# 0 (because .parts picked 3 and 7)
say everything_is_working($subject); # 0 (because .parts picked 9 and 0)

In other words, it isn't the junctive-ness that creates unexpected
behaviour, it's the assumption that every scalar works the same way.


 That's not true for user-defined functions, so I think the real problem
 is that the parallelizing of the expression that contains a junction may
 not be obvious at the point where it happens.

But that's not a unique property of junctions; that not obviousness
is equally true of any scalar that, for example, simply overloads .Num
or .Str or .Bool.

For instance:

if $result { say $result }

can easily print 0, which is not obvious, but is still both correct
and useful (when, for example, $result the result of a call to Csystem).


 Hmmm  maybe one way to improve that might be to say that you can't
 put a junction into an untyped scalar, or indeed in any variable that
 isn't explicitly marked as this thing might contain a junction. That
 would apply, as now, to function parameters and returns, but also to
 variables and aggregate members; indeed, *everywhere*.

But junctions are an intrinsic part of Perl 6. So it's unreasonable to
*not* expect them. And if you want to not expect them, you can just mark
your variables that way, with (ironically):

my $subject where none(Junction);

Besides, are you also going to extend this segregation of junctions to
not allow C0 but true in untyped scalars either? Because how else will
you avoid the non-obviousness of:

 if $result { say $result }

???


 I don't think strong typing is enough, because we explicitly mask the
 application of type constraints by autothreading. Each thread sees a
 thing with the type it expects, but the problem comes when the threads
 are recombined; the result often won't be what's expected.

Huh? If the variables are strongly typed as non-junctive, a junction
will never be able sneak past into or out-of an autothreading.


 Don't get me wrong, I think Junctions are a really clever way of
 writing concise conditional expressions, but I think algebraic
 consistency is more important than clever conciseness.

Aha. I see that we mean different things when we use the term algebraic
consistency. You seem to want all algebras to be universally
consistent; I want each algebra to be internally consistent. Or to put
it another way, you appear to want:

A given operator or function does one consistent thing
(regardless of the specific types of its operands)

whereas I want:

A given operator or function does one thing
(consistent with the specific types of its operands)

In other words, you seem to be arguing for monomorphism, whereas I'm
definitely arguing for polymorphism. Neither is inherently better, but
one is inherently more powerful. While I deeply respect your
position, I'm going to keep arguing for that more powerful alternative.

Damian