--- Damian Conway <[EMAIL PROTECTED]> wrote:
> Austin Hastings wrote:
> 
> > But what's the vision for p6? 
> > 
> > My expectation is that the type-checking stuff will be heavily used
> > for:
> > 
> > 1- Large scale projects.
> > 
> > 2- CPAN modules.
> > 
> > I expect that the folks who want to do one-liners will still want
> to be
> > able to say C<perl6 -MCPAN whatever>
> > 
> > So the "strict" bits have to be forgiving of the "non-strict"
> callers.
> 
> To me that's a contradiction in terms. Strictness can't be
> "forgiving". At the risk of stating the obvious, that's the 
> whole point of strictness: to be strict.

Of course strictness can be forgiving. That's why the "Insert Coin"
slots on soda machines have a beveled slot -- they expect the user to
be a little uncoordinated, but once inside it's going to be all
by-the-book.


> 
> What we're suggesting is that if you want to be non-strict, don't use
> types. After all, they're optional.
> 
> "But," (I hear you say), "I may not have a choice! If the Cruel
> Module Author chose to use types, then I'm forced to do so too. 
> That's not Lazy."
> 
> And you're right. The module author gets to determine the interface
> of their module. And if they determine that that interface will
> provide type-safety, then you have to respect that. Just as if 
> they decided that it would have an OO interface, or would set 
> PRE and POST conditions on calls, or would only take named 
> arguments.
> 
> Because the author of the module is *relying* on that interface to
> ensure the correctness of their code; to prevent inappropriate data
> being passed to their subroutines. Typing is yet another way in 
> which they can relax, knowing that the wrong type of data will be
> headed off at the (compiler) pass.
>

But this isn't really true, is it? When you come down to cases, you can
construct a case wherein the wrong type of data gets coerced into the
"round hole" in every language.

sub foo($a is Int);

my $line = "Hello, World";
my Int $i ::= $line;

foo($i);   # Compiles okay, doesn't work.

So the module author is relying on the module user to make sure that
the intent of the interface is satisfied. The type bits are there to
help them communicate, and the let the compiler jab the willing user in
the ribs when he goofs.

> > To me, the way around this is NOT to force interface contracts down
> to
> > some lowest common denominator, but rather to document what is
> > expected, and then allow the developer to accept responsibility for
> > stepping on his crank.
> 
> Yep. And the way for the client of some module to accept that
> responsibility 
> is to put in an explicit conversion.

This statement gives me the heebie-jeebies.

Perl5 ==================================[*]===== Java/C

extern long factorial(int);
char buf [BUFMAX];

while (gets(buf))
{
  if (want_fact())
  {
    int i;

    i = atoi(buf);
    printf("%ld", factorial(i));
  }
}

Versus:

use Math qw(factorial(Int $));
my $line;

while ($line = <>)
{
  if (want_fact())
  {
    my Int $i = $line;   # Or do I need C<my Int $i = $line.Int;> ?
    print factorial($i);
  }
}


Say it ain't so, Joe!


> > If you say, "Give me an Array of Hash of (String => Array of
> BigNum)"
> > and I say, "Here's an Array of Scalars. They're OK, I promise" and
> > things "gang agley", it's my fault. I can deal with that.
> 
> But large projects -- where typing will be most important -- *can't*
> deal with that. That's the point of typing: to specify and enforce 
> interface contracts. At compile-time if at all possible.


The large project folks will have the standard C<use ludicrous
typesafety;> at the top of their files, so no problem.


> > But at the bottom, the C++ problem is a problem of its own making
> > -- people want coercion. Just like me. I want coercion. I want 
> > the ability to take advantage of the really nifty, carefully 
> > written code available from dedicated, rigorous hackers like 
> > this Conway fellow. But I want to do it as a slap-dash hack 
> > with no thought given to type stuff. How?

> By distinguishing a parameter that *requires* a particular type, 
> from a parameter that *ensures* a particular type (by coercion 
> if necessary). I've suggested that using C<is copy> semantics 
> should indicate "make whatever I'm actually given into one of 
> these, if you can", whereas reference semantics say "this must
> already be one of these" (by their very nature, since an 
> aliased parameter *is* the argument).

