I can see how he can be amazed by simple stuff, like typos etc. The moroning example would be catched by python tools like pyflake, or compilers in case of C or rust or whatever... Still he reserves several slides for something i consider most basic, even in other dynamic languages through tooling (truth be told it wouldn't be caught by the interpreter at "compile time"). It's likely such a surprise for him because perl5 was pretty much not even parseable to tell such things..
I guess, but a lot of IDE's are not even made in the same language the person is programming in, the spell checker is independent from the language. PyCharm is evident of this, a Python IDE coded in Java (and portions in Python, but mostly Java).
Exactly. Normally the debugger or repl provides reflection capabilities to be used by the editor to support warnings, errors and hints, and restructuring tools.
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.
I humbly suggest that you may have missed the point. That was simply an example to illustrate the point, which is on slides 20-22. The rest of the talk expounds on this.
The moroning example gets caught at compile time in perl6 and also in perl5...
$ 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.
> It's likely such a surprise for him because perl5 was pretty much not even parseable to tell such things.
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. :)
This is the best part of Perl 6. I am not familiar with the terminology, but it seems "not just" gradual typing to me.
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 always find it puzzling that people throw away dynamic typing entirely. You can't make decisions about what's best for me based on rules of thumb like "as much static typing as possible," your use cases probably don't match mine if you don't understand why anyone would want dynamic typing.
Time, random, user input, self-modifying self-compiling code, undetermined function results, ...
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.
The reason is simple: Using a statically typed language (particularly one with reflection capabilities), it's trivial to do pretty much anything a dynamic language is capable of doing without extra tooling, perhaps with just a bit more verbosity in code. On the other hand, attempting to simulate static typing with an entirely dynamic language doesn't happen without extra tooling around the compiler.
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.
Perl 6 feels dynamic but aiui its functional pieces are statically typed checked and statically call resolved unless a dynamic constraint is used. (See another of my comments in this thread for more on that.)
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.
Haskell does safe coercions. That was the point of the op's argument, actually, but he mistakenly introduced it first through it's implemenation which, after confirming safety, uses a function called 'unsafeCoerce'.
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.
From the static typing POV, the dualism is imaginary because gradual types are a perfectly acceptable and long known static type system. This is the real point of the "unityped" argument.
Right. I think it's reasonable to say that all P6 code is statically typed, using nominal typing.[1]
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
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.
The idea of trying to move subset types forward is interesting. Using an SMT solver [0] you can move quite a bit forward. Otherwise, generally, you're talking about associating a dictionary of pre- and post-conditions with a type (e.g., Weekday is a new nominal type isomorphic with Day, but it also has an associated dict containing checks that it's within Mon-Fri). Carrying that dict around has been well-solved by Haskell's typeclass compilation machinery and then it's just a matter of inserting the checks at the right times and not more often, I suppose.
Perhaps you're aware that one of the biggest influences on P6 was Haskell and the 2005-2007 Audrey Tang / Pugs / lambdacamels era. Loads of crazy smart kids sharing wild thoughts with Larry, Damian etc. and writing the first P6 compiler in Haskell.
I'm sure that's not the case; instead, normally, it's an intentional distinction between the kind of things that "static types" are and the kind of things that "dynamic types" are. Merely because they share an informal descriptor doesn't make them comparable.
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.
I'm not sure what you're arguing. If you're trying to equate "dynamic types" with "(static) types" then I'd like to see how you're doing that. If you're trying to refute that "dynamic types" are "tags" then I'd like to hear your definition of "tags" which you feel inappropriately characterizes "dynamic types".
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.
In the type community dynamic types are called tags, to contrast from static types which are thrown away after compile-time.
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.
Because they've been investigating MetaObjects (and MetaObject Protocols) since the 80s. Sorry for the smugness, my argument boils down to "Don't reinvent the wheel too much".
I can't seem to locate definitive sources at the moment, but it's mentioned often[1]. My general understanding of it is that the MOP is heavily influenced by CLOS, and the object system heavily influenced by the Smalltalk traits paper, which may itself have heavy CLOS influences. The easiest way to figure out just how similar they are may be to look at the Perl 6 docs on both the MOP[2] and Objects[3]. There's also the synopsis[4] documents which guide the design.
In addition, Perl 5's Moose[5] object system (and relatives Moo, Mouse, etc) are also heavily influenced by Perl 6, CLOS and Smalltalk.
They deliberately forgot to steal the syntactic part. :)
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:
> 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:
What makes you so sure Perl 6 is reinventing, rather than using, building upon, and combining with other language features? For as much as you're expounding on your point, your argument might as well be "Why not use C? C has been using types for decades." You'll have a to draw a meaningful comparison before it's worth most people's time.
Most languages played with ideas of types. MetaLevel programming is still a very small niche. I should be happy in fact, I just realized, whatever the vessel as long as the idea goes on.
Perl 5 has a long history of metaprogramming (obviously not as long as Lisp). This became much easier with WRT objects when Class::MOP[1] was introduced. Perl 6 doubled down.
It might be worth mentioning that I'm working on a similar type system for perl5 for quite some time, which is efficient like perl6. In contrast to Moose, which is inefficient and more about the MOP. Types there are only used to slow things down, not to speed it up. All these things were developed around 2002, just forgotten and then partially destroyed with 5.10 and ongoing.
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.
45 comments
[ 2.8 ms ] story [ 106 ms ] threadSuch 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.
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. :)
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 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.
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:
* 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:
* Not yet implemented but due to land soon: * And for calls: * Optional explicit types enable multisub dispatch: * 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:
* 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.
[1] http://en.wikipedia.org/wiki/Nominal_type_system
[0] http://goto.ucsd.edu/~rjhala/liquid/haskell/blog/about/
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.
[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
3: http://doc.perl6.org/language/classtut
4: http://design.perl6.org/
5: http://moose.iinteractive.com/en/
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:
https://github.com/masak/007
http://masak.github.io/007/
(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:
https://github.com/jnthn/6model/blob/master/overview.pod
I think the Rakudo devs implemented hygienic macros first because they were expecting them to be more difficult than non-hygienic.
1: https://metacpan.org/pod/Class::MOP
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.