How can classes be made parametric in Perl 6? - metaprogramming

Normally in Perl 6, only roles are allowed to be parametric. Here, we'll be attempting to make classes, a kind (referred to from here on out as a metaobject) that isn't normally allowed to be parametric, parametric.
If you try to make a class parametric the naive way, this happens:
bastille% perl6 -e 'class Foo[::T] {}'
===SORRY!=== Error while compiling -e
Unable to parse class definition
at -e:1
------> class Foo⏏[::T] {}
expecting any of:
generic role
But if you take a look at what metaobject the CArray type from NativeCall uses, you'll find that it's in fact a class, not a role, yet it's still parametric!
bastille% perl6 -MNativeCall -e 'say CArray[int32].HOW.^name'
Perl6::Metamodel::ClassHOW+{<anon>}+{<anon>}
How is this done?

Making classes parametric takes a little bit of metaprogramming to accomplish. A simple parametric container class can be implemented like so:
use v6.d;
class Container {
my role ContainerImpl[::T] {
has T $.value;
method new(Container: T $value) {
self.bless: :$value
}
multi method gist(Container:D: --> Str:D) {
$!value.gist
}
multi method Str (Container:D: --> Str:D) {
$!value.Str
}
multi method perl(Container:D: --> Str:D) {
self.^name ~ '.new(' ~ $!value.perl ~ ')'
}
}
method ^parameterize(Mu:U \this, Mu \T) {
my $type := this.^mixin: ContainerImpl[T];
$type.^set_name: this.^name ~ '[' ~ T.^name ~ ']';
$type
}
}
say Container[Int].new(1).perl;
# OUTPUT: Container[Int].new(1)
So how does this work?
Metaclasses that do the Perl6::Metamodel::MetaMethodContainer role, such as Perl6::Metamodel::ClassHOW, can have additional metamethods mixed in with the type's knowhow (which describes how a specific kind of type, such as a class or role, behaves). Rakudo's grammar invokes the parameterize metamethod on any given type with the parametric type and any parameterized types as arguments when parsing a type's name. Normally, types that are parametric are supposed to implement the parametric archetype, but this doesn't get checked here, which allows any type to be parameterized as long as it implements a parameterize metamethod.
The mixin metamethod is specific to the Perl6::Metamodel::Mixins role, which Perl6::Metamodel::ClassHOW also does. The method mixes in a role by reblessing the class so it's considered to be the same type as the (parameterized in this case) role passed to it.
Combining the parameterize and mixin metamethods allows you to implement parametric behaviour in a role, then use it in a class by parameterizing it before mixing it in. This allows the class to behave as if it actually were a parametric type, even though it's still technically not one.

