Re: Type variables vs type literals
HaloO Larry, you wrote: : Could you explain what exactly 'run-time lazy type aliasing' is? : I mean what does it achieve and how does it compare to type : instanciation and parametric contraint checking? It achieves guaranteed *late* binding of types, whereas generics/roles are biased towards early binding (class composition time, usually at compile time). To me binding implies prior existance. That is the type instanciation takes place elsewhere and flows into a scope as a temporarily bound variable. How do you intent to achieve demand driven on-the-fly type instanciations? This seems to me a necessity for Perl6 where not all type information is available at compile time. : Something like unary operator does? Don't follow that. Well, I regard operator does as binary. It's LHS is the receiver of the role given as the RHS. This composing into comes in two modes for compile time and runtime. E.g. class LHS does RHS {...} # binary does at compile time $x does RHS; # runtime role attachment does RHS;# unary form? : The only thing I did was to replace the :: sigil with a subtype : declaration. It's the lack of a my that bothers me. : Does subtype always go into global scope? I thought : that it requires a *name to go into global and our to go to the : innermost module---or was that package? For unqualified packages/modules/classes the default up till now has been to put them into *. But I think I'm going to change that. See below. That is very wise. But I would make a distinction in defaulting to my or our: the keyword declared things like class, sub, etc default to our, while the sigiled variables default to my. The latter would prevent data leakage and auto-vivification surprises: sub a { $x = 'a' } sub b { $x = 'b' } sub x { rand() 0.5 ?? a :: b; say $x; # should print undef unless $x defined before a } : I mean even non-declared : auto-vivified vars go into the immediately surrounding scope, : or am I horribly mistaken? They are our by default, not my. Just as subs are our by default. And indeed, that is probably what package/module/class names are going to default to as well. I think that forcing our on variables that shall go to the outer scope where sharing is intended is a good idea. There could even be a compiler mode that doesn't actually produce bytecode but re-writes the source with nested our declaration moved to there proper place. The purpose of my and our declarations is to give the compiler and the runtime environment about the declared variable. Our stores that information at the innermost level of collaboration while my stores it at the innermost scope. For auto-vivified vars no storing is needed because they bear no specific meaning. I was confused because I thought of file-scoped class Foo; declarations as defaulting directly to *. That's inaccurate. It's just that the current package at the start of a file happens to be *, and so it doesn't matter if you say class Foo or our class Foo, because they mean the same thing at that point. If you say class Bar within the scope of any other package/module/class, though, it should default to our class Bar. So it might as well default to our everywhere. The upshot is that if you want to declare a module that declares a bunch of global classes inside it, you have to use *. That seems like good documentation anyway. Only the first class or module declaration in a file defaults to *. I think it defaults to the scope it is embedded into. Conceptually a module is instanciated into the surrounding scope by a use statement. The implementation might of course be optimised in loading the generic code of the module ones and create a proxy and/or specific instanciations of generics into the importing scope. Hmm. Does that mean that the top-level program starts running in * instead of in Main. Or maybe the * rule only works on .pm files? I would keep files out of the picture. The canonical form of a Perl6 program is the concatination of all sources with the package Foo; ... eof replaced with package Foo {...}. In this line of thinking * is the outer shell of the Perl6 bubble you are in. Immediately outside that bubble one typically finds Parrot or Pugs, but it could be other forms of programatical embedding. : my ::TypeRef; # type variable? No, that's what I've been trying to say. I haven't wanted ::T to be a type variable by itself. It's a type literal that simply doesn't have to have been declared yet, but we know that if it had been declared, we could also call it bare T. I want some other syntax for declaring type variables that involves explicitly mying or ouring the type. [Or at least I did want it. I change my mind below.] So currently the above would be a syntax error. We don't want ::TypeRef to behave differently in my ::T; my ::T $foo; They should either both declare ::T, or both refer to an existing type literally named T. That's where the ::-as-a-variable-sigil
Re: Type variables vs type literals
HaloO Larry, you wrote: Well, there's something to be said for that, but my gut feeling says that we should reserve the explicit generic notation for compile time processing via roles, and then think of run-time lazy type aliasing Could you explain what exactly 'run-time lazy type aliasing' is? I mean what does it achieve and how does it compare to type instanciation and parametric contraint checking? as something a little different. So if you really want to write that sort of thing, I'd rather generalize roles to work as modules containing generic subs: role FooStuff[T] { sub foo (T $x, T $y) { } ... } Otherwise people are tempted to scatter generics all over the place, and it's probably better to encourage them to group similarly parameterized generics in the same spot for sanity. It also encourages people to instantiate their generics in one spot even if they're calling them from all over. [..] I think people should say does FooStuff[Int] and automatically alias all the routines to the Int form so we don't have to scatter :[Int] all over. Or if they just say does FooStuff, they get lazily-bound type parameters. I get that right, FooStuff can be instanciated without a class or object? Something like unary operator does? Into which scope do these role instanciations go? Does does *FooStuff[Int] mean global? : ::T $x := $obj; : my T $y = $x; : : The prime use of that feature is to ensure type homogenity for : temporary and loop vars in a certain scope. But the scope in the : above syntax is not apparent enough---to me at least. Actually, I was holding those up as bad examples of scope visibility, so I'm agreeing with you there. (Which is why I suggested that we require a my context, even if only implied by the formal paramter list.) : Thus I opt for an explicit : :subtype T of $obj.type; # or just $obj perhaps? :T $x := $obj; :my T $y = $x; Yuck. I think that completely obscures the lazy type binding. Plus you've managed to omit the my, so you've created a global subtype T. (Unless, as I'm leaning towards, we make such bare inner types illegal, and require my, our, or *.) The only thing I did was to replace the :: sigil with a subtype declaration. Does subtype always go into global scope? I thought that it requires a *name to go into global and our to go to the innermost module---or was that package? I mean even non-declared auto-vivified vars go into the immediately surrounding scope, or am I horribly mistaken? sub foo() { if (1) { $x = 'from above' } if (2) { say $x } # prints 'from above' even if no outer $x exists? say $x; # prints undef or 'from above' if outer $x } Up til now I assumed that ::T and plain T mean exactly the same if T is pre-declared/defined. The type sigil is only needed to defer the definition/binding. In a certain sense this is a hanging type reference similar to foo beeing a reference to Code. Which brings me to the question how to bind a type reference. I mean we have my fooref := foo which allows fooref() calls, right? How are type references derefed if not by dropping the sigil? ::TypeRef() perhaps? my ::TypeRef; # type variable? sub make ( ::Type, Type $value ) returns Type { if (::TypeRef != ::Type) { ::TypeRef = ::Type; } my Type $x; $x = $value; return $x; } my $i = make(Int, 17); my @a = make(Array, [1,2,3]); # single element array? # or three element array? my $e = make(Int 'string'); # type error in make? With the automatic binding of a ::Type variable to the type of it's argument the definition of make could be shortend to sub make ( ::Type $value ) returns Type { ... } and called like this my $i = make(17); which at least prevents type errors ;) Regards, -- TSa (Thomas Sandlaß)
Re: Type variables vs type literals
HaloO Larry, you wrote: On Thu, Jun 30, 2005 at 09:25:10AM +0800, Autrijus Tang wrote: : Currently, does this: : : sub foo (::T $x, ::T $y) { } : : and this: : : sub foo (T $x, T $y) { } : : Means the same thing, namely : :a) if the package T is defined in scope, use that as the : type constraint for $x and $y : :b) otherwise, set ::T to be the most immediate common supertype : of $x and $y. : : Is this understanding correct? I'd like to disambiguate the two cases : by making ::T in parameter list to always mean type variable (sense b), : and the bare T always mean type name (sense a)? I think it would be good to distinguish those, but I've been mulling over whether ::T should be the syntax for b. (And also whether b is the correct way to think about it in Perl 6.) The problem with using ::T for autovivify your type from the argument is that ::($x) doesn't mean that, and it looks like it should. I think the point is that using a type variable in the signature of a sub is too subtle for recognizing it as a type function that produces typed subs. Thus---even if it resembles C++ templates---I suggest to put the type parameters onto the sub: sub foo[T] (T $x, T $y) { } which basically constrains $x and $y to have the same type. A call to foo might automatically instanciate appropriate implementations. With MMD and all methods and attributes beeing virtual this could usually be a no-op as far as the code generation is concerned. But the type checker has to know what constraints are in effect. The syntax for explicit selection could be foo:(Int)('3','2') which prevents the interpretation foo:(Str,Str). OTOH, this might overload the :() too much. Could we invent more colon postfix ops? Thus the above were foo:[Int]('3','2'). And while we are at it since () is more call-like and [] lookup-like we could reserve :() for more complicated type literals and use :[] for formal type signatures on subs as it is the case for roles. The :: does imply indirection of some sort in either case, but it's two very different kinds of indirection. ::($x) can't declare a lexical alias for some type, whereas ::T presumably could, though your formulation seems more of a unificational approach that would require ::T everywhere. In other words, if we do type binding like b, we would probably want to generalize it to other binding (and assigning?) contexts to represent the type of the bound or assigned object, and at the same time create a lexical alias for the bare type. So we might want to be able to say ::T $x := $obj; my T $y = $x; The prime use of that feature is to ensure type homogenity for temporary and loop vars in a certain scope. But the scope in the above syntax is not apparent enough---to me at least. Thus I opt for an explicit subtype T of $obj.type; # or just $obj perhaps? T $x := $obj; my T $y = $x; With my proposal from above the short form could be :[T] $x := $obj; my T $y = $x; or the current form with :() :(T) $x := $obj; my T $y = $x; Regards, -- TSa (Thomas Sandlaß)
Re: Type variables vs type literals
On Mon, Jul 04, 2005 at 04:09:59PM +0200, TSa (Thomas Sandlaß) wrote: : I think the point is that using a type variable in the signature of a sub : is too subtle for recognizing it as a type function that produces typed : subs. Thus---even if it resembles C++ templates---I suggest to put the : type parameters onto the sub: : : sub foo[T] (T $x, T $y) { } : : which basically constrains $x and $y to have the same type. A call : to foo might automatically instanciate appropriate implementations. : With MMD and all methods and attributes beeing virtual this could : usually be a no-op as far as the code generation is concerned. : But the type checker has to know what constraints are in effect. Well, there's something to be said for that, but my gut feeling says that we should reserve the explicit generic notation for compile time processing via roles, and then think of run-time lazy type aliasing as something a little different. So if you really want to write that sort of thing, I'd rather generalize roles to work as modules containing generic subs: role FooStuff[T] { sub foo (T $x, T $y) { } ... } Otherwise people are tempted to scatter generics all over the place, and it's probably better to encourage them to group similarly parameterized generics in the same spot for sanity. It also encourages people to instantiate their generics in one spot even if they're calling them from all over. : The syntax for explicit selection could be foo:(Int)('3','2') which : prevents the interpretation foo:(Str,Str). OTOH, this might overload : the :() too much. Could we invent more colon postfix ops? : Thus the above were foo:[Int]('3','2'). And while we are at it : since () is more call-like and [] lookup-like we could reserve :() : for more complicated type literals and use :[] for formal type signatures : on subs as it is the case for roles. I think people should say does FooStuff[Int] and automatically alias all the routines to the Int form so we don't have to scatter :[Int] all over. Or if they just say does FooStuff, they get lazily-bound type parameters. : The :: does imply : indirection of some sort in either case, but it's two very different : kinds of indirection. ::($x) can't declare a lexical alias for : some type, whereas ::T presumably could, though your formulation seems : more of a unificational approach that would require ::T everywhere. : : In other words, if we do type binding like b, we would probably want to : generalize it to other binding (and assigning?) contexts to represent : the type of the bound or assigned object, and at the same time create : a lexical alias for the bare type. So we might want to be able to say : : ::T $x := $obj; : my T $y = $x; : : The prime use of that feature is to ensure type homogenity for : temporary and loop vars in a certain scope. But the scope in the : above syntax is not apparent enough---to me at least. Actually, I was holding those up as bad examples of scope visibility, so I'm agreeing with you there. (Which is why I suggested that we require a my context, even if only implied by the formal paramter list.) : Thus I opt for an explicit : :subtype T of $obj.type; # or just $obj perhaps? :T $x := $obj; :my T $y = $x; Yuck. I think that completely obscures the lazy type binding. Plus you've managed to omit the my, so you've created a global subtype T. (Unless, as I'm leaning towards, we make such bare inner types illegal, and require my, our, or *.) : With my proposal from above the short form could be : ::[T] $x := $obj; :my T $y = $x; : : or the current form with :() : ::(T) $x := $obj; :my T $y = $x; I think any lexically scoped declaration should be governed by a my (either explicitly or implicitly), and that includes type declarations. A bare :[T] or :(T) outside a declaration is not good enough. It's okay if there's already a my outside, in which case you're declaring both the variable and the type, but there also needs to be a way of scoping my to the type without declaring the variable. I want to be able to distinguish my (T) $x := $obj from (my T) $x := $obj And maybe that's just how we should leave it. I confess that my [T] $x := $obj [my T] $x := $obj method foo ($self: [T] $x) {...} are prettier, and more reminiscent of generics, but I worry about confusiion with [$h,[EMAIL PROTECTED] bindings, and with reduction operators. On the other hand, (T) looks like a cast, and [T] isn't *really* all that ambiguous. Given how often these things will occur in other contexts that use colons for other things, I don't think it's a very good plan to go with :[T]. These are visually confusing: my :[T] $x := $obj method foo ($self: :[T] $x) {...} Bare [T] would preclude its use in rvalue context, however, as it might be taken as a reduction operator. No big loss, in my estimation. Could maybe allow [my T] there, I suppose. Larry
Re: Type variables vs type literals
On Thu, Jun 30, 2005 at 09:25:10AM +0800, Autrijus Tang wrote: : Currently, does this: : : sub foo (::T $x, ::T $y) { } : : and this: : : sub foo (T $x, T $y) { } : : Means the same thing, namely : :a) if the package T is defined in scope, use that as the : type constraint for $x and $y : :b) otherwise, set ::T to be the most immediate common supertype : of $x and $y. : : Is this understanding correct? I'd like to disambiguate the two cases : by making ::T in parameter list to always mean type variable (sense b), : and the bare T always mean type name (sense a)? I think it would be good to distinguish those, but I've been mulling over whether ::T should be the syntax for b. (And also whether b is the correct way to think about it in Perl 6.) The problem with using ::T for autovivify your type from the argument is that ::($x) doesn't mean that, and it looks like it should. The :: does imply indirection of some sort in either case, but it's two very different kinds of indirection. ::($x) can't declare a lexical alias for some type, whereas ::T presumably could, though your formulation seems more of a unificational approach that would require ::T everywhere. In other words, if we do type binding like b, we would probably want to generalize it to other binding (and assigning?) contexts to represent the type of the bound or assigned object, and at the same time create a lexical alias for the bare type. So we might want to be able to say ::T $x := $obj; my T $y = $x; and mean something like (my type T) $x := $obj my T $y = $x; In that formulation, the unificational approach looks more asymmetrical: sub foo (::T $x, T $y) { } meaning sub foo ((my type T) $x, T $y) { } On the other hand, if we continue the trend that multiple my's in the same scope declare the same variable (possibly with warning), then sub foo (::T $x, ::T $y) { } would presumably be okay semantically, even if the second ::T is construed as warning bait. But it also goes against the grain to autolexicalize something based on the sigil, so we probably should consider ::T a variable and require the sigil everywhere in the absence of a more explicit declaration. ...Except for the inconvenient fact that it would conflict with the use of ::T as a literal-but-not-yet-declared type, which is valid even in signatures, and the b form abrogates that interpretation. So either we need a different sigil for type variables, or a syntax for explitly binding and declaring an autovivified type. (Which, interestingly, could also be used in rvalue context.) So maybe we really want something like sub foo ((my T) $x, T $y) { } for the asymmetrical case, and sub foo ((my T) $x, (my T) $y) { } for the type unificational case. Maybe we can shorten that to sub foo ((T) $x, (T) $y) { } within an existing my context, such as parameter lists. And maybe that's even how we suppress the warning for duplicate my T declaration. For bare assignment with run-time type capture, you'd have to say my $x; (my T) $x = $obj; but we also don't have to dup the my when there already is one: my (T) $x = $obj; my (T) $x := $obj; In either case, the actual type is bound rather than assigned, but I don't think that makes any effective difference since types are always represented by object refs... =begin hole-poking mode Larry
Re: Type variables vs type literals
On Fri, Jul 01, 2005 at 11:51:55AM -0700, Larry Wall wrote: : So either we need a different sigil for type variables, or a syntax : for explitly binding and declaring an autovivified type. (Which, : interestingly, could also be used in rvalue context.) I neglected to provide an example of this, but it'd be something like $x = (my T) $y; to declare that T is whatever type $y happens to be when evaluated. It would have to be a special form, though, since it needs to expect a term after it rather than an operator. And it would require the absence of anything following T, since (my T $x) means something entirely different, and in fact requires an operator to follow. I don't see a better approach offhand, unless it's [my T], which would have to be just a special, and risks visual confusion with lists and reduction operators. So I'm still thinking (T)/(my T) is the better approach. But it could use more collective mulling. Larry
Re: Type variables vs type literals
Perhaps type parameters to roles could also be written in (T) notation: role Tree[(Returns)] {...} but that would imply the parameter name is Returns rather than returns. Maybe that's okay, since it's usually a positional parameter or a special of form anyway. Larry