Perl 6 – Getting beyond static vs. dynamic [pdf]
fosdem.org
fosdem.org
I always it puzzling that to avoid the tedious parts of static type systems, people throw away static checking entirely. As demonstrated by Perl 6 and other gradual typing systems, the whole dualism is imaginary.
I wish that the "runtime dynamicity with as much compile-time staticity as possible" idea will be the norm in the dynamic language community. I wish that someday when we talk about the days of 100% dynamicity with no static checking, we can remark that we have luckily passed that dark age.
I've not actually checked the following code yet. I just wanted to post it now and will return to update after I've tested this stuff tonight, but here's what I'm currently thinking:
* The Any type is the default value type.
* Built-in literals are automatically converted in to more specific value types at the time they are encountered by the compiler:
my $a; # $a contains an `(Any)` aka an undefined Any
$a = 42; # puts an Int value into $a
* User defined literals and run-time string input are also supposed to be optionally automatically converted from an Any into a more specific type on assignment, but that's not yet implemented in the Rakudo P6 compiler.* Optional explicit types improve type-checking on assignment:
my Int $d = 22/7; # compile-time error
* Not yet implemented but due to land soon: my Int(Any) $e = 22/7; # coerces 22/7 (a Rat, thus an Any)
# to 3 (an Int) at compile-time
* And for calls: sub add (Int $x, Int $y) { $x + $y }
my $a = 42;
my $b = '42';
my Str $c = '42';
add($a, $a); # OK
add($a, $b); # run-time error?
add($a, $c); # compile-time error
* Optional explicit types enable multisub dispatch: sub add (Int $x, Str $y) { say "oops!" }
add($a, $c); # resolves to above sub at compile-time
# says "oops!" at run-time
* P6 allows users to introduce "subsets". Subsets combine a base type (always static/compile-time) with additional constraint logic. The additional constraint may force the subset to become partially dynamic rather than solely static.* Subsets can be applied in an assignment or signature:
my enum Day ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
subset Weekday of Day where 'Mon' .. 'Fri';
my Weekday $wd = Sat; # compile-time error
sub foo (Weekday $wd) { ... }
* The additional constraint logic of a subset is a P6 "smart match" -- arbitrary code that accepts or rejects a specific value of the base type. In the Weekday subset above the constraint logic is `'Mon' .. 'Fri';`.* If code analysis can deduce a finite set from the combination of base type and the additional constraint logic, then a compiler may choose to make the whole subset static and check and resolve calls and constrained assignments at compile-time. Otherwise the static part of a subset is checked at compile-time, which may lead to a particular call being among the candidates for a final dispatch, and then there may be a final round of call resolution and checking at run-time.
* Assignments and function calls that don't involve dynamic subsets, or where the compiler can eliminate them from consideration at compile-time, are type checked and dispatch resolved at compile-time.
* Methods are always dispatch resolved at run-time.
Which is a gross, deliberate misunderstanding of how dynamic typing works.
The unityped argument states only that any language admits a static type analysis regardless of whether it admits to it in polite company. From here gradual types are natural: just embed the unitype into a richer static type system.
If there's a dynamic type system (called a "tag" system sometimes to ensure distinctiveness) then that all lives after these types have run. It can be influenced by these types, but it is certainly more than merely syntactic/logical which gives it different correctness/behavior/UX properties.
If you're arguing something like "both 'static types' and 'dynamic types' are elements of a larger class of things called 'types'" then I'd be happy to talk about that given a meaningful definition of this larger class "types".
Generally, however, I find that most definitions of this larger class of "types" are difficult to operationalize. Due to that, please re-read my responses and mentally impute "static types" wherever I say "types". I try to be explicit about this, and it is my general MO, so if any disagreement or misunderstanding still remains then I'd be happy to talk about that as well.
Types are not just "types", there exists myriads of interpretations and implementations. E.g. nominal vs structural, compile-time vs run-time, dynamic vs static, polynomial (HM) vs exponential (cartesian), sound vs unsound. Please get yourself accustomed to the type literature, or invent your own glossary, as perl usually does when it has no idea about prior art. In this case it has.
From the implementation POV tags are indices into types and used with primitives as tags, hence the name, reserved "bits", e.g. with 30bit intnums or the many free NaN tagging bits in doubles or normal class slots with multiword objects. They are carried along the lifetime of the variable, hence add critical run-time safety and inherent slowness.
I agree that there are no absolute rules. A fixed system is boring, and often very inefficient depending how you look at it. Sure my system is static, but inside the black box there is a lot of "dynamism" that I can't change without a recompile. The development environment is dynamic. Writing code, creating the rules, and creating the locked down static types are all dynamic actions. There is no need for a separation between developer/user. User can set the rules, create the workflow, compile the code on the fly. There is no need for a separation between compile and run time.
GHC is itself written in Haskell (utilizing a technique known as bootstrapping), but the runtime system for Haskell, essential to run programs, is written in C and C−−.
I don't know much about C--, but C, what Haskell's runtime is written in, is fairly dynamic.
Even languages with strict type systems like Haskell don't entirely throw away dynamic typing. It has `unsafeCoerce` which can attempt to cast a value at runtime, and fail if it's not the right type like any dynamic language would. However, when combined with Data.Typeable, storing type information in addition to the values you're interested in allows you to turn `unsafeCoerce` into something much safer: `fromDynamic`, which doesn't cause the runtime to fail, but instead returns a Maybe type - one that either contains a successfully coerced value or Nothing - and the language forces you to check this before you can use the value. You have the best of both worlds now - the ability to write whatever dynamic typed code you want, but with the ability to fall back to a strict type system to keep things in check.
Aiui method calls are statically type-checked and run-time resolved with a couple exceptions. If a method name (not a method object) is in a variable, type-checking is run-time. If a class is finalized (not yet implemented), method calls are resolved at compile-time.
This is pretty sketchy info because I'm still trying to figure it all out but I'll be trying to confirm it tonight or next week and will post back here if I find out mistakes I've made or more useful info.
This is the exact opposite of dynamic typing. Dynamic typing doesn't throw away type information, it uses it to perform safe conversions, not unsafe coercions.
The module is called Data.Dynamic and it is a complete dynamic type system embedded inside Haskell at runtime.
It's not very popular. Read into that whatever you like. There are some reasons to use it, perhaps, but most, to my understanding, see it as opaque and difficult to reason with.
Perl5 hates it, but they haven't seen it and they have generally no idea. It is entirely doable in perl5 only. I got 2-6x speedups in prototypes. But then, perl6 has so much more features worth switching over.
[1] There were a few talks on youtube about "advanced python programming" who were about similar ideas.
But for many people, it also has nicer syntax :-)
Do you have links about influnce of CLOS MOP in Perl6 ? For what it's worth I do love Perl6 efforts like first class grammar/rules.
In addition, Perl 5's Moose[5] object system (and relatives Moo, Mouse, etc) are also heavily influenced by Perl 6, CLOS and Smalltalk.
1: http://irclog.perlgeek.de/perl6/search/?nick=&q=clos
2: http://doc.perl6.org/language/mop
The P6 project assumed it's possible to create hygienic lisp style macros without homoiconicity. In fact it's always been known that it's theoretically possible to create lisp style macros without homoiconicity, but it's usually been considered too darned difficult. P6 has definitely not blinked in the face of "difficult" but it currently looks likely that they'll fail to get there (strong lisp style macro features) this year. See 007 for the experimental sandbox created this year by the lead macro designer:
(And maybe enjoy his toy lisp in P6: https://github.com/masak/ipso)
> Do you have links about influnce of CLOS MOP in Perl6 ? For what it's worth I do love Perl6 efforts like first class grammar/rules.
Here's an email written by Stevan Little in 2005. He was the lead designer of key elements of P6' OO, and the creator of Moose, the leading OO system in P5:
> I also borrowed many ideas from CLOS (in particular from book "The Art of the MetaObject Protocol"). CLOS is more like what you describe, where standard-class is the metaobject to define classes. I see this as mapping to the MetaClass, and our Class as being something akin to the find-class generic function in CLOS.
There's lots of bits like that scattered around the net.
I'm not sure if 6model was influenced by CLOS and it's technically not part of P6 as I understand it but it seems to be in the same space from an intentional point of view. In combination with P6's OO semantics it means it may well be practical at last to pass full blown objects back and forth among arbitrary OO languages in a high performance way. Again, doc is very hard to come by but here's what I've found:
I think the Rakudo devs implemented hygienic macros first because they were expecting them to be more difficult than non-hygienic.
Jonathan got an honors degree from Cambridge University in CS specializing in compilers etc. If you pay attention to his slides you'll see he's paid to train others to use a variety of languages (all kinda boring ones, except P6 imo, but still). Imo you are underestimating his expertise and the import of what he's saying in his presentation.
The bit that I'm still confused about is how the interleaving of run-time inside compile-time inside run-time inside compile-time etc. contributes to the imposition of compile-time type checking via run-time code and how far that paradigm can go. I'd appreciate you (or anyone else) carefully explaining that bit to me. :)
$ perl -Mstrict -M"feature 'say'" -c morning.pl
Global symbol "$moroning" requires explicit package name at morning.pl line 7.
morning.pl had compilation errors.Such a typo catcher would be a written (I'm thinking of a lisp or lua here) on the editor side, having the information about the variable and being able to query the namespace or locals visible or not visible. The things jonathan did was just to put this into the compiler for the thrills, but it can be easily extracted, extended and customized from the outside, in emacs, vi, eclipse, ... such as jonathan did with his perl6 debugger.