TL;DR This answer is a "simplified" version of #Kaiepi++'s. It only covers the core bit of code shown below that's extracted from their answer. It's written so that it should work as a standalone explanation, or as an introduction or complement to their answer.
Making a class parametric
The titular question is very broad. But the body of the question boils down to making a class parametric and that's what this answer (and #Kaiepi's) focuses on.
Classes, as a kind of type, don't support parametricity out of the box. But P6 is fully metaprogrammable. So you can just metaprogram a class to add parametricity. NB. This is not an officially supported technique!1
(You could add parametricity at the kind level, such that either all classes, or some new kind of type that you derive from classes, are parametric. But I think that would take considerable effort.2 In the meantime a half dozen lines of fairly straight-forward metaprogramming is all that's required to make a single class parametric. So that's all we'll do in this answer.)
The code
class foo {
my role bar[::T] {}
method ^parameterize(Mu:U \this, Mu \T) {
my $type := this.^mixin: bar[T];
$type.^set_name: this.^name ~ '[' ~ T.^name ~ ']';
$type
}
}
say foo[Int].new.perl;
# OUTPUT: foo[Int].new
The above code is extracted from #Kaiepi's answer, leaving out what I considered non-essential. The rest of this answer explains the code in detail.
role bar[::T]
A role collects attributes and methods together just like a class. The key difference in the context of this SO is that a role is parameterizable and can be added to a class so that the class becomes parameterized.
The bit between the [ and ] is a signature. The ::T is a type variable. The signature can be as complex as you want it to be, just like a regular function signature.
The bar role I've shown has an empty body. In an actual application of this technique you would write the attributes and methods that you want added to the foo class. These would be attributes and methods that need to make use of the parameterization, plus other attributes and methods that it's reasonable to include in the same role.
^some-method-name
A ^ at the start of a method name signals that it will not be a call on its explicit invocant but rather a call "up to" the invocant's "higher order workings" as embodied in a knowhow object that knows how that kind of type works.
Declaring a method with an initial ^ causes the knowhow object for the containing class to be customized to include that method.
^parameterize
If you write foo[...] where the compiler expects a type, the compiler calls (the equivalent of) foo.^parameterize which turns into a call to parameterize on foo's knowhow object.
And foo's knowhow object has been customized to include our method:
method ^parameterize(Mu:U \this, Mu \T) {
my $type := this.^mixin: bar[T];
$type.^set_name: this.^name ~ '[' ~ T.^name ~ ']';
$type
}
\this
What's this all about? (The \ just means "slash the sigil"; I don't mean that aspect.)
this is the foo type object, i.e. the same type object associated with self in ordinary methods in foo that don't start with ^.3
Adding bar to foo so that foo gets parameterized
We've now arrived at the point where we can generate a parameterized foo:
my $type := this.^mixin: bar[T];
Starting with an unparameterized foo held in this we "mix" in bar parameterized with the T passed to ^parameterize.
Following protocol for P6's nominal type system
This line ensures our new parameterized type plays well with the system:
$type.^set_name: this.^name ~ '[' ~ T.^name ~ ']';
Moving on to #Kaiepi's answer
This answer is a simplified version of #Kaiepi's answer.
It isn't sufficient to cover issues such as ensuring that .perl works correctly if an actual implementation is a class with parameterized public attributes.
Footnotes
1 Many details of the metamodel are not part of official P6. The .^parameterize method is not.
2 I'm pretty confident that, with suitable (learning about guts and) metaprogramming, one could make all classes, or a new kind derived from classes, behave like roles inasmuch as being a kind of type that supports parameterization "out of the box" using the obvious syntax:
class foo[::T] { ... }
3 I strongly concur with #Kaiepi's decision not to use \self as the first parameter of a ^ method. That would be a lie and shadow the usual self. Presumably #Kaiepi's thinking is that this is often used as a synonym of self but, if you know P6, clearly isn't the same as self because it's the first parameter but not the invocant parameter.

Related

Is the `def` keyword optional? If so, why use it?

I am aware that a variable can be dynamically typed with the def keyword in Groovy. But I have also noticed that in some circumstances it can be left out, such as when defining method parameters, eg func(p1, p2) instead of func(def p1, def p2). The latter form is discouraged.
I have noticed that this is extendable to all code - anytime you want to define a variable and set its value, eg var = 2 the def keyword can be safely left out. It only appears to be required if not instantiating the variable on creation, ie. def var1 so that it can be instantiated as a NullObject.
Is this the only time def is useful? Can it be safely left out in all other declarations, for example, of classes and methods?
Short answer: you can't. There are some use cases where skipping the type declaration (or def keyword) works, but it is not a general rule. For instance, Groovy scripts allow you to use variables without specific type declaration, e.g.
x = 10
However, it works because groovy.lang.Script class implements getProperty and setProperty methods that get triggered when you access a missing property. In this case, such a variable is promoted to be a global binding, not a local variable. If you try to do the same on any other class that does not implement those methods, you will end up getting groovy.lang.MissingPropertyException.
Skipping types in a method declaration is supported, both in dynamically compiled and statically compiled Groovy. But is it useful? It depends. In most cases, it's much better to declare the type for a better readability and documentation purpose. I would not recommend doing it in the public API - the user of your API will see Object type, while you may expect some specific type. It shows that this may work if your intention is to receive any object, no matter what is its specific type. (E.g. a method like dump(obj) could work like that.)
And last but not least, there is a way to skip type declaration in any context. You can use a final keyword for that.
class Foo {
final id = 1
void bar(final name) {
final greet = "Hello, "
println greet + name + "!"
}
}
This way you can get a code that compiles with dynamic compilation, as well as with static compilation enabled. Of course, using final keyword prevents you from re-assigning the variable, but for the compiler, this is enough information to infer the proper type.
For more information, you can check a similar question that was asked on SO some time ago: Groovy: "def" keyword vs concrete type
in Groovy it plays an important role in Global and Local variable
if the variable name is same with and without def
def is considered local and without def its global
I have explained here in detail https://stackoverflow.com/a/45994227/2986279
So if someone use with and without it will make a difference and can change things.