Hmm. Granted that references must be basically correct when you use
them, there's really two cases here:

1- I give you something that's just wrong, but convertible. Example:
Array of int/Int.

In this case, I'm not sure that 

2- I give you something that I know is right, but I don't have all the
type fluff attached. Example: Int/Scalar.

In this case, the data is essentially right, and I need a way to tell
the typechecker: Hey, I'm right! Shaddap.

Consider the case of an XML datastore:

<struct>
<list name="a"><item value=1/><item value=2/><item value=3/></list>
<list name="b"><item value=4/><item value=5/><item value=6/></list>
<list name="c"><item value=7/><item value=8/><item value=9/></list>
</struct>

When I C<use XML::P6Parser>, it's going to give me a {Hash of String =>
Array of Scalar} or maybe {Array of {String, Array of Scalar}}

When I C<use Statistics::AnalyzeThis;>, it's going to want an Array of
Ints or Nums or some such. So while I'm willing to say:

analyze(%hoAoS.values);

I'm leery of the complex-cast semantics. (Speaking of which, what are
they?)

analyze(%hoAoS.values.dynamic_cast(Array of Int)); # Shades of C++


> Alternatively, one might imagine a separate and explicit trait that
> specifies that a parameter is allowed to coerce its corresponding
> argument to it's own type.
> 
> But what we can't allow is a reference parameter to coerce its
> argument. For example:
> 
>       sub loggedincr (Int $i is rw) {
>               print $log: "Incremented $i.id()\n";
>               $i++ unless $i.constant;
>       }
> 
>       # and later...
> 
>       int $x = 7;
>       loggedincr($x);
> 
> The $i parameter *can't* be bound to $x, because $x doesn't have the
> necessary features to be an Int (e.g. it doesn't have the capacity 
> to respond to method or property lookups). Even wrapping $x in a 
> temporary Int wouldn't help, since it would render the logging of 
> an .id meaningless. The whole point of having types is to catch 
> this kind of problem at compile-time.

Yep. Typesafe compilers catch some errors. No question. Further, there
is no trivial coercion of items not in the same family. That would
require either a "helper function" or an explicit coercion by the
coder.

It occurs to me that this is creating a pretty wide gap between
"typeless" and "typed" code. I can write C<sub foo> or I can write
three flavors of C<multi foo($x is ______)>

> >>On the other hand, I have *no* problem with this:
> >>
> >>    sub foo(@a is Array of Int is copy) {...}
> >>                               ^^^^^^^
> >>doing the kind of coercive wrapping you're suggesting.
> > 
> > 
> > What's the difference? (Aside from the obvious, that is...)
> 
> Suppose I call:
> 
>       foo(@x);
> 
> The difference is that @a above is a lexically scoped variable into
> which the contents of the corresponding argument (@x) are copied
> (i.e. assigned). Now it's perfectly possible that the contents of 
> @x can be assigned into @a, even if the argument and the parameter
> are of wildly different types.

Will you be invoking the copy constructor? If so, which one:
C<@x.COPY(@a)> or C<foreach @x { @x.push($_.COPY(})>?

Also, for C<is copy> traits, one of the early optimizations is going to
be copy-on-write, I suspect. Having the syntax for "is coerced" and "is
copy" be the same may impair some optimization (coercing when only COW
is desired for large arrays).

> 
> But if I say:
> 
>       sub foo(@a is Array of Int) {...}
>       ...
>       foo(@x);
> 
> then I'm saying: "within &foo, @a is just another name for @x". So
> they are (temporarily) the same thing. 

> That can only (be allowed to) happen if the actual type of @x 
> satisfies all the requirements of the declared type of @a.

And that's the kind of thing I want the type-checker to permit. To me,
this translates into the reverse of the "normal" strongly-typed
permissions:

"Everyone Knows" that an Int is a Scalar, and therefore a sub that has
a Scalar parameter can safely be passed an Int. This is normal.

What I want is the ability to do the opposite: Silence the warning that
occurs when I pass a Scalar to a sub expecting an Int.

=Austin


Reply via email to