Introspection: how do we get the name of a class within a class?

Say we have
class Foo {}
Is there a way to obtain "Foo" from within the class?
Yes.
class Foo {
say ::?CLASS.^name; # OUTPUT: Foo
}
Kaiepi's solution has its place, which I'll get to below, but also consider:
class Foo {
say Foo.perl; # Foo
say OUR.WHO; # Foo
::?PACKAGE
}
Foo.perl
This provides a simple answer to your literal question (though it ignores what you're really after, as explained in your comment below and as suggested by the metaprogramming tag and your use of the word "introspection").
OUR.WHO
I think this is typically more appropriate than ::?CLASS.^name for several reasons:
Looks less line-noisy.
Works for all forms of package, i.e. ones declared with the built in declarators package, module, grammar, or role as well as class, and also custom declarators like actor, monitor, etc.
Will lead readers to mostly directly pertinent issues if they investigate OUR and/or .WHO in contrast to mostly distracting arcana if they investigate the ::?... construct.
::?CLASS vs ::?PACKAGE
OUR.WHO only works in a value grammatical slot, not a type grammatical slot. For the latter you need a suitable ::?... form, eg:
class Foo { has ::?CLASS $bar }
And these ::?... forms also work as values:
class Foo { has $bar = ::?CLASS }
So, despite their relative ugliness, they're more general in this particular sense. That said, if generality is at a premium then ::?PACKAGE goes one better because it works for all forms of package.

In Groovy, can I override java-style casting syntax on POJO classes?

I would like to be able to use plain java-style implicit/explicit casting instead of asType overrides so that sources written in Java work properly. I've overridden asType on String similarly to the approach suggested in How to overload some Groovy Type conversion for avoiding try/catch of NumberFormatException? like:
oldAsType = String.metaClass.getMetaMethod("asType", [Class] as Class[])
String.metaClass.asType = {Class typ ->
if (Foo.class.isAssignableFrom(typ)) {
Foo.myCast(delegate)
} else {
oldAsType.invoke(delegate,typ)
}
}
I'd like all of these options to work:
// groovy
String barString
Foo foo = barString asType(Foo.class) // asType works but
Foo foo = barString // implicit cast fails
Foo foo = (Foo) barString // explicit cast fails
The latter two fail because groovy is using DefaultTypeTransformation.castToType, which doesn't attempt to invoke new Foo() unless the object to be cast is either one of a slew of special cases or is some sort of Collection type.
Note that the solution Can I override cast operator in Groovy? doesn't solve the issue because the code that is doing the casting is regular Java code that I cannot alter, at least not at the source code level. I'm hoping that there is either a secret hook into casting or a way to override the static castToType method (in a Java class, called by another Java class - which Can you use Groovy meta programming to override a private method on a Java class says is unsupported)... or some other clever approach I haven't thought of.
Edit: The question is about using Java-style casting syntax, essentially to use groovy facilities to add an autoboxing method. Groovy calls this mechanism "casting," for better or worse (see DefaultTypeTransformation.castToType as referenced above). In particular, I have replaced an enum with a resourced class and want to retain JSON serialization. Groovy's JSON package automatically un/marshals enum values of instance members to strings and I'm trying to make the replacement class serialize compatibly with a minimal changes to the source code.
Part of the problem here is you are confusing conversion with casting. Using the "as" operator is not the same thing as imposing a cast. They seem similar, but they serve separate purposes.
Foo foo = (Foo) barString
That doesn't say something like "create a Foo out of barString". That says "Declare a reference named foo, associate the static type Foo with that reference and then point that reference at the object on the heap that the reference barString currently points to.". Unlike languages like C++, Groovy and Java do not allow you to ever get in a situation where a reference points at an object that is of a type that is incompatible with the reference's type. If you ever got into a situation where a Foo reference was pointing to a String on the heap, that would represent a bug in the JVM. It cannot be done. You can come up with ways to create Foo objects out of String objects, but that isn't what the code above is about.
The answer appears to be "no". Absent a rewrite of the DefaultTypeTransformation.castToType to allow for this sort of metaprogramming, the implication is to use another implementation strategy or use a different language.

On delegate, event, evenhandler, in F#

If I look at Control.IEvent<'Delegate,'Args>, it imposes on 'Args that
IEvent<'Delegate,'Args when 'Delegate : delegate<'Args,unit>...>
Looking at WorkbookBase.NewSheet Event, to which WorkbookEvents_NewSheetEventHandler, of type obj-> unit, can subscribe, I would have expected 'Args to be of type obj in its F# type.
However, I see that the event NewSheet is of type IEvent<WorkbookEvents_NewSheetEventHandler, unit>
What can explain this ?
Adding the extract of the spec mentioned in the answer :
5.2.8 Delegate Constraints
An explicit delegate constraint has the following form:
typar : delegate
During constraint solving (§14.5), the constraint type : delegate is met if type is a delegate type D with declaration type D = delegate of object * arg1 * ... * argN and tupled-arg-type = arg1 * ... * argN. That is, the delegate must match the CLI design pattern where the sender object is the first argument to the event.
Note: This constraint form exists primarily to allow the definition of certain F# library functions that are related to event programming. It is rarely used directly in F# programming.
The delegate constraint does not imply anything about subtypes. In particular, a ‘delegate’ constraint does not imply that the type is a subtype of System.Delegate.
The delegate constraint applies only to delegate types that follow the usual form for CLI event handlers, where the first argument is a “sender” object. The reason is that the purpose of the constraint is to simplify the presentation of CLI event handlers to the F# programmer.
Look at how the spec defines the delegate constraint: the obj sender argument is implicit.

should it be allowed to change the method signature in a non statically typed language

Hypothetic and academic question.
pseudo-code:
<pre><code>
class Book{
read(theReader)
}
class BookWithMemory extends Book {
read(theReader, aTimestamp = null)
}
</pre></code>
Assuming:
an interface (if supported) would prohibit it
default value for parameters are supported
Notes:
PHP triggers an strict standards error for this.
I'm not surprised that PHP strict mode complains about such an override. It's very easy for a similar situation to arise unintentionally in which part of a class hierarchy was edited to use a new signature and a one or a few classes have fallen out of sync.
To avoid the ambiguity, name the new method something different (for this example, maybe readAt?), and override read to call readAt in the new class. This makes the intent plain to the interpreter as well as anyone reading the code.
The actual behavior in such a case is language-dependent -- more specifically, it depends on how much of the signature makes up the method selector, and how parameters are passed.
If the name alone is the selector (as in PHP or Perl), then it's down to how the language handles mismatched method parameter lists. If default arguments are processed at the call site based on the static type of the receiver instead of at the callee's entry point, when called through a base class reference you'd end up with an undefined argument value instead of your specified default, similarly to what would happen if there was no default specified.
If the number of parameters (with or without their types) are part of the method selector (as in Erlang or E), as is common in dynamic languages that run on JVM or CLR, you have two different methods. Create a new overload taking additional arguments, and override the base method with one that calls the new overload with default argument values.
If I am reading the question correctly, this question seems very language specific (as in it is not applicable to all dynamic languages), as I know you can do this in ruby.
class Book
def read(book)
puts book
end
end
class BookWithMemory < Book
def read(book,aTimeStamp = nil)
super book
puts aTimeStamp
end
end
I am not sure about dynamic languages besides ruby. This seems like a pretty subjective question as well, as at least two languages were designed on either side of the issue (method overloading vs not: ruby vs php).

Resources