├── LICENSE ├── MANIFEST ├── MANIFEST.skip ├── Makefile.PL ├── README.md ├── bin ├── generate_rfc.pl └── type-bench.pl ├── config └── rfcs ├── lib ├── Corinna │ ├── Example │ │ └── Person.pm │ └── RFC │ │ ├── Config │ │ └── Reader.pm │ │ ├── Role │ │ └── File.pm │ │ └── Writer.pm ├── Object │ ├── Types.pm │ └── Types │ │ ├── Factory.pm │ │ ├── Moo.pm │ │ ├── Moo │ │ └── Factory.pm │ │ ├── Moose.pm │ │ └── Moose │ │ └── Factory.pm └── YAML │ └── Tiny.pm ├── pod └── perlclasstut.pod ├── rfc ├── attributes.md ├── class-construction.md ├── classes.md ├── faq.md ├── grammar.md ├── major-changes.md ├── method-modifiers.md ├── methods.md ├── mvp.md ├── overview.md ├── phasers.md ├── questions.md ├── quotes.md ├── roles.md └── toc.md ├── t ├── corinna │ ├── example │ │ └── person.t │ └── rfc │ │ ├── config │ │ └── reader.t │ │ └── role │ │ └── file.t ├── object │ ├── types.t │ └── types │ │ ├── factory.t │ │ ├── moo │ │ └── factory.t │ │ └── moose │ │ └── factory.t └── test.conf └── templates ├── README.md └── rfc ├── attributes.md ├── class-construction.md ├── classes.md ├── faq.md ├── grammar.md ├── major-changes.md ├── method-modifiers.md ├── methods.md ├── mvp.md ├── overview.md ├── phasers.md ├── questions.md ├── quotes.md ├── roles.md └── toc.md /LICENSE: -------------------------------------------------------------------------------- 1 | The Artistic License 2.0 2 | 3 | Copyright (c) 2000-2006, The Perl Foundation. 4 | 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | This license establishes the terms under which a given free software 11 | Package may be copied, modified, distributed, and/or redistributed. 12 | The intent is that the Copyright Holder maintains some artistic 13 | control over the development of that Package while still keeping the 14 | Package available as open source and free software. 15 | 16 | You are always permitted to make arrangements wholly outside of this 17 | license directly with the Copyright Holder of a given Package. If the 18 | terms of this license do not permit the full use that you propose to 19 | make of the Package, you should contact the Copyright Holder and seek 20 | a different licensing arrangement. 21 | 22 | Definitions 23 | 24 | "Copyright Holder" means the individual(s) or organization(s) 25 | named in the copyright notice for the entire Package. 26 | 27 | "Contributor" means any party that has contributed code or other 28 | material to the Package, in accordance with the Copyright Holder's 29 | procedures. 30 | 31 | "You" and "your" means any person who would like to copy, 32 | distribute, or modify the Package. 33 | 34 | "Package" means the collection of files distributed by the 35 | Copyright Holder, and derivatives of that collection and/or of 36 | those files. A given Package may consist of either the Standard 37 | Version, or a Modified Version. 38 | 39 | "Distribute" means providing a copy of the Package or making it 40 | accessible to anyone else, or in the case of a company or 41 | organization, to others outside of your company or organization. 42 | 43 | "Distributor Fee" means any fee that you charge for Distributing 44 | this Package or providing support for this Package to another 45 | party. It does not mean licensing fees. 46 | 47 | "Standard Version" refers to the Package if it has not been 48 | modified, or has been modified only in ways explicitly requested 49 | by the Copyright Holder. 50 | 51 | "Modified Version" means the Package, if it has been changed, and 52 | such changes were not explicitly requested by the Copyright 53 | Holder. 54 | 55 | "Original License" means this Artistic License as Distributed with 56 | the Standard Version of the Package, in its current version or as 57 | it may be modified by The Perl Foundation in the future. 58 | 59 | "Source" form means the source code, documentation source, and 60 | configuration files for the Package. 61 | 62 | "Compiled" form means the compiled bytecode, object code, binary, 63 | or any other form resulting from mechanical transformation or 64 | translation of the Source form. 65 | 66 | 67 | Permission for Use and Modification Without Distribution 68 | 69 | (1) You are permitted to use the Standard Version and create and use 70 | Modified Versions for any purpose without restriction, provided that 71 | you do not Distribute the Modified Version. 72 | 73 | 74 | Permissions for Redistribution of the Standard Version 75 | 76 | (2) You may Distribute verbatim copies of the Source form of the 77 | Standard Version of this Package in any medium without restriction, 78 | either gratis or for a Distributor Fee, provided that you duplicate 79 | all of the original copyright notices and associated disclaimers. At 80 | your discretion, such verbatim copies may or may not include a 81 | Compiled form of the Package. 82 | 83 | (3) You may apply any bug fixes, portability changes, and other 84 | modifications made available from the Copyright Holder. The resulting 85 | Package will still be considered the Standard Version, and as such 86 | will be subject to the Original License. 87 | 88 | 89 | Distribution of Modified Versions of the Package as Source 90 | 91 | (4) You may Distribute your Modified Version as Source (either gratis 92 | or for a Distributor Fee, and with or without a Compiled form of the 93 | Modified Version) provided that you clearly document how it differs 94 | from the Standard Version, including, but not limited to, documenting 95 | any non-standard features, executables, or modules, and provided that 96 | you do at least ONE of the following: 97 | 98 | (a) make the Modified Version available to the Copyright Holder 99 | of the Standard Version, under the Original License, so that the 100 | Copyright Holder may include your modifications in the Standard 101 | Version. 102 | 103 | (b) ensure that installation of your Modified Version does not 104 | prevent the user installing or running the Standard Version. In 105 | addition, the Modified Version must bear a name that is different 106 | from the name of the Standard Version. 107 | 108 | (c) allow anyone who receives a copy of the Modified Version to 109 | make the Source form of the Modified Version available to others 110 | under 111 | 112 | (i) the Original License or 113 | 114 | (ii) a license that permits the licensee to freely copy, 115 | modify and redistribute the Modified Version using the same 116 | licensing terms that apply to the copy that the licensee 117 | received, and requires that the Source form of the Modified 118 | Version, and of any works derived from it, be made freely 119 | available in that license fees are prohibited but Distributor 120 | Fees are allowed. 121 | 122 | 123 | Distribution of Compiled Forms of the Standard Version 124 | or Modified Versions without the Source 125 | 126 | (5) You may Distribute Compiled forms of the Standard Version without 127 | the Source, provided that you include complete instructions on how to 128 | get the Source of the Standard Version. Such instructions must be 129 | valid at the time of your distribution. If these instructions, at any 130 | time while you are carrying out such distribution, become invalid, you 131 | must provide new instructions on demand or cease further distribution. 132 | If you provide valid instructions or cease distribution within thirty 133 | days after you become aware that the instructions are invalid, then 134 | you do not forfeit any of your rights under this license. 135 | 136 | (6) You may Distribute a Modified Version in Compiled form without 137 | the Source, provided that you comply with Section 4 with respect to 138 | the Source of the Modified Version. 139 | 140 | 141 | Aggregating or Linking the Package 142 | 143 | (7) You may aggregate the Package (either the Standard Version or 144 | Modified Version) with other packages and Distribute the resulting 145 | aggregation provided that you do not charge a licensing fee for the 146 | Package. Distributor Fees are permitted, and licensing fees for other 147 | components in the aggregation are permitted. The terms of this license 148 | apply to the use and Distribution of the Standard or Modified Versions 149 | as included in the aggregation. 150 | 151 | (8) You are permitted to link Modified and Standard Versions with 152 | other works, to embed the Package in a larger work of your own, or to 153 | build stand-alone binary or bytecode versions of applications that 154 | include the Package, and Distribute the result without restriction, 155 | provided the result does not expose a direct interface to the Package. 156 | 157 | 158 | Items That are Not Considered Part of a Modified Version 159 | 160 | (9) Works (including, but not limited to, modules and scripts) that 161 | merely extend or make use of the Package, do not, by themselves, cause 162 | the Package to be a Modified Version. In addition, such works are not 163 | considered parts of the Package itself, and are not subject to the 164 | terms of this license. 165 | 166 | 167 | General Provisions 168 | 169 | (10) Any use, modification, and distribution of the Standard or 170 | Modified Versions is governed by this Artistic License. By using, 171 | modifying or distributing the Package, you accept this license. Do not 172 | use, modify, or distribute the Package, if you do not accept this 173 | license. 174 | 175 | (11) If your Modified Version has been derived from a Modified 176 | Version made by someone other than you, you are nevertheless required 177 | to ensure that your Modified Version complies with the requirements of 178 | this license. 179 | 180 | (12) This license does not grant you the right to use any trademark, 181 | service mark, tradename, or logo of the Copyright Holder. 182 | 183 | (13) This license includes the non-exclusive, worldwide, 184 | free-of-charge patent license to make, have made, use, offer to sell, 185 | sell, import and otherwise transfer the Package with respect to any 186 | patent claims licensable by the Copyright Holder that are necessarily 187 | infringed by the Package. If you institute patent litigation 188 | (including a cross-claim or counterclaim) against any party alleging 189 | that the Package constitutes direct or contributory patent 190 | infringement, then this Artistic License to you shall terminate on the 191 | date that such litigation is filed. 192 | 193 | (14) Disclaimer of Warranty: 194 | THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS 195 | IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED 196 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR 197 | NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL 198 | LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL 199 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 200 | DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF 201 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 202 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | bin/generate_rfc.pl 2 | config/rfcs 3 | lib/Corinna/RFC/Config/Reader.pm 4 | lib/Corinna/RFC/Role/File.pm 5 | lib/Corinna/RFC/Types.pm 6 | lib/Corinna/RFC/Writer.pm 7 | Makefile.PL 8 | MANIFEST 9 | MYMETA.json 10 | MYMETA.yml 11 | README.md 12 | rfc/attributes.md 13 | rfc/class-construction.md 14 | rfc/classes.md 15 | rfc/grammar.md 16 | rfc/major-changes.md 17 | rfc/methods.md 18 | rfc/overview.md 19 | rfc/phasers.md 20 | rfc/questions.md 21 | rfc/quotes.md 22 | rfc/roles.md 23 | rfc/toc.md 24 | t/corinna/rfc/config/reader.t 25 | t/corinna/rfc/role/file.t 26 | t/test.conf 27 | templates/README.md 28 | templates/rfc/attributes.md 29 | templates/rfc/class-construction.md 30 | templates/rfc/classes.md 31 | templates/rfc/grammar.md 32 | templates/rfc/major-changes.md 33 | templates/rfc/methods.md 34 | templates/rfc/overview.md 35 | templates/rfc/phasers.md 36 | templates/rfc/questions.md 37 | templates/rfc/quotes.md 38 | templates/rfc/roles.md 39 | templates/rfc/toc.md 40 | -------------------------------------------------------------------------------- /MANIFEST.skip: -------------------------------------------------------------------------------- 1 | # Version control files and dirs. 2 | ^.git 3 | 4 | # Makemaker generated files and dirs. 5 | ^MANIFEST\. 6 | ^Makefile$ 7 | ^blib/ 8 | ^MakeMaker-\d 9 | 10 | # Temp, old and emacs backup files. 11 | ~$ 12 | \.swp$ 13 | ^#.*#$ 14 | ^\.# 15 | -------------------------------------------------------------------------------- /Makefile.PL: -------------------------------------------------------------------------------- 1 | use 5.006; 2 | use strict; 3 | use warnings; 4 | use ExtUtils::MakeMaker; 5 | 6 | my %WriteMakefileArgs = ( 7 | NAME => 'Corinna::RFC::Writer', 8 | AUTHOR => q{Curtis "Ovid" Poe }, 9 | VERSION_FROM => 'lib/Corinna/RFC/Writer.pm', 10 | ABSTRACT_FROM => 'lib/Corinna/RFC/Writer.pm', 11 | LICENSE => 'artistic_2', 12 | MIN_PERL_VERSION => '5.026', 13 | CONFIGURE_REQUIRES => { 14 | 'ExtUtils::MakeMaker' => '0', 15 | }, 16 | TEST_REQUIRES => { 17 | 'Test::Most' => '0.37', 18 | 'Keyword::DEVELOPMENT' => '0.07', 19 | 'YAML::Tiny' => '0', 20 | }, 21 | PREREQ_PM => { 22 | 'DateTime' => 0, 23 | 'Hash::Ordered' => 0, 24 | 'Object::Pad' => '0.801', 25 | 'Moose' => '2.2014', 26 | 'Moo' => '2.005003', 27 | 'Syntax::Keyword::Try' => '0.25', 28 | 'Template::Tiny::Strict' => '1.18', 29 | 'Regexp::Common' => '2017060201', 30 | 'Type::Tiny' => 0, 31 | }, 32 | dist => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', }, 33 | clean => { FILES => 'Corinna-RFC-Writer-*' }, 34 | test => { 35 | TESTS => 36 | 't/*.t t/*/*.t t/*/*/*.t t/*/*/*/*.t t/*/*/*/*/*.t t/*/*/*/*/*/*.t' 37 | } 38 | ); 39 | 40 | # Compatibility with old versions of ExtUtils::MakeMaker 41 | unless ( eval { ExtUtils::MakeMaker->VERSION('6.64'); 1 } ) { 42 | my $test_requires = delete $WriteMakefileArgs{TEST_REQUIRES} || {}; 43 | @{ $WriteMakefileArgs{PREREQ_PM} }{ keys %$test_requires } = 44 | values %$test_requires; 45 | } 46 | 47 | unless ( eval { ExtUtils::MakeMaker->VERSION('6.55_03'); 1 } ) { 48 | my $build_requires = delete $WriteMakefileArgs{BUILD_REQUIRES} || {}; 49 | @{ $WriteMakefileArgs{PREREQ_PM} }{ keys %$build_requires } = 50 | values %$build_requires; 51 | } 52 | 53 | delete $WriteMakefileArgs{CONFIGURE_REQUIRES} 54 | unless eval { ExtUtils::MakeMaker->VERSION('6.52'); 1 }; 55 | delete $WriteMakefileArgs{MIN_PERL_VERSION} 56 | unless eval { ExtUtils::MakeMaker->VERSION('6.48'); 1 }; 57 | delete $WriteMakefileArgs{LICENSE} 58 | unless eval { ExtUtils::MakeMaker->VERSION('6.31'); 1 }; 59 | 60 | WriteMakefile(%WriteMakefileArgs); 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Corinna 2 | 3 | ## RFC 4 | 5 | **This file is automatically generated. If you wish to submit a PR, do not 6 | edit this file directly. Please edit 7 | [templates/README.md](https://github.com/Ovid/Cor/tree/master/templates/README.md) instead.** 8 | 9 | --- 10 | 11 | # Bringing Modern OOP to the Perl Core 12 | 13 | ```perl 14 | class Cache::LRU v0.1.0 { 15 | use Hash::Ordered; 16 | use Carp 'croak'; 17 | 18 | field $num_caches :common = 0; 19 | field $cache :handles(exists delete) = Hash::Ordered->new; 20 | field $max_size :param :reader = 20; 21 | field $created :reader = time; 22 | 23 | ADJUST { # called after new() 24 | $num_caches++; 25 | if ( $max_size < 1 ) { 26 | croak(...); 27 | } 28 | } 29 | DESTRUCT { $num_caches-- } 30 | 31 | method num_caches :common () { $num_caches } 32 | 33 | method set( $key, $value ) { 34 | $cache->unshift( $key, $value ); # new values in front 35 | if ( $cache->keys > $max_size ) { 36 | $cache->pop; 37 | } 38 | } 39 | 40 | method get($key) { 41 | return unless $cache->exists($key); 42 | my $value = $cache->get($key); 43 | $self->unshift( $key, $value ); # put it at the front 44 | return $value; 45 | } 46 | } 47 | ``` 48 | 49 | This repository is to track the RFC for the Corinna MVP OOP proposal. 50 | 51 | 1. [Table of Contents](rfc/toc.md) 52 | 2. [Overview](rfc/overview.md) 53 | 3. [Grammar](rfc/grammar.md) 54 | 4. [Classes](rfc/classes.md) 55 | 5. [Class Construction](rfc/class-construction.md) 56 | 6. [Fields](rfc/attributes.md) 57 | 7. [Methods](rfc/methods.md) 58 | 8. [Roles](rfc/roles.md) 59 | 9. [Phasers](rfc/phasers.md) 60 | 10. [Method Modifiers](rfc/method-modifiers.md) 61 | 11. [Questions](rfc/questions.md) 62 | 12. [Quotes](rfc/quotes.md) 63 | 13. [Changes](rfc/major-changes.md) 64 | 14. [P5P MVP](rfc/mvp.md) 65 | 15. [FAQ](rfc/faq.md) 66 | 67 | 68 | **Important**: All of the above represent works in progress. Please do not 69 | consider this the final RFC. We're writing down the shell of the RFC and will 70 | fine-tune. Anything in the Wiki should be considered "rough drafts." 71 | 72 | ## Section Numbers 73 | 74 | As you navigate the various RFC pages, you'll note that it's broken down into 75 | fine-grained section numbers such as 3.2.2. Prior to the RFC being filed, 76 | these are fluid and will likely change without warning. After the RFC is 77 | filed, we may make minor changes to the sections, but the section numbers 78 | themselves should be frozen to make it easy to refer to a given section. 79 | 80 | ## Not a Tutorial 81 | 82 | This is not a tutorial on OO programming. That could easily fill a book. It's 83 | assumed you're already very familiar with Perl's built-in OO. It's very useful 84 | if you're also familiar with Moo/se. 85 | 86 | However, [here's a basic tutorial for Corinna if you'd like to skip the 87 | detail](https://github.com/Ovid/Cor/blob/master/pod/perlclasstut.pod). 88 | 89 | ## Principle of Parsimony 90 | 91 | Many things in the proposal are _deliberately_ restrictive, such as Corinna 92 | only allowing single inheritance. This is to allow Corinna to be cautious in 93 | not promising too much. If we later find this too restrictive, we can allow 94 | multiple inheritance. However, if we start with multiple inheritance and 95 | discover we don't need or want multiple inheritance, we would break existing 96 | code by taking it away. 97 | 98 | Any proposals to change the RFC must consider the principle of parsimony. 99 | 100 | **Note**: it's been brought to my attention that the [Principle of 101 | Parsimony](https://www.oxfordreference.com/display/10.1093/oi/authority.20110803100346221) 102 | is a phrase used in biology and has a different meaning. Oops. 103 | 104 | # KIM 105 | 106 | We are trying to adhere to the 107 | [KIM](https://ovid.github.io/articles/language-design-consistency.html) 108 | principle. In short, features should try to follow this syntax: 109 | 110 | ``` 111 | KEYWORD IDENTIFIER MODIFIER? SETUP? 112 | ``` 113 | 114 | So instead of this: 115 | 116 | ```perl 117 | class Foo isa Bar does SomeRole v1.2.3 { 118 | overrides method some_method() { 119 | ... 120 | } 121 | } 122 | ``` 123 | 124 | We're doing this: 125 | 126 | ```perl 127 | class Foo :isa(Bar) :does(SomeRole) :version(v1.2.3) { 128 | method some_method :overrides () { 129 | ... 130 | } 131 | } 132 | ``` 133 | 134 | By trying to have all syntax arranged like this we reduce the core of Corinna 135 | down to four new keywords: 136 | 137 | * `class` 138 | * `role` 139 | * `field` 140 | * `method` 141 | 142 | Everything which tweaks those behaviors should be a modifier. 143 | 144 | This part is still a work in progress and may evolve. 145 | 146 | ## This Repository 147 | 148 | This repository is not for code. Instead, it’s to have a central place to 149 | discuss the Corinna proposal to bring modern OO to the core of the Perl 150 | language. 151 | 152 | You may be looking for the [Wiki](https://github.com/Ovid/Cor/wiki) as that 153 | has much historical discussion of this project. However, the documents in the 154 | `rfc/` folder will be considered the canonical ones. 155 | 156 | [You can file issues](https://github.com/Ovid/Cor/issues) to address your 157 | particular concerns and get feedback. 158 | 159 | ## MVP 160 | 161 | This work is to drive us to v0.1.0—not v1.0.0—the “minimum viable product.” The 162 | intent is to get the subset of features we really need into the language, make 163 | sure it’s stable, and then iterate on top of that. I don’t want us building on 164 | top of this foundation only to discover the foundation is not stable. 165 | 166 | I’ve been getting some pushback from people being quite insistent that if I 167 | don‘t support one or more of the features that _they_ like to use, that it will 168 | be so terribly hobbled that it will be of limited use to them. 169 | 170 | Please keep in mind that if this MVP does get into core, I will have lost 171 | control over it (yay!) and I’ll have no particularly greater voice than others. 172 | That means: 173 | 174 | **If this gets into the core and you want a feature, lobby for it.** 175 | 176 | I’m not a gatekeeper. I don’t want to be a gatekeeper. I want to get modern OO 177 | into the Perl core and the more things added to the MVP to scratch people’s 178 | personal itches, the more bugs there will be and the less likely it is to be 179 | stable. Let’s focus on the MVP and getting this in core. 180 | 181 | After that, it will be up to the community to decide where it wants to go. I 182 | will have _zero_ authority to stop things I disagree with if P5P decides to 183 | implement them. This will effectively be a democracy. A level playing field. 184 | If you won’t support Corinna because your personal favorite feature isn’t in 185 | the MVP, I don’t know what to say to that. You, like everyone else, will be 186 | able to lobby for that feature. 187 | 188 | # DEDICATION 189 | 190 | This project is dedicated to the memory of both Jeff Goff and David Adler, 191 | two Perl developers who also loved Raku and worked to create a better 192 | language. They were both wonderful people and will be missed. 193 | -------------------------------------------------------------------------------- /bin/generate_rfc.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use lib 'lib'; 4 | use strict; 5 | use warnings; 6 | use Corinna::RFC::Writer; 7 | 8 | my $writer = Corinna::RFC::Writer->new( file => 'config/rfcs', verbose => 1 ); 9 | $writer->generate_rfcs; 10 | 11 | __END__ 12 | 13 | =head1 NAME 14 | 15 | bin/generate_rfc.pl - regenerate the Corinna RFC 16 | 17 | =head1 SYNOPSIS 18 | 19 | perl bin/generate_rfc.pl 20 | 21 | =head1 DESCRIPTION 22 | 23 | This program reads the `config/rfcs` to determine what files in the 24 | `templates` directory should be read and written out as part of the full RFCs. 25 | It reads each of the files in the `templates/rfc` directory and will rewrite 26 | each markdown `^#` header with a corresponding section number such as `3.2.3`. 27 | This will make it easier, later, to refer to different portions of the RFC 28 | when it's presented. 29 | 30 | -------------------------------------------------------------------------------- /bin/type-bench.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use 5.26.0; 4 | use warnings; 5 | use lib 'lib'; 6 | use YAML::Tiny; 7 | use Benchmark 'cmpthese'; 8 | 9 | my $sample_data = YAML::Tiny->read_string( yaml() )->[0]; 10 | my $o_pad = Object::Pad::Test->get_definition(); 11 | my $moose = Moose::Test->get_definition(); 12 | my $moo = Moo::Test->get_definition(); 13 | my $types_standard = Types::Standard::Test->get_definition(); 14 | 15 | cmpthese( 16 | 50_000, 17 | { 18 | 'Object::Pad' => sub { $o_pad->validate($sample_data) }, 19 | 'Moose' => sub { $moose->validate($sample_data) }, 20 | 'Moo' => sub { $moo->validate($sample_data) }, 21 | 'Types::Standard' => sub { $types_standard->($sample_data) }, 22 | 'O:P/construct' => sub { Object::Pad::Test->get_definition->validate($sample_data) }, 23 | 'Moose/construct' => sub { Moose::Test->get_definition->validate($sample_data) }, 24 | 'Moo/construct' => sub { Moo::Test->get_definition->validate($sample_data) }, 25 | 'T:S/construct' => sub { Types::Standard::Test->get_definition->($sample_data) }, 26 | } 27 | ); 28 | 29 | sub yaml { 30 | my $yaml = <<~'END'; 31 | doe: "a deer, a female deer" 32 | ray: "a drop of golden sun" 33 | pi: 3.14159 34 | xmas: true 35 | french-hens: 3 36 | calling-birds: 37 | - huey 38 | - dewey 39 | - louie 40 | - fred 41 | xmas-fifth-day: 42 | calling-birds: four 43 | french-hens: 3 44 | golden-rings: 5 45 | partridges: 46 | count: 1 47 | location: "a pear tree" 48 | turtle-doves: two 49 | END 50 | } 51 | 52 | package Object::Pad::Test { 53 | use Object::Types ':all'; 54 | 55 | sub get_definition { 56 | Dict( 57 | doe => Str, 58 | ray => Str, 59 | pi => Num, 60 | xmas => Enum(qw/true false/), 61 | 'french-hens' => Int, 62 | 'calling-birds' => ArrayRef( Enum(qw/huey dewey louie fred/) ), 63 | 'xmas-fifth-day' => Dict( 64 | 'calling-birds' => Str, 65 | 'french-hens' => Int, 66 | 'golden-rings' => Int, 67 | partridges => Dict( 68 | count => Int, 69 | location => Str, 70 | 'lords-a-leaping' => Optional(Int), 71 | ), 72 | 'turtle-doves' => Str, 73 | ) 74 | ); 75 | } 76 | } 77 | 78 | package Moose::Test { 79 | use Object::Types::Moose ':all'; 80 | 81 | sub get_definition { 82 | Dict( 83 | doe => Str, 84 | ray => Str, 85 | pi => Num, 86 | xmas => Enum(qw/true false/), 87 | 'french-hens' => Int, 88 | 'calling-birds' => ArrayRef( Enum(qw/huey dewey louie fred/) ), 89 | 'xmas-fifth-day' => Dict( 90 | 'calling-birds' => Str, 91 | 'french-hens' => Int, 92 | 'golden-rings' => Int, 93 | partridges => Dict( 94 | count => Int, 95 | location => Str, 96 | 'lords-a-leaping' => Optional(Int), 97 | ), 98 | 'turtle-doves' => Str, 99 | ) 100 | ); 101 | } 102 | } 103 | 104 | package Moo::Test { 105 | use Object::Types::Moo ':all'; 106 | 107 | sub get_definition { 108 | Dict( 109 | doe => Str, 110 | ray => Str, 111 | pi => Num, 112 | xmas => Enum(qw/true false/), 113 | 'french-hens' => Int, 114 | 'calling-birds' => ArrayRef( Enum(qw/huey dewey louie fred/) ), 115 | 'xmas-fifth-day' => Dict( 116 | 'calling-birds' => Str, 117 | 'french-hens' => Int, 118 | 'golden-rings' => Int, 119 | partridges => Dict( 120 | count => Int, 121 | location => Str, 122 | 'lords-a-leaping' => Optional(Int), 123 | ), 124 | 'turtle-doves' => Str, 125 | ) 126 | ); 127 | } 128 | } 129 | 130 | package Types::Standard::Test { 131 | use Types::Standard qw( Str Int Enum ArrayRef Dict Num Optional ); 132 | use Type::Params qw( compile ); 133 | 134 | sub get_definition { 135 | compile( 136 | Dict [ 137 | doe => Str, 138 | ray => Str, 139 | pi => Num, 140 | xmas => Enum [qw/true false/], 141 | 'french-hens' => Int, 142 | 'calling-birds' => 143 | ArrayRef [ Enum [qw/huey dewey louie fred/] ], 144 | 'xmas-fifth-day' => Dict [ 145 | 'calling-birds' => Str, 146 | 'french-hens' => Int, 147 | 'golden-rings' => Int, 148 | partridges => Dict [ 149 | count => Int, 150 | location => Str, 151 | 'lords-a-leaping' => Optional [Int], 152 | ], 153 | 'turtle-doves' => Str, 154 | ] 155 | ] 156 | ); 157 | } 158 | } 159 | 160 | __END__ 161 | 162 | =head1 NAME 163 | 164 | bin/type-bench.pl - Benchmarks for Object::Pad (and ultimately, Corinna) 165 | 166 | =head1 SYNOPSIS 167 | 168 | $ time perl bin/type-bench.pl 169 | Rate T:S/construct Moose/construct Moo/construct O:P/construct Moose Object::Pad Moo Types::Standard 170 | T:S/construct 716/s -- -66% -78% -78% -82% -86% -87% -99% 171 | Moose/construct 2098/s 193% -- -35% -37% -48% -59% -60% -97% 172 | Moo/construct 3218/s 350% 53% -- -3% -20% -38% -39% -96% 173 | O:P/construct 3327/s 365% 59% 3% -- -17% -35% -37% -96% 174 | Moose 4023/s 462% 92% 25% 21% -- -22% -24% -95% 175 | Object::Pad 5155/s 620% 146% 60% 55% 28% -- -3% -94% 176 | Moo 5302/s 641% 153% 65% 59% 32% 3% -- -93% 177 | Types::Standard 79365/s 10989% 3683% 2367% 2286% 1873% 1440% 1397% -- 178 | 179 | real 3m3.179s 180 | user 2m59.690s 181 | sys 0m1.171s 182 | 183 | =head1 DESCRIPTION 184 | 185 | To get a heavier-duty benchmark than simply creating objects and changing a 186 | few values, we have re-implemented a subset of L in 187 | L, L, and L. In short, this somewhat complex type 188 | constraint is used to validate a data structure 50,000 times: 189 | 190 | Dict [ 191 | doe => Str, 192 | ray => Str, 193 | pi => Num, 194 | xmas => Enum [qw/true false/], 195 | 'french-hens' => Int, 196 | 'calling-birds' => ArrayRef [ Enum [qw/huey dewey louie fred/] ], 197 | 'xmas-fifth-day' => Dict [ 198 | 'calling-birds' => Str, 199 | 'french-hens' => Int, 200 | 'golden-rings' => Int, 201 | partridges => Dict [ 202 | count => Int, 203 | location => Str, 204 | 'lords-a-leaping' => Optional [Int], 205 | ], 206 | 'turtle-doves' => Str, 207 | ] 208 | ]; 209 | 210 | Unsurprisingly, C wins, hands down. However, we see 211 | C is considerably faster than both C and C if you 212 | include object construction. Otherwise, C and C are about 213 | neck and neck as of this writing (September 10, 2021); 214 | 215 | If someone wants to contribute a C version to compare against, that 216 | would be interesting. 217 | 218 | There are tests for behavior in the C directory. They have been 219 | cut-n-pasted, but could stand to be rewritten into a single test file. 220 | -------------------------------------------------------------------------------- /config/rfcs: -------------------------------------------------------------------------------- 1 | # see Corinna::RFC::Config::Reader for syntax 2 | [@rfcs] ; the @ means 'preserve the order of these values 3 | Overview=overview.md 4 | Grammar=grammar.md 5 | Classes=classes.md 6 | Class Construction=class-construction.md 7 | Fields=attributes.md 8 | Methods=methods.md 9 | Roles=roles.md 10 | Phasers=phasers.md 11 | Method Modifiers=method-modifiers.md 12 | Questions=questions.md 13 | Quotes=quotes.md 14 | Changes=major-changes.md 15 | P5P MVP=mvp.md 16 | FAQ=faq.md 17 | 18 | [main] ; k/v pairs: $config->{main}{readme} = README.md 19 | template_dir=templates ; where the templates are stored 20 | rfc_dir=rfc ; where the rfcs will be saved 21 | readme=README.md ; name of the README.md file 22 | toc=toc.md ; name we'll use for our table of contents file 23 | toc_marker={{TOC}} ; the marker in the toc file for post-process insertion of table of contents 24 | github=https://github.com/Ovid/Cor ; url of this repo 25 | -------------------------------------------------------------------------------- /lib/Corinna/Example/Person.pm: -------------------------------------------------------------------------------- 1 | use Object::Pad 0.56; 2 | 3 | class Corinna::Example::Person { 4 | use Time::HiRes 'time'; 5 | 6 | # TODO Rename `has` to `field` 7 | field $name :param; # must be passed to customer (:param) 8 | field $title :param = undef; # optionally passed to constructor (:param, but with default) 9 | field $created :reader; # cannot be passed to constructor (no :param) 10 | 11 | my $num_people = 0; # class data, defaults to 0 (common, with hand-rolled reader method) 12 | 13 | # TODO Add ability to declare class methods: common method num_people () { $num_people } 14 | method num_people () { $num_people } 15 | 16 | ADJUST { 17 | $num_people++; 18 | 19 | # TODO Allow `field $created = time;` (or similar) to allow default at instantiation time 20 | $created = time; 21 | } 22 | 23 | # TODO Implement `DESTRUCT` for a destructor 24 | DESTROY { $num_people-- } # destructor 25 | 26 | method name () { # instance method 27 | return defined $title ? "$title $name" : $name; 28 | } 29 | } 30 | 31 | __END__ 32 | 33 | =head1 NAME 34 | 35 | Corinna::Example::Person - Example of a "Person" class 36 | 37 | =head1 SYNOPSIS 38 | 39 | use Corinna::Example::Person; 40 | my $villain = Person->new( title => 'Dr.', name => 'Zacharary Smith' ); 41 | my $boy = Person->new( name => 'Will Robinson' ); 42 | 43 | say $villain->name; # Dr. Zacharary Smith 44 | say $boy->name; # Will Robinson 45 | say $boy->created; # time() of creation 46 | say $boy->num_people; # how many people objects exist 47 | 48 | Note that C is a class method but we cannot yet call C<< Corinna::Example::Person->num_people >>. 49 | 50 | =head1 SOURCE 51 | 52 | See "4.2 Discussion" in L. 53 | -------------------------------------------------------------------------------- /lib/Corinna/RFC/Config/Reader.pm: -------------------------------------------------------------------------------- 1 | # Forked from Config::Tiny::Ordered 2 | 3 | use v5.26.0; 4 | use lib 'lib'; 5 | use Object::Pad 0.58; 6 | 7 | class Corinna::RFC::Config::Reader :does(Corinna::RFC::Role::File) { 8 | use Syntax::Keyword::Try; 9 | use Storable 'dclone'; 10 | use Object::Types qw(ArrayRef HashRef Str Dict); 11 | use Carp 'croak'; 12 | 13 | field $FILE :param(file); 14 | field $CONFIG = {}; 15 | 16 | BUILD { 17 | $self->_read_string; 18 | $self->_validate; 19 | } 20 | 21 | method config() { 22 | 23 | # by doing this, the caller can mutate $CONFIG to their 24 | # heart's content, but internaly we're "safe" 25 | return dclone($CONFIG); 26 | } 27 | 28 | method _validate() { 29 | state $check = Dict( 30 | rfcs => ArrayRef( Dict( key => Str, value => Str ), ), 31 | main => Dict( 32 | template_dir => Str, 33 | rfc_dir => Str, 34 | readme => Str, 35 | toc => Str, 36 | toc_marker => Str, 37 | github => Str, 38 | ), 39 | ); 40 | try { 41 | $check->validate($CONFIG); 42 | } 43 | catch ($error) { 44 | croak("Config file error: $error"); 45 | } 46 | } 47 | 48 | method _read_string() { 49 | my $config_data = $self->_slurp($FILE); 50 | 51 | my $line_number = 0; 52 | my $namespace = '_'; # Catch-all in case they add extra stuff 53 | $CONFIG->{$namespace} = []; 54 | 55 | LINE: for ( split /\n/, $config_data ) { 56 | $line_number++; 57 | 58 | # Skip comments and empty lines. 59 | next LINE if /^\s*(?:\#|\;|$)/; 60 | 61 | # Remove inline comments. 62 | s/\s\;\s.+$//g; 63 | 64 | # Handle section headers. 65 | if (/^ \s* \[ (?\@)? \s* (?.+?) \s* \] \s*$/x) { 66 | if ( $+{is_list} ) { # they want a list 67 | $CONFIG->{ $namespace = $+{namespace} } ||= []; 68 | } 69 | else { # they want k/v pairs 70 | $CONFIG->{ $namespace = $+{namespace} } ||= {}; 71 | } 72 | next LINE; 73 | } 74 | 75 | # Handle properties. 76 | if (/^ \s* (?[^=]+?) \s* = \s* (?.*?) \s* $/x) { 77 | my $section = $CONFIG->{$namespace}; 78 | if ( 'ARRAY' eq ref $section ) { 79 | push $section->@*, { key => $+{key}, value => $+{value} }; 80 | } 81 | else { 82 | $section->{ $+{key} } = $+{value}; 83 | } 84 | next LINE; 85 | } 86 | die "Syntax error at line $line_number '$_'"; 87 | } 88 | delete $CONFIG->{_} unless $CONFIG->{_}->@*; 89 | } 90 | } 91 | 92 | __END__ 93 | 94 | =pod 95 | 96 | =head1 NAME 97 | 98 | Corinna::RFC::Config::Reader - Immutable object to read the Corinna RFC generator config 99 | 100 | =head1 SYNOPSIS 101 | 102 | In your configuration file: 103 | 104 | # see Corinna::RFC::Config::Reader for syntax 105 | [@rfcs] ; the @ means 'preserve the order of these values 106 | Overview=overview.md 107 | Grammar=grammar.md 108 | Classes=classes.md 109 | Class Construction=class-construction.md 110 | Attributes=attributes.md 111 | Methods=methods.md 112 | Roles=roles.md 113 | Phasers=phasers.md 114 | Questions=questions.md 115 | Quotes=quotes.md 116 | Changes=major-changes.md 117 | 118 | [main] ; k/v pairs: $config->{main}{readme} = README.md 119 | template_dir=templates ; where the templates are stored 120 | rfc_dir=rfc ; where the rfcs will be saved 121 | readme=README.md ; name of the README.md file 122 | toc=toc.md ; name we'll use for our table of contents file 123 | toc_marker={{TOC}} ; the marker in the toc file for post-process insertion of table of contents 124 | github=https://github.com/Ovid/Cor ; url of this repo 125 | 126 | In your program: 127 | 128 | use Corinna::RFC::Config::Reader; 129 | 130 | # Create a config: 131 | my $reader = Corinna::RFC::Config::Reader->new( file => 'file.conf' ); 132 | my $config = $reader->config; 133 | 134 | Your config will contain: 135 | 136 | { 137 | main => { 138 | github => 'https://github.com/Ovid/Cor', 139 | readme => 'README.md', 140 | rfc_dir => 'rfc', 141 | template_dir => 'templates', 142 | toc => 'toc.md', 143 | toc_marker => '{{TOC}}' 144 | }, 145 | rfcs => [ 146 | { key => 'Overview', value => 'overview.md' }, 147 | { key => 'Grammar', value => 'grammar.md' }, 148 | { key => 'Classes', value => 'classes.md' }, 149 | { key => 'Class Construction', value => 'class-construction.md' }, 150 | { key => 'Attributes', value => 'attributes.md' }, 151 | { key => 'Methods', value => 'methods.md' }, 152 | { key => 'Roles', value => 'roles.md' }, 153 | { key => 'Phasers', value => 'phasers.md' }, 154 | { key => 'Questions', value => 'questions.md' }, 155 | { key => 'Quotes', value => 'quotes.md' }, 156 | { key => 'Changes', value => 'major-changes.md' } 157 | ] 158 | }; 159 | 160 | =head1 DESCRIPTION 161 | 162 | C is a perl class to read Corinna RFC .ini style configuration 163 | file. 164 | 165 | This module differs from C in that if there is a data section 166 | whose name begins with an C<@> symbol, the data is stored in memory in the 167 | same order as it appears in the input file or string. 168 | 169 | Futher, there is a grammar for the resulting config data that is defined via 170 | C. 171 | 172 | =head1 CONFIGURATION FILE SYNTAX 173 | 174 | Files are the same format as for windows .ini files. For example: 175 | 176 | [section] 177 | var1=value1 178 | var2=value2 179 | 180 | Lines starting with C<'#'> or C<';'> are considered comments and ignored, 181 | as are blank lines. 182 | 183 | Sections started with an `@` symbol are preserved in order: 184 | 185 | [@rfcs] 186 | Overview=overview.md 187 | Grammar=grammar.md 188 | Classes=classes.md 189 | 190 | =head1 METHODS 191 | 192 | =head2 C $config_file )> 193 | 194 | my $config = Corinna::RFC::Config::Reader->new(file => $file); 195 | 196 | Returns a new C object. 197 | 198 | =head2 C 199 | 200 | Returns a I hashref of the config. Because we clone the data before we return it, 201 | you may call C<< $reader->config >> multiple times and always get the same response. 202 | =head1 Repository 203 | 204 | https://github.com/Ovid/Cor 205 | 206 | =head1 DESIGN NOTES 207 | 208 | This module is rather interesting for new developers in that it: 209 | 210 | =over 4 211 | 212 | =item * Consumes a role (L) 213 | 214 | =item * Is immutable 215 | 216 | =item * The internal C<_validate> method shows how to apply type constraints 217 | 218 | =back 219 | 220 | =head1 SUPPORT 221 | 222 | Bugs should be reported via https://github.com/Ovid/Cor/issues 223 | 224 | =head1 AUTHORS 225 | 226 | Curtis "Ovid" Poe Eovid@allaroundtheworld.frE, based on code by Adam 227 | Kennedy Eadamk@cpan.orgE and Ron Savage Ersavage@cpan.orgE. 228 | 229 | =head1 SEE ALSO 230 | 231 | L, L, L, 232 | L, L, L 233 | 234 | =head1 Copyright and License 235 | 236 | This software is copyright (c) 2021 by Curtis "Ovid" Poe. 237 | 238 | This is free software; you can redistribute it and/or modify it under the same 239 | terms as the Perl 5 programming language system itself. 240 | 241 | =cut 242 | -------------------------------------------------------------------------------- /lib/Corinna/RFC/Role/File.pm: -------------------------------------------------------------------------------- 1 | use Object::Pad 0.58; 2 | 3 | role Corinna::RFC::Role::File { 4 | method _slurp($file) { 5 | open my $fh, '<:encoding(UTF-8)', $file or die "Cannot open $file for reading: $!"; 6 | return do { local $/; <$fh> }; 7 | } 8 | 9 | method _splat( $file, $string ) { 10 | if ( ref $string ) { 11 | croak("Data for splat '$file' must not be a reference ($string)"); 12 | } 13 | open my $fh, '>:encoding(UTF-8)', $file or die "Cannot open $file for writing: $!"; 14 | print {$fh} $string; 15 | } 16 | } 17 | 18 | __END__ 19 | 20 | =head1 NAME 21 | 22 | Corinna::RFC::Role::File - Read and write files 23 | 24 | =head1 SYNOPSIS 25 | 26 | class Foo does Corinna::RFC::Role::File { 27 | ... 28 | } 29 | 30 | 31 | Class C can now use C<_slurp($filename)> and C<_splat($filename, $string)>. Both assume 32 | C<:encoding(UTF-8)>. 33 | 34 | Note that these methods have a leading underscore and should be "private" to 35 | the consuming class, but Perl doesn't yet support that. It would be nice to do this: 36 | 37 | role Some::Role { 38 | trusted method do_something () { 39 | ... 40 | } 41 | } 42 | 43 | And have that method be available for flattening into a consumer, but I available outside 44 | the class. This is similar to C methods in Java. 45 | 46 | It also raises an interesting issue with roles: if a methods exported by a role is public, but 47 | the consumer does not wish to expose that part of its interface, should it have a way to adjust the 48 | access level to C or C? 49 | 50 | =head1 REQUIRES 51 | 52 | Nothing. 53 | 54 | =head1 PROVIDES 55 | 56 | Note: while both methods are class methods, as of C 0.52, we cannot call these methods 57 | on non-instances. 58 | 59 | =head2 C<_slurp($filename)> 60 | 61 | my $contents = $class->_slurp($filename); 62 | 63 | Class method. Reads the entire contents of C<$filename> and returns it. 64 | 65 | =head2 C<_splat($filename, $string)> 66 | 67 | $class->_splat( $filename, $string ); 68 | 69 | Class method. Writes the contents of C<$string> to C<$filename>. 70 | -------------------------------------------------------------------------------- /lib/Object/Types.pm: -------------------------------------------------------------------------------- 1 | package Object::Types; 2 | use 5.26.0; 3 | use warnings; 4 | use experimental 'signatures'; 5 | no warnings 'experimental'; 6 | use Object::Types::Factory; 7 | use base 'Exporter'; 8 | 9 | our @EXPORT_OK = qw( 10 | Any 11 | ArrayRef 12 | Coerce 13 | Dict 14 | Enum 15 | HashRef 16 | Int 17 | Maybe 18 | Num 19 | Optional 20 | Regex 21 | Str 22 | ); 23 | our %EXPORT_TAGS = ( all => \@EXPORT_OK ); 24 | 25 | sub _create (@args) { 26 | return Object::Types::Factory->create(@args); 27 | } 28 | 29 | sub Any () { _create('Any') } 30 | sub Int () { _create('Int') } 31 | sub Num () { _create('Num') } 32 | sub Str () { _create('Str') } 33 | sub Regex ($re) { _create( 'Regex', regex => $re ) } 34 | sub Enum (@elements) { _create( 'Enum', elements => [@elements] ) } 35 | 36 | sub ArrayRef ($type=undef) { 37 | if ( defined $type ) { 38 | _create( 'ArrayRef', contains => $type ); 39 | } 40 | else { 41 | _create('ArrayRef'); 42 | } 43 | } 44 | 45 | sub HashRef(@elements) { 46 | if ( 1 == @elements ) { 47 | _create( 'HashRef', contains => $elements[0] ); 48 | } 49 | elsif ( !@elements ) { 50 | _create('HashRef'); 51 | } 52 | else { 53 | _create( 'HashRef', element_hash => {@elements} ); 54 | } 55 | } 56 | 57 | sub Dict(%elements) { 58 | _create( 'HashRef', element_hash => {%elements}, restricted => 1 ); 59 | } 60 | 61 | sub Maybe($type) { _create( 'Maybe', contains => $type ) } 62 | sub Optional($type) { _create( 'Optional', contains => $type ) } 63 | 64 | sub Coerce ( $type, $code ) { 65 | _create( 'Coerce', contains => $type, via => $code ); 66 | } 67 | 68 | 1; 69 | 70 | __END__ 71 | 72 | =head1 NAME 73 | 74 | Object::Types - Types::Standard-like functions with better error reporting 75 | 76 | =head1 SYNOPSIS 77 | 78 | use Object::Types qw(:all); 79 | my $true_false = Enum(qw/true false/); 80 | my $YAMLBool = Coerce( $true_false, sub { $_[0] eq 'true' ? 1 : 0 } ); 81 | my $data_structure = Dict( 82 | routes => Dict( 83 | requires_proof => $YAMLBool, 84 | list_of_coercions => ArrayRef( ArrayRef($YAMLBool) ), 85 | ), 86 | ); 87 | $data_structure->validate($some_data); 88 | 89 | =head1 DESCRIPTION 90 | 91 | This is a limited subset of a reimplemenation of L due to two 92 | serious issues. First, the error messages in C are almost 93 | impossible to read: 94 | 95 | Reference {"maybe_true" => "true","not_a_hashref" => "asdf"} did not pass type constraint "Dict[maybe_true=>YAMLBool,not_a_hashref=>Optional[HashRef]]" (in $_[0]) at fail.pl line 18 96 | Reference {"maybe_true" => "true","not_a_hashref" => "asdf"} did not pass type constraint "Dict[maybe_true=>YAMLBool,not_a_hashref=>Optional[HashRef]]" (in $_[0]) 97 | "Dict[maybe_true=>YAMLBool,not_a_hashref=>Optional[HashRef]]" constrains value at key "maybe_true" of hash with "YAMLBool" 98 | "YAMLBool" is a subtype of "Bool" 99 | Value "true" did not pass type constraint "Bool" (in $_[0]->{"maybe_true"}) 100 | "Bool" is defined as: (!ref $_ and (!defined $_ or $_ eq q() or $_ eq '0' or $_ eq '1')) 101 | 102 | Second, not only is the above error message very hard to parse (and that's a 103 | simple example), it also turns out to be wrong. See 104 | L for more detail. 105 | 106 | For this code, we can trivially reproduce the error: 107 | 108 | my $true_false = Enum(qw/true false/); 109 | my $YAMLBool = Coerce( $true_false, sub { $_[0] eq 'true' ? 1 : 0 } ); 110 | my $check = Dict( 111 | maybe_true => $YAMLBool, 112 | not_a_hashref => Optional(HashRef), 113 | ); 114 | 115 | my $minimal = { 116 | maybe_true => 'true', 117 | not_a_hashref => 'abcd', 118 | }; 119 | $check->validate($minimal); 120 | 121 | And the error message is both correct and easy to understand: 122 | 123 | Validation for '$data{not_a_hashref}' failed for type HashRef with value 'abcd' 124 | 125 | =head1 LIMITED FUNCTIONALITY 126 | 127 | This mostly mimics basic behavior of L. However, many 128 | features are missing because I only implemented what I needed. 129 | 130 | =head1 FUNCTIONS 131 | 132 | All functions are exportabe on-demand, or via C<:all>. Further, they can be c 133 | composed to arbitrary depths. 134 | 135 | If a call to C fails, the code will C with an 136 | appropriate error message, giving you enough information to diagnose the 137 | error: 138 | 139 | Validation for '$data{routes}[0]{'auth'}' failed for type HashRef with value 'abcd' 140 | 141 | The structure of the failure is: 142 | 143 | Validation for '$path_to_data' failed for type $type with value 'offending value' 144 | 145 | By default, the variable name in the error is C<$data>. You may pass a second 146 | argument to C with any string you desire and that will be used as 147 | the variable names instead: 148 | 149 | $something->validate($json, '$json'); 150 | 151 | =head2 C 152 | 153 | my $any = Any; 154 | $any->validate($something); # always succeeds 155 | 156 | =head2 C 157 | 158 | my $aref = ArrayRef; 159 | $aref->validate( [] ); # good 160 | $aref->validate( \@anything ); # good 161 | $aref->validate(2); # boom! 162 | 163 | my $aref_of_ints = ArrayRef(Int); 164 | $aref->validate( [] ); # good 165 | $aref->validate( [ 2, 3, -1500 ] ); # good! 166 | $aref->validate( ['ovid'] ); # boom! 167 | $aref->validate(2); # boom! 168 | 169 | =head2 C 170 | 171 | my $inc = Coerce( Int, sub { $_[0] + 1 } ); 172 | my $num = 4; 173 | $inc->validate($num); 174 | say $num; # 5 175 | 176 | Easily coerce data. Note that we use C<$_[0]> instead of C<$_> for the 177 | coercion value. 178 | 179 | B: this will I the data you pass in. If you need the data 180 | unchanged, clone the data before passing it. 181 | 182 | =head2 C 183 | 184 | my $colors = Enum(qw/red white blue/); 185 | $colors->validate('red'); # good 186 | $colors->validate('green')' # bad 187 | 188 | As a convenience, C can also take types, and even nest enums. 189 | 190 | my $true_false = Enum(qw/true false/); 191 | my $typed_enum = Enum($true_false, Num, 'Ovid'); 192 | 193 | C<$typed_enum> will match any of C, C, C, or a number. 194 | 195 | =head2 C 196 | 197 | # this is the same as HashRef(Any); 198 | my $hashref = HashRef; 199 | 200 | This has three modes. 201 | 202 | The first is matching I hashref: 203 | 204 | my $hashref = HashRef; 205 | 206 | The second is passing a single type, requiring all values to be of that type: 207 | 208 | my $hashref_of_arrayref_of_ints = HashRef(ArrayRef(Int)); 209 | 210 | The third is passing a list of keys whose values are the desired types. Any 211 | extra keys will be ignored. If you want extra keys to be fatal, use C 212 | instead: 213 | 214 | my $hashref = HashRef( 215 | name => Str, 216 | colors => Enum(qw/red white blue/), 217 | json => { 218 | order_ids => ArrayRef(Int), 219 | payload => Any, 220 | }, 221 | ); 222 | 223 | =head2 C 224 | 225 | my $dict = Dict( 226 | name => Str, 227 | colors => Enum(qw/red white blue/), 228 | json => { 229 | order_ids => ArrayRef(Int), 230 | payload => Any, 231 | }, 232 | ); 233 | 234 | Like C, but extra keys are fatal. If you want some keys to be 235 | I, see C. Otherwise, if you want some values to be 236 | optional (in other words, to allow C), use C. 237 | 238 | =head2 C 239 | 240 | Ints. Duh. 241 | 242 | =head2 C 243 | 244 | Any number. 245 | 246 | =head2 C 247 | 248 | Matches strings. Unlike C from C, this will fail 249 | validation unless there is at least one word character. 250 | 251 | =head2 C 252 | 253 | my $re = Re(qr/ab*c/); 254 | 255 | Matches regular expressions. 256 | 257 | =head2 C and C 258 | 259 | my $maybe_int = Maybe(Int); 260 | $maybe_int->validate(3); # good 261 | $maybe_int->validate(undef); # good 262 | $maybe_int->validate('Ovid'); # boom! 263 | 264 | These two functions behave identically most of the time. However, when used on 265 | the I, C says "the value may be undefined" 266 | and C says "the key may be missing." 267 | -------------------------------------------------------------------------------- /lib/Object/Types/Moo.pm: -------------------------------------------------------------------------------- 1 | package Object::Types::Moo; 2 | use 5.26.0; 3 | use warnings; 4 | use experimental 'signatures'; 5 | no warnings 'experimental'; 6 | use Object::Types::Moo::Factory; 7 | use base 'Exporter'; 8 | 9 | our @EXPORT_OK = qw( 10 | Any 11 | ArrayRef 12 | Coerce 13 | Dict 14 | Enum 15 | HashRef 16 | Int 17 | Maybe 18 | Num 19 | Optional 20 | Regex 21 | Str 22 | ); 23 | our %EXPORT_TAGS = ( all => \@EXPORT_OK ); 24 | 25 | sub _create (@args) { 26 | return Object::Types::Moo::Factory->create(@args); 27 | } 28 | 29 | sub Any () { _create('Any') } 30 | sub Int () { _create('Int') } 31 | sub Num () { _create('Num') } 32 | sub Str () { _create('Str') } 33 | sub Regex ($re) { _create( 'Regex', regex => $re ) } 34 | sub Enum (@elements) { _create( 'Enum', elements => [@elements] ) } 35 | 36 | sub ArrayRef ($type=undef) { 37 | if ( defined $type ) { 38 | _create( 'ArrayRef', contains => $type ); 39 | } 40 | else { 41 | _create('ArrayRef'); 42 | } 43 | } 44 | 45 | sub HashRef(@elements) { 46 | if ( 1 == @elements ) { 47 | _create( 'HashRef', contains => $elements[0] ); 48 | } 49 | elsif ( !@elements ) { 50 | _create('HashRef'); 51 | } 52 | else { 53 | _create( 'HashRef', element_hash => {@elements} ); 54 | } 55 | } 56 | 57 | sub Dict(%elements) { 58 | _create( 'HashRef', element_hash => {%elements}, restricted => 1 ); 59 | } 60 | 61 | sub Maybe($type) { _create( 'Maybe', contains => $type ) } 62 | sub Optional($type) { _create( 'Optional', contains => $type ) } 63 | 64 | sub Coerce ( $type, $code ) { 65 | _create( 'Coerce', contains => $type, via => $code ); 66 | } 67 | 68 | 1; 69 | 70 | __END__ 71 | 72 | =head1 NAME 73 | 74 | Object::Types::Moo - Types::Standard-like functions with better error reporting 75 | 76 | =head1 SYNOPSIS 77 | 78 | use Object::Types::Moo qw(:all); 79 | my $true_false = Enum(qw/true false/); 80 | my $YAMLBool = Coerce( $true_false, sub { $_[0] eq 'true' ? 1 : 0 } ); 81 | my $data_structure = Dict( 82 | routes => Dict( 83 | requires_proof => $YAMLBool, 84 | list_of_coercions => ArrayRef( ArrayRef($YAMLBool) ), 85 | ), 86 | ); 87 | $data_structure->validate($some_data); 88 | 89 | =head1 DESCRIPTION 90 | 91 | This is a limited subset of a reimplemenation of L due to two 92 | serious issues. First, the error messages in C are almost 93 | impossible to read: 94 | 95 | Reference {"maybe_true" => "true","not_a_hashref" => "asdf"} did not pass type constraint "Dict[maybe_true=>YAMLBool,not_a_hashref=>Optional[HashRef]]" (in $_[0]) at fail.pl line 18 96 | Reference {"maybe_true" => "true","not_a_hashref" => "asdf"} did not pass type constraint "Dict[maybe_true=>YAMLBool,not_a_hashref=>Optional[HashRef]]" (in $_[0]) 97 | "Dict[maybe_true=>YAMLBool,not_a_hashref=>Optional[HashRef]]" constrains value at key "maybe_true" of hash with "YAMLBool" 98 | "YAMLBool" is a subtype of "Bool" 99 | Value "true" did not pass type constraint "Bool" (in $_[0]->{"maybe_true"}) 100 | "Bool" is defined as: (!ref $_ and (!defined $_ or $_ eq q() or $_ eq '0' or $_ eq '1')) 101 | 102 | Second, not only is the above error message very hard to parse (and that's a 103 | simple example), it also turns out to be wrong. See 104 | L for more detail. 105 | 106 | For this code, we can trivially reproduce the error: 107 | 108 | my $true_false = Enum(qw/true false/); 109 | my $YAMLBool = Coerce( $true_false, sub { $_[0] eq 'true' ? 1 : 0 } ); 110 | my $check = Dict( 111 | maybe_true => $YAMLBool, 112 | not_a_hashref => Optional(HashRef), 113 | ); 114 | 115 | my $minimal = { 116 | maybe_true => 'true', 117 | not_a_hashref => 'abcd', 118 | }; 119 | $check->validate($minimal); 120 | 121 | And the error message is both correct and easy to understand: 122 | 123 | Validation for '$data{not_a_hashref}' failed for type HashRef with value 'abcd' 124 | 125 | =head1 LIMITED FUNCTIONALITY 126 | 127 | This mostly mimics basic behavior of L. However, many 128 | features are missing because I only implemented what I needed. 129 | 130 | =head1 FUNCTIONS 131 | 132 | All functions are exportabe on-demand, or via C<:all>. Further, they can be c 133 | composed to arbitrary depths. 134 | 135 | If a call to C fails, the code will C with an 136 | appropriate error message, giving you enough information to diagnose the 137 | error: 138 | 139 | Validation for '$data{routes}[0]{'auth'}' failed for type HashRef with value 'abcd' 140 | 141 | The structure of the failure is: 142 | 143 | Validation for '$path_to_data' failed for type $type with value 'offending value' 144 | 145 | By default, the variable name in the error is C<$data>. You may pass a second 146 | argument to C with any string you desire and that will be used as 147 | the variable names instead: 148 | 149 | $something->validate($json, '$json'); 150 | 151 | =head2 C 152 | 153 | my $any = Any; 154 | $any->validate($something); # always succeeds 155 | 156 | =head2 C 157 | 158 | my $aref = ArrayRef; 159 | $aref->validate( [] ); # good 160 | $aref->validate( \@anything ); # good 161 | $aref->validate(2); # boom! 162 | 163 | my $aref_of_ints = ArrayRef(Int); 164 | $aref->validate( [] ); # good 165 | $aref->validate( [ 2, 3, -1500 ] ); # good! 166 | $aref->validate( ['ovid'] ); # boom! 167 | $aref->validate(2); # boom! 168 | 169 | =head2 C 170 | 171 | my $inc = Coerce( Int, sub { $_[0] + 1 } ); 172 | my $num = 4; 173 | $inc->validate($num); 174 | say $num; # 5 175 | 176 | Easily coerce data. Note that we use C<$_[0]> instead of C<$_> for the 177 | coercion value. 178 | 179 | B: this will I the data you pass in. If you need the data 180 | unchanged, clone the data before passing it. 181 | 182 | =head2 C 183 | 184 | my $colors = Enum(qw/red white blue/); 185 | $colors->validate('red'); # good 186 | $colors->validate('green')' # bad 187 | 188 | As a convenience, C can also take types, and even nest enums. 189 | 190 | my $true_false = Enum(qw/true false/); 191 | my $typed_enum = Enum($true_false, Num, 'Ovid'); 192 | 193 | C<$typed_enum> will match any of C, C, C, or a number. 194 | 195 | =head2 C 196 | 197 | # this is the same as HashRef(Any); 198 | my $hashref = HashRef; 199 | 200 | This has three modes. 201 | 202 | The first is matching I hashref: 203 | 204 | my $hashref = HashRef; 205 | 206 | The second is passing a single type, requiring all values to be of that type: 207 | 208 | my $hashref_of_arrayref_of_ints = HashRef(ArrayRef(Int)); 209 | 210 | The third is passing a list of keys whose values are the desired types. Any 211 | extra keys will be ignored. If you want extra keys to be fatal, use C 212 | instead: 213 | 214 | my $hashref = HashRef( 215 | name => Str, 216 | colors => Enum(qw/red white blue/), 217 | json => { 218 | order_ids => ArrayRef(Int), 219 | payload => Any, 220 | }, 221 | ); 222 | 223 | =head2 C 224 | 225 | my $dict = Dict( 226 | name => Str, 227 | colors => Enum(qw/red white blue/), 228 | json => { 229 | order_ids => ArrayRef(Int), 230 | payload => Any, 231 | }, 232 | ); 233 | 234 | Like C, but extra keys are fatal. If you want some keys to be 235 | I, see C. Otherwise, if you want some values to be 236 | optional (in other words, to allow C), use C. 237 | 238 | =head2 C 239 | 240 | Ints. Duh. 241 | 242 | =head2 C 243 | 244 | Any number. 245 | 246 | =head2 C 247 | 248 | Matches strings. Unlike C from C, this will fail 249 | validation unless there is at least one word character. 250 | 251 | =head2 C 252 | 253 | my $re = Re(qr/ab*c/); 254 | 255 | Matches regular expressions. 256 | 257 | =head2 C and C 258 | 259 | my $maybe_int = Maybe(Int); 260 | $maybe_int->validate(3); # good 261 | $maybe_int->validate(undef); # good 262 | $maybe_int->validate('Ovid'); # boom! 263 | 264 | These two functions behave identically most of the time. However, when used on 265 | the I, C says "the value may be undefined" 266 | and C says "the key may be missing." 267 | -------------------------------------------------------------------------------- /lib/Object/Types/Moose.pm: -------------------------------------------------------------------------------- 1 | package Object::Types::Moose; 2 | use 5.26.0; 3 | use warnings; 4 | use experimental 'signatures'; 5 | no warnings 'experimental'; 6 | use Object::Types::Moose::Factory; 7 | use base 'Exporter'; 8 | 9 | our @EXPORT_OK = qw( 10 | Any 11 | ArrayRef 12 | Coerce 13 | Dict 14 | Enum 15 | HashRef 16 | Int 17 | Maybe 18 | Num 19 | Optional 20 | Regex 21 | Str 22 | ); 23 | our %EXPORT_TAGS = ( all => \@EXPORT_OK ); 24 | 25 | sub _create (@args) { 26 | return Object::Types::Moose::Factory->create(@args); 27 | } 28 | 29 | sub Any () { _create('Any') } 30 | sub Int () { _create('Int') } 31 | sub Num () { _create('Num') } 32 | sub Str () { _create('Str') } 33 | sub Regex ($re) { _create( 'Regex', regex => $re ) } 34 | sub Enum (@elements) { _create( 'Enum', elements => [@elements] ) } 35 | 36 | sub ArrayRef ($type=undef) { 37 | if ( defined $type ) { 38 | _create( 'ArrayRef', contains => $type ); 39 | } 40 | else { 41 | _create('ArrayRef'); 42 | } 43 | } 44 | 45 | sub HashRef(@elements) { 46 | if ( 1 == @elements ) { 47 | _create( 'HashRef', contains => $elements[0] ); 48 | } 49 | elsif ( !@elements ) { 50 | _create('HashRef'); 51 | } 52 | else { 53 | _create( 'HashRef', element_hash => {@elements} ); 54 | } 55 | } 56 | 57 | sub Dict(%elements) { 58 | _create( 'HashRef', element_hash => {%elements}, restricted => 1 ); 59 | } 60 | 61 | sub Maybe($type) { _create( 'Maybe', contains => $type ) } 62 | sub Optional($type) { _create( 'Optional', contains => $type ) } 63 | 64 | sub Coerce ( $type, $code ) { 65 | _create( 'Coerce', contains => $type, via => $code ); 66 | } 67 | 68 | 1; 69 | 70 | __END__ 71 | 72 | =head1 NAME 73 | 74 | Object::Types::Moose - Types::Standard-like functions with better error reporting 75 | 76 | =head1 SYNOPSIS 77 | 78 | use Object::Types::Moose qw(:all); 79 | my $true_false = Enum(qw/true false/); 80 | my $YAMLBool = Coerce( $true_false, sub { $_[0] eq 'true' ? 1 : 0 } ); 81 | my $data_structure = Dict( 82 | routes => Dict( 83 | requires_proof => $YAMLBool, 84 | list_of_coercions => ArrayRef( ArrayRef($YAMLBool) ), 85 | ), 86 | ); 87 | $data_structure->validate($some_data); 88 | 89 | =head1 DESCRIPTION 90 | 91 | This is a limited subset of a reimplemenation of L due to two 92 | serious issues. First, the error messages in C are almost 93 | impossible to read: 94 | 95 | Reference {"maybe_true" => "true","not_a_hashref" => "asdf"} did not pass type constraint "Dict[maybe_true=>YAMLBool,not_a_hashref=>Optional[HashRef]]" (in $_[0]) at fail.pl line 18 96 | Reference {"maybe_true" => "true","not_a_hashref" => "asdf"} did not pass type constraint "Dict[maybe_true=>YAMLBool,not_a_hashref=>Optional[HashRef]]" (in $_[0]) 97 | "Dict[maybe_true=>YAMLBool,not_a_hashref=>Optional[HashRef]]" constrains value at key "maybe_true" of hash with "YAMLBool" 98 | "YAMLBool" is a subtype of "Bool" 99 | Value "true" did not pass type constraint "Bool" (in $_[0]->{"maybe_true"}) 100 | "Bool" is defined as: (!ref $_ and (!defined $_ or $_ eq q() or $_ eq '0' or $_ eq '1')) 101 | 102 | Second, not only is the above error message very hard to parse (and that's a 103 | simple example), it also turns out to be wrong. See 104 | L for more detail. 105 | 106 | For this code, we can trivially reproduce the error: 107 | 108 | my $true_false = Enum(qw/true false/); 109 | my $YAMLBool = Coerce( $true_false, sub { $_[0] eq 'true' ? 1 : 0 } ); 110 | my $check = Dict( 111 | maybe_true => $YAMLBool, 112 | not_a_hashref => Optional(HashRef), 113 | ); 114 | 115 | my $minimal = { 116 | maybe_true => 'true', 117 | not_a_hashref => 'abcd', 118 | }; 119 | $check->validate($minimal); 120 | 121 | And the error message is both correct and easy to understand: 122 | 123 | Validation for '$data{not_a_hashref}' failed for type HashRef with value 'abcd' 124 | 125 | =head1 LIMITED FUNCTIONALITY 126 | 127 | This mostly mimics basic behavior of L. However, many 128 | features are missing because I only implemented what I needed. 129 | 130 | =head1 FUNCTIONS 131 | 132 | All functions are exportabe on-demand, or via C<:all>. Further, they can be c 133 | composed to arbitrary depths. 134 | 135 | If a call to C fails, the code will C with an 136 | appropriate error message, giving you enough information to diagnose the 137 | error: 138 | 139 | Validation for '$data{routes}[0]{'auth'}' failed for type HashRef with value 'abcd' 140 | 141 | The structure of the failure is: 142 | 143 | Validation for '$path_to_data' failed for type $type with value 'offending value' 144 | 145 | By default, the variable name in the error is C<$data>. You may pass a second 146 | argument to C with any string you desire and that will be used as 147 | the variable names instead: 148 | 149 | $something->validate($json, '$json'); 150 | 151 | =head2 C 152 | 153 | my $any = Any; 154 | $any->validate($something); # always succeeds 155 | 156 | =head2 C 157 | 158 | my $aref = ArrayRef; 159 | $aref->validate( [] ); # good 160 | $aref->validate( \@anything ); # good 161 | $aref->validate(2); # boom! 162 | 163 | my $aref_of_ints = ArrayRef(Int); 164 | $aref->validate( [] ); # good 165 | $aref->validate( [ 2, 3, -1500 ] ); # good! 166 | $aref->validate( ['ovid'] ); # boom! 167 | $aref->validate(2); # boom! 168 | 169 | =head2 C 170 | 171 | my $inc = Coerce( Int, sub { $_[0] + 1 } ); 172 | my $num = 4; 173 | $inc->validate($num); 174 | say $num; # 5 175 | 176 | Easily coerce data. Note that we use C<$_[0]> instead of C<$_> for the 177 | coercion value. 178 | 179 | B: this will I the data you pass in. If you need the data 180 | unchanged, clone the data before passing it. 181 | 182 | =head2 C 183 | 184 | my $colors = Enum(qw/red white blue/); 185 | $colors->validate('red'); # good 186 | $colors->validate('green')' # bad 187 | 188 | As a convenience, C can also take types, and even nest enums. 189 | 190 | my $true_false = Enum(qw/true false/); 191 | my $typed_enum = Enum($true_false, Num, 'Ovid'); 192 | 193 | C<$typed_enum> will match any of C, C, C, or a number. 194 | 195 | =head2 C 196 | 197 | # this is the same as HashRef(Any); 198 | my $hashref = HashRef; 199 | 200 | This has three modes. 201 | 202 | The first is matching I hashref: 203 | 204 | my $hashref = HashRef; 205 | 206 | The second is passing a single type, requiring all values to be of that type: 207 | 208 | my $hashref_of_arrayref_of_ints = HashRef(ArrayRef(Int)); 209 | 210 | The third is passing a list of keys whose values are the desired types. Any 211 | extra keys will be ignored. If you want extra keys to be fatal, use C 212 | instead: 213 | 214 | my $hashref = HashRef( 215 | name => Str, 216 | colors => Enum(qw/red white blue/), 217 | json => { 218 | order_ids => ArrayRef(Int), 219 | payload => Any, 220 | }, 221 | ); 222 | 223 | =head2 C 224 | 225 | my $dict = Dict( 226 | name => Str, 227 | colors => Enum(qw/red white blue/), 228 | json => { 229 | order_ids => ArrayRef(Int), 230 | payload => Any, 231 | }, 232 | ); 233 | 234 | Like C, but extra keys are fatal. If you want some keys to be 235 | I, see C. Otherwise, if you want some values to be 236 | optional (in other words, to allow C), use C. 237 | 238 | =head2 C 239 | 240 | Ints. Duh. 241 | 242 | =head2 C 243 | 244 | Any number. 245 | 246 | =head2 C 247 | 248 | Matches strings. Unlike C from C, this will fail 249 | validation unless there is at least one word character. 250 | 251 | =head2 C 252 | 253 | my $re = Re(qr/ab*c/); 254 | 255 | Matches regular expressions. 256 | 257 | =head2 C and C 258 | 259 | my $maybe_int = Maybe(Int); 260 | $maybe_int->validate(3); # good 261 | $maybe_int->validate(undef); # good 262 | $maybe_int->validate('Ovid'); # boom! 263 | 264 | These two functions behave identically most of the time. However, when used on 265 | the I, C says "the value may be undefined" 266 | and C says "the key may be missing." 267 | -------------------------------------------------------------------------------- /rfc/class-construction.md: -------------------------------------------------------------------------------- 1 | Prev: [Classes](classes.md) 2 | Next: [Fields](attributes.md) 3 | 4 | --- 5 | 6 | # Section 5: Class Construction 7 | 8 | **This file is automatically generated. If you wish to submit a PR, do not 9 | edit this file directly. Please edit 10 | [templates/rfc/class-construction.md](https://github.com/Ovid/Cor/tree/master/templates/rfc/class-construction.md) instead. Use `bin/generate_rfc.pl` to regenerate the RFCs.** 11 | 12 | --- 13 | 14 | # 5.1 Overview 15 | For object construction, we provide a list of needed steps and then show 16 | pseudocode to make the construction process explicit. 17 | 18 | **Note**: because Corinna is single inheritance, MRO order is simply child to 19 | parent. 20 | 21 | Anything which can be removed from this will make object construction 22 | faster. Anything which can be pushed to compile-time will make object 23 | construction faster. 24 | 25 | This is almost certainly incorrect, but it's a start. 26 | 27 | Also, roles get an ADJUST phaser now 28 | 29 | 1. Check that the even-sized list of args to new() are not duplicated 30 | (stops the new( this => 1, this => 2 ) error) 31 | 2. And that the keys are not references 32 | 3. Walk through classes in reverse MRO order. Croak() if any field 33 | name is reused 34 | 4. After previous step, if we have any extra keys passed to new() which cannot be 35 | allocated to a field, throw an exception 36 | 5. For the internal NEW phaser, assign all values to their correct fields in 37 | reverse mro order 38 | 6. Call all ADJUST phasers in reverse MRO order (no need to validate here because 39 | everything should be checked at this point) 40 | 41 | # 5.2 Steps 42 | ## 5.2.1 Step 1 Verify even-sized list of args 43 | Check that the even-sized list of args to `new()` are not duplicated (stops 44 | the `new( this => 1, this => 2` ) error). 45 | 46 | ```perl 47 | my @args = ...; # get list passed to new() 48 | if ( @args % 2 ) { 49 | croak("even-sized list required"); 50 | } 51 | ``` 52 | 53 | ## 5.2.2 Step 2 Constructor keys may not be references 54 | Keys are not references (duh). 55 | 56 | ```perl 57 | my %arg_for; 58 | while (@args) { 59 | my ( $key, $value ) = (shift @args, shift @args); 60 | ref $key and croak qq{'$key' must not be a ref}; 61 | exists $arg_for{$key} and croak qq{duplicate key '$key' detected}; 62 | $arg_for{$key} = $value; 63 | } 64 | ``` 65 | 66 | ## 5.2.3 Step 3 Find constructor args 67 | Walk through classes from parent to child. `croak()` if any 68 | constructor argument is reused. Different roles and classes can 69 | consume the same role, their constructor arguments only count once. 70 | 71 | ```perl 72 | my %orig_args = %arg_for; # shallow copy 73 | my %constructor_args; 74 | 75 | 76 | my @duplicate_constructor_args; 77 | my %seen_roles; 78 | foreach my $class (@reverse_mro) { 79 | my @roles = grep { ! $seen_roles{$_} } roles_from_class($class); 80 | @seen_roles{ @roles } = (1) x @roles; 81 | foreach my $thing ( $class, @roles ) { 82 | foreach my $name ( get_fields_with_param_attributes($thing) ) { 83 | if ( my $other_class = $constructor_args{$name} ) { 84 | # XXX Warning! This may be a bad thing 85 | # If you don't happen to notice that some parent class has done 86 | # `field $cutoff :param = 42;` 87 | # then you might accidentally write: 88 | # `field $cutoff :param = DateTime->now->add(days => 7);` 89 | # instead, we probably need some way of signaling this to the 90 | # programmer. A compile-time error would be good. 91 | push @duplicate_constructor_args 92 | => "Arg '$name' in '$thing' already used in '$other_class'"; 93 | } 94 | $constructor_args{$name} = $class; 95 | } 96 | } 97 | } 98 | if (my $error = join ' ' => @duplicate_constructor_args) { 99 | croak($error); 100 | } 101 | ``` 102 | 103 | **Note**: "reused" constructor arguments refers to the public name for the 104 | field. You can reuse `field $message;` in subclasses because it's not public. 105 | However, you cannot reuse `field $message :param;` in a subclass because the 106 | field name default to `message`. Instead, you would need to rename it: `field 107 | $message :param :name(client_message);`. 108 | 109 | We have this restriction to enforce encapsulation of logic in a class. If the 110 | parent class has `field $error :param;` and expects that to contain an error 111 | _object_ and a child class has a `field $error :param;` and expects that to 112 | contain an error _string_, you're in trouble. Until such time that we can 113 | squeeze types into Corinna (and to Perl in general), this restriction makes 114 | the code safer, albeit at the cost of some annoyance. 115 | 116 | ## 5.2.4 Step 4 Err out on unknown keys 117 | 118 | After the previous step, if we have any extra keys passed to `new()` which cannot 119 | be allocated to a field, throw an exception. This works because by the time we 120 | get to the final class, all keys should be accounted for. Stops the issue of 121 | `Class->new(feild => 4)` when the field is `field $field :param = 3;` 122 | 123 | ```perl 124 | my @bad_keys; 125 | foreach my $key ( keys %arg_for ) { 126 | exists $constructor_args{$key} 127 | or push @bad_keys, $key; 128 | } 129 | if (@bad_keys) { 130 | croak(...); 131 | } 132 | ``` 133 | 134 | ## 5.2.5 Step 5 `new()` 135 | For the internal NEW phaser, assign all values to their correct fields from 136 | parent to child. 137 | 138 | ```perl 139 | my @field_values; 140 | foreach my $this_class (@reverse_mro) { 141 | my @roles = roles_from_class($class); 142 | foreach my $thing ( $class, @roles ) { 143 | foreach my $field_name ( get_fields_in_initialization_order($thing) ) { 144 | push @field_values => $arg_for{$field_name}; 145 | } 146 | } 147 | } 148 | 149 | # PSEUDOCODE! In no way is this meant to suggest that this will be the 150 | # underlying representation of Corinna objects. 151 | my $self = bless \@field_values => $class; 152 | ``` 153 | 154 | ## 5.2.6 Step 6 `ADJUST` 155 | Call all `ADJUST` phasers from parent to children (no need to validate here because 156 | everything should be checked at this point). 157 | 158 | ```perl 159 | foreach my $class (@reverse_mro) { 160 | my @roles = roles_from_class($class); 161 | foreach my $thing ( $class, @roles ) { 162 | 163 | # the prefix is just pseudo-code to show the idea 164 | $thing::ADJUST(); # phaser, not a method 165 | } 166 | } 167 | ``` 168 | 169 | # 5.3 MOP Pseudocode 170 | MOP stuff 171 | 172 | ```perl 173 | class MOP { 174 | method get_fields_with_param_attributes($class_or_role) { 175 | return 176 | grep { $self->has_attribute( ':param', $_ ) } 177 | get_all_fields($class_or_role); 178 | } 179 | 180 | method get_fields_in_initialization_order($class_or_role) { 181 | # get_all_fields($class_or_role) should return them in declaration order 182 | my @fields = get_all_fields($class_or_role); 183 | my @ordered; 184 | my $constructor_args_processed = 0; 185 | while (@fields) { 186 | my $field = shift @fields; 187 | if ( $self->has_attribute( ':param', $field ) ) { 188 | push @ordered => $fields; 189 | my @remaining; 190 | foreach my $field (@fields) { 191 | if ( $self->has_attribute( ':param', $field ) ) { 192 | push @ordered => $field; 193 | } 194 | else { 195 | push @remaining => $field; 196 | } 197 | } 198 | @fields = @remaining; 199 | } 200 | else { 201 | push @ordered => $field; 202 | } 203 | } 204 | } 205 | } 206 | ``` 207 | 208 | 209 | --- 210 | 211 | Prev: [Classes](classes.md) 212 | Next: [Fields](attributes.md) 213 | -------------------------------------------------------------------------------- /rfc/faq.md: -------------------------------------------------------------------------------- 1 | Prev: [P5P MVP](mvp.md) 2 | Next: [README](/README.md) 3 | 4 | --- 5 | 6 | # Section 15: FAQ 7 | 8 | **This file is automatically generated. If you wish to submit a PR, do not 9 | edit this file directly. Please edit 10 | [templates/rfc/faq.md](https://github.com/Ovid/Cor/tree/master/templates/rfc/faq.md) instead. Use `bin/generate_rfc.pl` to regenerate the RFCs.** 11 | 12 | --- 13 | 14 | # 15.1 Overview 15 | You have questions? We have answers. Note that this is a work-in-progress. 16 | More may be added later. 17 | 18 | ## 15.1.1 Why can't I inherit from blessed objects? 19 | Post-MVP this decision might be revisited, but for the MVP, we needed to 20 | constrain the problem space. Just some of the issues being faced: 21 | 22 | * `class` will understand the difference between methods and subroutines and 23 | when the MVP is more fleshed out, will not allow you to call subroutines as 24 | methods. What do you do when the parent class is `blessed`ed and all you 25 | have are subs? 26 | * `class` is single-inheritance and `bless` is multiple inheritance. Having to 27 | switch the MRO back and forth as you walk the inheritance hierarchy is 28 | begging for bugs. 29 | * Ultimately, `class` might need a different base class, such as 30 | `UNIVERSAL::Class`. If we inherit from a `bless`ed object, which base class 31 | wins? 32 | * Many awesome tools have been written for 33 | [Moose](https://metacpan.org/pod/Moose) and they assume 34 | [Class::MOP](https://metacpan.org/pod/Class::MOP) works. We cannot make that 35 | assumption, so we don't. 36 | 37 | Post-MVP, we might revision this decision, but for the MVP, there's a huge 38 | amount of work to do and trying to make sure we didn't screw up anywhere is 39 | much harder if we try to slurp in the entire `bless`ed ecosystem. Bear with 40 | us. 41 | 42 | Otherwise, you can try the [Object::Pad](https://metacpan.org/pod/Object::Pad) 43 | module. That's been the test bed for Corinna and it _does_ allow inheriting 44 | from legacy objects. Caveat Emptor. 45 | 46 | ## 15.1.2 But I _need_ to inherit from a blessed object! 47 | No, you can't. Sorry. You can try `Object::Pad`, not use `class`, or 48 | investigate composition over inheritance: 49 | 50 | ```perl 51 | class My::Class { 52 | use Some::Bless::Class; 53 | 54 | field $arg_for_blessed :param(arg); 55 | field $delegate = Some::Bless::Class->new($arg_for_blessed); 56 | 57 | method to_delegate (@args) { 58 | return $delegate->some_method(@args); 59 | } 60 | } 61 | ``` 62 | 63 | None of those answers are satisfactory, but this is an MVP. 64 | 65 | ## 15.1.3 Why can't I use subroutines for methods? 66 | If you write this: 67 | 68 | ```perl 69 | class My::Class { 70 | field $name; 71 | sub get_name ($self) { $name } 72 | } 73 | ``` 74 | 75 | That's a syntax error because we can't access the instance variable `$name` 76 | from a subroutine. They don't know what instance variables are. Or consider 77 | these; 78 | 79 | ```perl 80 | 81 | class Example1 { 82 | sub sum ($self) { ... } 83 | } 84 | 85 | class Example2 { 86 | use List::Util 'sum'; 87 | } 88 | ``` 89 | 90 | In both cases, we have a `sum` subroutine, but for the first, it's clearly 91 | intended to be a method and not a helper function. From the outside, we have 92 | no way of knowing that. `class` makes a clear distinction between methods and 93 | subroutines and when the MVP is complete, `$class->can('sum')` should return 94 | false if `sum` is a subroutine and not a method. 95 | 96 | ## 15.1.4 Will `class` break existing code? 97 | No. Well, if you tried to slap it into an existing procedural code and you had subroutines 98 | with conflicting names to the keywords, _maybe_. Or you could use a lexical 99 | block and it should be perfectly safe: 100 | 101 | ```perl 102 | # lots of code here 103 | { 104 | use feature 'class'; 105 | class Foo ... 106 | } 107 | # more code here, but it doesn't see `class` behavior 108 | ``` 109 | 110 | Otherwise, `class` objects can call methods on `bless` objects and vice-versa 111 | without problem. 112 | 113 | ## 15.1.5 How can I refactor existing objects with `class`? 114 | Er, you probably shouldn't. Rewriting is okay, but refactoring is a different 115 | story. 116 | 117 | If you wanted to experiment, keep this in mind: a `class` cannot inherit from 118 | `bless`ed objects, but as of this writing, a `bless`ed object _can_ inherit 119 | from a `class` object. So go to the root of your object hierarchy and look at 120 | converting that to a `class` and see what happens. 121 | 122 | For any decent-sized system, you're going to have too many edge cases for this 123 | to be easy (or sane). For example, `class` constructors require an even-sized 124 | list of key/value pairs. Many other constructors don't. You may need to 125 | rethink your constructor strategy. 126 | 127 | At the end of the day, trying to gradually mix-and-match an OOP hierarchy from 128 | `bless` to `class` is like trying to use motorcycle parts to fix a car. Maybe 129 | you'll get lucky and it works, but probably not for larger codebases (at least, 130 | not without a lot of heavy lifting). 131 | 132 | The above is for refactoring. However, you could _rewrite_ existing objects via 133 | the new `class` syntax. If your tests are sane, that may not be too hard. 134 | However, if people are using your code, they may not be in a position to 135 | upgrade to v5.38.0. You could investigate 136 | [Feature::Compat::Class](https://metacpan.org/pod/Feature::Compat::Class), but 137 | read the docs carefully. There are potential compatibility issues. 138 | 139 | ## 15.1.6 Are there any interesting projects being written with `class`? 140 | Yes! Check out Chris "peregrin" Prather's [Roguelike 141 | tutorial](https://chris.prather.org/menu/roguelike). It's pretty amazing and 142 | shows that `class` is more powerful than your author suspected (I thought we'd 143 | need more features to get this far. I was wrong). 144 | 145 | Stevan "Damn it" Little is writing [Stella](https://github.com/stevan/Stella), 146 | an actor model written with `class`. So far, he's been very pleased with how 147 | easy `class` is to work with. 148 | 149 | ## 15.1.7 Are a class and a package the same thing? 150 | Not quite. A `class` keyword declares a class _and_ a namespace. A `package` 151 | keyword just declares the namespace. They both have the same scoping rules. 152 | 153 | What this means, however, is that you can use `class` similarly to how you 154 | would use a `package`. 155 | 156 | For example, if you have a file named `lib/My/Awesome/Class.pm`, you won't need 157 | a `package` statement inside if you use `class` instead: 158 | 159 | ```perl 160 | use experimental 'class'; 161 | 162 | class My::Awesome::Class; 163 | # more code here ... 164 | ``` 165 | 166 | ## 15.1.8 You keep writing classes with a postfix block. Is that required? 167 | No, it's not. Like the `package` keyword, the postfix block is optional. I 168 | prefer it for the extremely clear scoping. You don't need to use it if you 169 | don't want to. 170 | 171 | ## 15.1.9 Is there a tutorial? 172 | There's a tutorial at 173 | [perlclasstut.pod](https://github.com/Ovid/Cor/blob/master/pod/perlclasstut.pod). 174 | 175 | Note that this tutorial is for the full MVP. If you're reading this before the 176 | full MVP is released, some of the features in that tutorial won't yet be 177 | working. I don't know what version of Perl you have installed, so you'll need 178 | to consult your documentation. 179 | 180 | If you prefer, [here's a gist of the tutorial, formatted via 181 | markdown](https://gist.github.com/Ovid/4cc649c1eb3142b6a856d94c54b1d4ed). It's 182 | not guaranteed to be kept up-to-date. 183 | 184 | ## 15.1.10 How do I know which `class` features my Perl supports? 185 | The experimental `class` feature was first added in Perl v5.38.0. You'll want 186 | to check `perldoc perlclass` for your version of Perl. You can also [read it 187 | online](https://perldoc.perl.org/perlclass), but be sure to select the correct 188 | version of Perl from the menu at the top. 189 | 190 | 191 | --- 192 | 193 | Prev: [P5P MVP](mvp.md) 194 | Next: [README](/README.md) 195 | -------------------------------------------------------------------------------- /rfc/grammar.md: -------------------------------------------------------------------------------- 1 | Prev: [Overview](overview.md) 2 | Next: [Classes](classes.md) 3 | 4 | --- 5 | 6 | # Section 3: Grammar 7 | 8 | **This file is automatically generated. If you wish to submit a PR, do not 9 | edit this file directly. Please edit 10 | [templates/rfc/grammar.md](https://github.com/Ovid/Cor/tree/master/templates/rfc/grammar.md) instead. Use `bin/generate_rfc.pl` to regenerate the RFCs.** 11 | 12 | --- 13 | 14 | To make this more manageable and not define a grammar for all of Perl, we will break the grammar out into separate components for simplicity. 15 | 16 | Note that due to the adoption of the [KIM 17 | syntax](https://ovid.github.io/articles/language-design-consistency.html) 18 | (keyword, identifier, modifier), we only introduce four new keywords to the 19 | Perl language: 20 | 21 | * `class` 22 | * `role` 23 | * `field` 24 | * `method` 25 | 26 | # 3.1 Class and Role Grammar 27 | The primary grammar looks like: 28 | 29 | ``` 30 | Corinna ::= CLASS | ROLE 31 | CLASS ::= 'class' NAMESPACE DECLARATION? BLOCK 32 | ROLE ::= 'role' NAMESPACE ROLES? BLOCK 33 | NAMESPACE ::= IDENTIFIER { '::' IDENTIFIER } 34 | DECLARATION ::= ':abstract'? PARENT? ROLES? VERSION? 35 | PARENT ::= ':isa(' NAMESPACE ')' 36 | ROLES ::= ':does(' NAMESPACE { ',' NAMESPACE } ','? ')' 37 | IDENTIFIER ::= [_:alpha:] {[_:alnum:]} 38 | VERSION ::= ':version(' VERSION_NUMBER ') 39 | VERSION_NUMBER ::= # all allowed Perl version numbers 40 | BLOCK ::= # Perl +/- Extras 41 | ``` 42 | 43 | We recommend [semantic versioning](https://semver.org/), but we allow all 44 | existing Perl version formats to facilitate upgrading existing modules. 45 | 46 | # 3.2 Method Grammar 47 | The method grammar (skipping some bits to avoid defining a grammar for Perl): 48 | 49 | ``` 50 | METHOD ::= 'method' ACCESS_LEVELS SIGNATURE '{' (perl code) '}' 51 | SIGNATURE ::= IDENTIFIER '(' current sub argument structure + extra work from Dave Mitchell ')' 52 | ACCESS_LEVELS ::= ACCESS_LEVEL { ACCESS_LEVEL } 53 | ACCESS_LEVEL ::= ':' ( 'private' | 'overrides' | 'common' ) 54 | SIGNATURE ::= # currently allowed Perl signatures 55 | ``` 56 | 57 | # 3.3 Field Grammar 58 | "Fields" in Corinna parlance are the variables where class and instance data are stored. 59 | 60 | For simplicity: `SCALAR`, `ARRAY`, and `HASH` refer to their corresponding variable names. `PERL_EXPRESSION` means what it says. `IDENTIFIER` is a valid Perl identifier. 61 | 62 | ``` 63 | FIELD ::= 'field' ( 64 | SCALAR ATTRIBUTES? DEFAULT? 65 | | ( ARRAY | HASH ) ':common'? DEFAULT? # only the :common attribute is 66 | # currently supported for array/hash fields 67 | ) 68 | DEFAULT ::= '{' PERL_EXPRESSION '}' 69 | ATTRIBUTES ::= { ATTRIBUTE } 70 | ATTRIBUTE ::= ':' ( 71 | 'param' NAME? # allowed in constructor 72 | | 'name' NAME? # alternate name (defaults to field name minus the sigil) 73 | | 'reader' NAME? # $field method to read the field 74 | | 'writer' NAME? # set_$field method to write the field 75 | | 'predicate' NAME? # has_$field method to test if field is defined 76 | | 'common' # identifies field as class method 77 | | HANDLES 78 | ) 79 | HANDLES ::= 'handles' '(' 80 | IDENTIFIER { ',' IDENTIFIER } # list of methods this field handles 81 | | PAIR { ',' PAIR } # map of methods (to, from) this field handles 82 | | '*' # this field handles all unknown methods, but inheritance takes precedence 83 | ')' 84 | PAIR ::= IDENTIFIER ':' IDENTIFIER 85 | NAME ::= '(' IDENTIFIER ')' 86 | ``` 87 | 88 | 89 | --- 90 | 91 | Prev: [Overview](overview.md) 92 | Next: [Classes](classes.md) 93 | -------------------------------------------------------------------------------- /rfc/major-changes.md: -------------------------------------------------------------------------------- 1 | Prev: [Quotes](quotes.md) 2 | Next: [P5P MVP](mvp.md) 3 | 4 | --- 5 | 6 | # Section 13: Changes 7 | 8 | **This file is automatically generated. If you wish to submit a PR, do not 9 | edit this file directly. Please edit 10 | [templates/rfc/major-changes.md](https://github.com/Ovid/Cor/tree/master/templates/rfc/major-changes.md) instead. Use `bin/generate_rfc.pl` to regenerate the RFCs.** 11 | 12 | --- 13 | 14 | Due to the fast-moving nature of this project, we won't note every little 15 | change to the RFC. You can always clone the repo and read the commits. 16 | Instead, we'll cover major changes here. 17 | 18 | # Change Log 19 | 20 | # August 17, 2022 21 | 22 | - `field` attributes are now formally known as attributes. Previously this was 23 | ambiguous. The grammar and RFCs have been updated to reflect this. 24 | 25 | # December 8, 2021 26 | 27 | - Injected `$class` and `$self` variables are now documented as being 28 | immutable. That prevents this bug: 29 | 30 | ```perl 31 | method foo () { 32 | $self = 42; 33 | } 34 | ``` 35 | 36 | - Class data and methods are agreed to be declared with `:common` 37 | 38 | 39 | ```perl 40 | field $foo :common; # class data 41 | method bar :common () { ... } # class method 42 | ``` 43 | 44 | - Class data (declared with `:common` can accept defaults (see the next 45 | change) and only allows the `:reader` attribute with it. 46 | 47 | - Field initialization no longer allows `=`. You must wrap the default in 48 | curly braces (an anonymous sub). This is because of this problem: 49 | 50 | ```perl 51 | field $colors = [qw/blue white red/]; 52 | ``` 53 | 54 | Every `$color` would get a reference to the same anonymous array. Instead, write 55 | it like this: 56 | 57 | ```perl 58 | field $colors { [qw/blue white red/] }; 59 | ``` 60 | 61 | With that, you can change the colors of a particular instance without changing 62 | the others. Yes, you could do `$foo = [qw/blue white black/]'` in a method, 63 | but if someone did `$foo->[-1] = 'black';`, they might wonder why all other 64 | instances had their value changed. 65 | 66 | We realize this change might seem strange, but we're hopeful people will get 67 | used to it. 68 | 69 | As of this writing, we do not plan to inject `$class` or `$self` into that 70 | block. That avoids this bug: 71 | 72 | ```perl 73 | field $color { $self->get_colors }; 74 | ``` 75 | 76 | Because initialization happens when the instance construction might not be 77 | complete, some fields may not be properly defined, leading to obscure bugs. 78 | 79 | # December 6, 2021 80 | 81 | - After considerable discussion, `slot` has been renamed to `field`. A few 82 | people objected to "slot" because it's an unusual term (borrowed from Lisp). 83 | Some non-native English speakers pointed out that "slot" is also harder for 84 | them to understand, while "field" is very clear. There was discussion about 85 | confusion with the little-used "fields" pragma, but it was generally agreed 86 | this would be unlikely. 87 | 88 | # December 4, 2021 89 | 90 | - We have removed the special `:handles(*)` syntax. It was proving too 91 | problematic and, in fact, would not have allowed us to simulate inheritance 92 | because once you enter the delegate, you can't override its methods because 93 | it's not in your inheritance hierarchy. 94 | 95 | # November 23, 2021 96 | 97 | - Clarify that the `:handles(*)` delegation will not auto-delegate to methods 98 | beginning with underscores to avoid those becoming part of the public 99 | interface. Of course, internally you can still call those methods directly 100 | on the slot variable calling the object. 101 | 102 | # November 15, 2021 103 | 104 | - After many suggestions from Paul Evans and later by Damian Conway, Corinna 105 | has been switched over to 106 | [KIM](https://ovid.github.io/articles/language-design-consistency.html) 107 | (Keyword, Identifier, Modifier) syntax. See also [Damian Conway's post on 108 | the 109 | topic](http://blogs.perl.org/users/damian_conway/2021/11/a-dream-resyntaxed.html). 110 | 111 | # November 2, 2021 112 | 113 | - Method modifiers RFC section added. 114 | - Classes documentation now shows we use any legal version numbers, not just 115 | semver triples. 116 | 117 | ## September 22, 2021 118 | 119 | - Abtract methods in classes and required methods in roles are no longer 120 | allowed to declare their argument lists. This gives us room to reconsider 121 | this behavior post-MVP. 122 | 123 | ## September 21, 2021 124 | 125 | - `:name` attribute for slots removed from MVP. Might be returned later. 126 | - Version numbers no longer limited to semver. All current Perl version 127 | formats intended to be supported. 128 | - Classes which both inherit and consume roles must now declare the parent 129 | before the roles (previously, the order was not relevant). 130 | - Method access levels such as `common`, `private`, and `overrides` are now 131 | attributes that come between the method name and the argument list: 132 | 133 | ```perl 134 | method foo :overrides ($bar, $baz) { ... } 135 | ``` 136 | 137 | ## August 26, 2021 138 | 139 | - Class slots now declared with `my`. They do not take attributes. 140 | 141 | ## August 19, 2021 142 | 143 | - Slot declaration keyword renamed from `has` to `slot` 144 | 145 | ## August 14, 2021 146 | 147 | - First draft of RFC released as markdown in github repo so that pull requests 148 | can be received. Wiki is noted to primarily be of historical interest. 149 | 150 | 151 | --- 152 | 153 | Prev: [Quotes](quotes.md) 154 | Next: [P5P MVP](mvp.md) 155 | -------------------------------------------------------------------------------- /rfc/method-modifiers.md: -------------------------------------------------------------------------------- 1 | Prev: [Phasers](phasers.md) 2 | Next: [Questions](questions.md) 3 | 4 | --- 5 | 6 | # Section 10: Method Modifiers 7 | 8 | **This file is automatically generated. If you wish to submit a PR, do not 9 | edit this file directly. Please edit 10 | [templates/rfc/method-modifiers.md](https://github.com/Ovid/Cor/tree/master/templates/rfc/method-modifiers.md) instead. Use `bin/generate_rfc.pl` to regenerate the RFCs.** 11 | 12 | --- 13 | 14 | # 10.1 Overview 15 | Note: the bulk of this document is translated almost verbatim from [the Moose 16 | documentation](https://metacpan.org/dist/Moose/view/lib/Moose/Manual/MethodModifiers.pod). 17 | 18 | --- 19 | 20 | Corinna provides a feature called "method modifiers" via attributes. You can 21 | also think of these as "hooks" or "advice". 22 | 23 | It's probably easiest to understand this feature with a few examples: 24 | 25 | ```perl 26 | role Some::Role { 27 | method foo :before () { print "about to call foo\n"; } 28 | method foo :after () { print "just called foo\n"; } 29 | 30 | method foo :around () { 31 | print " I'm around foo\n"; 32 | 33 | $self->$ORIG(@_); 34 | 35 | print " I'm still around foo\n"; 36 | } 37 | } 38 | 39 | class Example :does(Some::Role) { 40 | method foo () { 41 | print " foo\n"; 42 | } 43 | } 44 | ``` 45 | 46 | Now if we call `Example->new->foo` I'll get the following output: 47 | 48 | ``` 49 | about to call foo 50 | I'm around foo 51 | foo 52 | I'm still around foo 53 | just called foo 54 | ``` 55 | 56 | You probably could have figured that out from the names `:before`, 57 | `:after`, and `:around`. 58 | 59 | Also, as you can see, the `:before` modifiers come before `:around` modifiers, and 60 | `:after` modifiers come last. 61 | 62 | When there are multiple modifiers of the same type, the `:before` and 63 | `:around` modifiers run from the last added to the first, and `:after` 64 | modifiers run from first added to last: 65 | 66 | ``` 67 | before 2 68 | before 1 69 | around 2 70 | around 1 71 | primary 72 | around 1 73 | around 2 74 | after 1 75 | after 2 76 | ``` 77 | 78 | # 10.2 Why use them? 79 | Method modifiers have many uses. They are often used in roles to alter the 80 | behavior of methods in the classes that consume the role. See [this RFC 81 | page](https://github.com/Ovid/Cor/blob/master/rfc/roles.md) for more 82 | information about roles. 83 | 84 | Since modifiers are mostly useful in roles, some of the examples below 85 | are a bit artificial. They're intended to give you an idea of how 86 | modifiers work, but may not be the most natural usage. 87 | 88 | # 10.3 Before, after, and around modifiers 89 | Method modifiers can be used to add behavior to methods without modifying the 90 | definition of those methods. 91 | 92 | ## 10.3.1 Before and after Modifiers 93 | Method modifiers can be used to add behavior to a method that Corinna 94 | generates for you, such as an field accessor: 95 | 96 | ```perl 97 | field $size :reader :writer; 98 | 99 | method set_size :before ($size) { 100 | Carp::cluck('Someone is setting size'); 101 | } 102 | ``` 103 | 104 | Another use for the `:before` modifier would be to do some sort of 105 | prechecking on a method call. For example: 106 | 107 | ```perl 108 | method set_size :before ($size) { 109 | die 'Cannot set size while the person is growing' 110 | if $self->is_growing; 111 | } 112 | ``` 113 | 114 | This lets us implement logical checks that don't make sense as type 115 | constraints. In particular, they're useful for defining logical rules 116 | about an object's state changes. 117 | 118 | Similarly, an `:after` modifier could be used for logging an action that 119 | was taken. 120 | 121 | Note that the return values of both `:before` and `:after` modifiers are 122 | ignored. 123 | 124 | ## 10.3.2 Around modifiers 125 | An `:around` modifier is more powerful than either a `:before` or 126 | `:after` modifier. It can modify the arguments being passed to the 127 | original method, and you can even decide to simply not call the 128 | original method at all. You can also modify the return value with an 129 | `:around` modifier. 130 | 131 | An `:around` modifier receives the original method injected into it via the 132 | `$ORIG` variable. 133 | 134 | ```perl 135 | method set_size :around ($size) { 136 | return $self->$ORIG() 137 | unless @_; 138 | 139 | $size = $size / 2 140 | if $self->likes_small_things(); 141 | 142 | return $self->$ORIG($size); 143 | } 144 | ``` 145 | 146 | **Important**: Note that while the `$ORIG` variable is injected directly into 147 | the `around` method, this behavior and name is provisional and may be changed. 148 | 149 | ## 10.3.3 Execution order of method modifiers and inheritance 150 | When both a superclass and an inheriting class have the same method modifiers, 151 | the method modifiers of the inheriting class are wrapped around the method 152 | modifiers of the superclass, as the following example illustrates: 153 | 154 | Here is the parent class: 155 | 156 | ```perl 157 | class Superclass { 158 | method rant () { printf " RANTING!\n" } 159 | method rant :before () { printf " In %s before\n", __PACKAGE__ } 160 | method rant :after () { printf " In %s after\n", __PACKAGE__ } 161 | method rant :around () { 162 | printf " In %s around before calling original\n", __PACKAGE__; 163 | $self->$ORIG; 164 | printf " In %s around after calling original\n", __PACKAGE__; 165 | } 166 | } 167 | ``` 168 | 169 | And the child class: 170 | 171 | ```perl 172 | class Subclass :isa(Superclass) { 173 | method rant :before () { printf "In %s before\n", __PACKAGE__ } 174 | method rant :after () { printf "In %s after\n", __PACKAGE__ } 175 | method rant :around () { 176 | printf " In %s around before calling original\n", __PACKAGE__; 177 | $self->$ORIG; 178 | printf " In %s around after calling original\n", __PACKAGE__; 179 | } 180 | } 181 | ``` 182 | 183 | And here's the output when we call the wrapped method (`Child->rant`): 184 | 185 | ``` 186 | $ perl -MSubclass -e 'Subclass->new->rant' 187 | 188 | In Subclass before 189 | In Subclass around before calling original 190 | In Superclass before 191 | In Superclass around before calling original 192 | RANTING! 193 | In Superclass around after calling original 194 | In Superclass after 195 | In Subclass around after calling original 196 | In Subclass after 197 | ``` 198 | 199 | # 10.4 Exceptions and stack traces 200 | An exception thrown in a `:before` modifier will prevent the method it 201 | modifies from being called at all. An exception in an `:around` modifier may 202 | prevent the modified method from being called if it's thrown before 203 | `$self->$ORIG` is called, but not after. An exception in an `:after` modifier 204 | obviously cannot prevent the method it wraps from being called. 205 | 206 | From the caller's perspective, an exception in a method modifier will look 207 | like the method it called threw an exception. However, method modifiers are 208 | just standard Perl subroutines. This means that they end up on the stack in 209 | stack traces as an additional frame. 210 | 211 | # 10.5 Caveats 212 | Be extremely careful if you use method modifiers to alter the output. If 213 | multiple modifiers are used and one adds $10 to a total and another one adds 214 | 20% VAT (tax), the final result will depend on the order the modifiers have been 215 | applied. Because this order is not guaranteed, you cannot be sure of what the 216 | result will be. Instead, write a `final_total` method (or something similar) 217 | which guarantees the order of application: 218 | 219 | ```perl 220 | method final_total () { 221 | my $total = ... calculate total 222 | $total = $self->some_surcharge($total); 223 | $total = $self->add_vat($total); 224 | return $total 225 | } 226 | ``` 227 | 228 | A method modifier implicitly adds the method to the list of required methods. 229 | 230 | Modifiers do _not_ get applied to methods until class/role composition is 231 | finished. Otherwise, the modifiers could be applied to the wrong method. 232 | 233 | 234 | --- 235 | 236 | Prev: [Phasers](phasers.md) 237 | Next: [Questions](questions.md) 238 | -------------------------------------------------------------------------------- /rfc/methods.md: -------------------------------------------------------------------------------- 1 | Prev: [Fields](attributes.md) 2 | Next: [Roles](roles.md) 3 | 4 | --- 5 | 6 | # Section 7: Methods 7 | 8 | **This file is automatically generated. If you wish to submit a PR, do not 9 | edit this file directly. Please edit 10 | [templates/rfc/methods.md](https://github.com/Ovid/Cor/tree/master/templates/rfc/methods.md) instead. Use `bin/generate_rfc.pl` to regenerate the RFCs.** 11 | 12 | --- 13 | 14 | # 7.1 Overview 15 | Corinna offers class methods and instance methods. You must also specify if 16 | they override a parent method. 17 | 18 | # 7.2 Instance Methods 19 | Instance methods are defined via `method $identifier (@args) { ... }`. They 20 | automatically have immutable `$class` and `$self` variables injected into 21 | them. `$class` contains the name of the class from which this method was 22 | called. `$self` is an instance of the current class. 23 | 24 | ```perl 25 | method name () { 26 | return defined $title ? "$title $name" : $name; 27 | } 28 | ``` 29 | 30 | Instance methods can access both class data and instance data and call both 31 | instance and class methods. 32 | 33 | # 7.3 Class Methods 34 | Class methods are defined via `method $identifier :common (@args) { ... }`. 35 | They automatically have an immutable `$class` variable injected into them. 36 | This contains the name of the class from which this method was called. 37 | 38 | ```perl 39 | method foo :common () { 40 | say "We were called via the $class class"; 41 | } 42 | ``` 43 | 44 | Class methods cannot call instance methods (since they have no `$self`) and 45 | referencing instance data in a class method should be a compile-time error. 46 | Ths includes trying to reference `$self` in a class method. 47 | 48 | # 7.4 Overridden Methods 49 | If a method in the current class overrides a method in a parent class, a warning 50 | will be issued. To suppress that warning, use `:overrides`. 51 | 52 | ```perl 53 | method name :overrides () { 54 | ... 55 | } 56 | ``` 57 | 58 | Note that instance methods can only override instance methods and class 59 | methods can only override class methods. 60 | 61 | # 7.5 Abstract Methods 62 | Abstract methods are declared as forward declarations. That is, methods 63 | without a method body and without a signature. 64 | 65 | ```perl 66 | method foo; 67 | method bar :common; 68 | ``` 69 | 70 | They have two uses. Any class with an abstract method must declare itself as 71 | abstract. Failure to do so would be a compile-time failure. 72 | 73 | Abstract methods declared in [roles](roles.md) are "required" methods that 74 | must be implemented by the consuming class or by other roles consumed at the 75 | same time. 76 | 77 | *Important* abstract methods must not be listed with arguments. These are a 78 | syntax errors: 79 | 80 | ```perl 81 | method foo (); 82 | method bar ($baz); 83 | ``` 84 | 85 | ## 7.5.1 Checking Abstract Methods 86 | When compiling code containing abstract methods, the check to see if the 87 | method is overridden it should be compile time for a class and composition 88 | time for a role. You should be able to compile an abstract class itself with a 89 | standard `perl -c` check, but any class which inherits from the abstract class 90 | and doesn't override the methods would either need to be declared `abstract` 91 | or have a compile-time failure. 92 | 93 | # 7.6 Private Methods 94 | Private methods are declared with the `private` keyword: 95 | 96 | ```perl 97 | method foo :private () {...} 98 | method bar :private :common () {...} 99 | ``` 100 | 101 | Private methods can only be called from methods defined in the same 102 | namespace and file at _compile time._ 103 | 104 | * Private methods are not inherited 105 | * If a class or role has a `:private` method with the name matching the name of 106 | the method being called, the dispatch is to that method. 107 | * Even if a parent class has a public or private method with the same signature, 108 | the methods in a class will call its private method, not the inherited one 109 | * Roles and classes cannot call each other's private methods 110 | 111 | Note that this means: 112 | 113 | * Roles cannot require private methods 114 | * A role's private methods can never conflict with another role or class's private methods 115 | * You cannot use `:overrides` and `:private` on the same method 116 | 117 | ## 7.6.1 Private Methods in Roles 118 | There is nothing special about private methods in roles, but they are _not_ 119 | flattened into the consuming class and cannot conflict with class methods. 120 | Private methods are bound to the namespace in which they are declared. This 121 | gives us great encapsulation, but does it require the method be bound at 122 | compile-time rather than runtime? If so, does Perl even support that? Or do we 123 | need to do a runtime check every time? 124 | 125 | Thus: 126 | 127 | ``` 128 | role SomeRole { 129 | method role_method () { $self->do_it } 130 | method do_it :private () { say "SomeRole" } 131 | } 132 | class SomeClass :does(SomeRole) { 133 | method class_method () { $self->do_it } 134 | method do_it :private () { say "SomeClass" } 135 | } 136 | my $object = SomeClass->new; 137 | say $object->class_method; # prints "SomeClass" 138 | say $object->role_method; # prints "SomeRole" 139 | ``` 140 | 141 | The above `do_it` methods do not conflict because it's not provided by the role. 142 | It's strictly internal. Further, it cannot be aliased, excluded, or renamed by 143 | the consumer. This gives role authors the ability to write a role and have 144 | fine-grained control over its behavior. 145 | 146 | 147 | --- 148 | 149 | Prev: [Fields](attributes.md) 150 | Next: [Roles](roles.md) 151 | -------------------------------------------------------------------------------- /rfc/mvp.md: -------------------------------------------------------------------------------- 1 | Prev: [Changes](major-changes.md) 2 | Next: [FAQ](faq.md) 3 | 4 | --- 5 | 6 | # Section 14: P5P MVP 7 | 8 | **This file is automatically generated. If you wish to submit a PR, do not 9 | edit this file directly. Please edit 10 | [templates/rfc/mvp.md](https://github.com/Ovid/Cor/tree/master/templates/rfc/mvp.md) instead. Use `bin/generate_rfc.pl` to regenerate the RFCs.** 11 | 12 | --- 13 | 14 | # 14.1 MMVP 15 | The Corinna MVP has been accepted by The Perl Steering Committee and work on 16 | integrating Corinna into the Perl core has begun. 17 | 18 | This is an MMVP (Minimally Minimal Viable Product). This is written after 19 | guidance provided by the Perl Steering committee. This addresses a few 20 | concerns. 21 | 22 | * The more we push into core, the more bugs there will be 23 | * The more we push into core, the more time it will take 24 | * The more we push into core, the more people might rely on misfeatures 25 | 26 | Thus, Corinna development for core will be staged, with subsequent work to 27 | realize the full MVP. 28 | 29 | Thus, we want the simplest thing that could possibly work in the first release. 30 | 31 | What follows is a minimal description of what we'd like for the MMVP. The plan 32 | is to implement this in seven stages. Each stage will be pushed separately, 33 | giving us time to test and verify that it does what we need. Note that some 34 | features are specified, in terms of semantics, but in the spirit of "no plan 35 | survives first contact with the enemy," we will nail some of the syntax down as 36 | we write tests to verify the behavior and solicit feedback from those playing 37 | with it. 38 | 39 | # 14.2 The Seven Stages 40 | ## 14.2.1 1. Classes 41 | Initial `use feature 'class'` to add basic `class`, `field`, and `method` keywords. 42 | This will include `ADJUST` and `ADJUSTPARAMS` phasers. 43 | 44 | No roles, no class/field/method attributes, no MOP. 45 | 46 | ## 14.2.2 2. Class inheritance - class :isa() attr 47 | Single-inheritance only. 48 | 49 | 50 | ``` 51 | class Employee :isa(Person) { 52 | field $employee_id :param; 53 | } 54 | ``` 55 | 56 | ## 14.2.3 3. Roles, and class/role :does() attr 57 | ``` 58 | role Role::Created { 59 | field $created :reader = time; 60 | } 61 | 62 | class SomeClass :does(Role::Created) { 63 | ... 64 | } 65 | ``` 66 | 67 | Note: for roles, the current implementation of required methods is to simply 68 | create a forward declaration: `method foo;` without listing the signature. 69 | Signatures are currently not introspectable, so we cannot use them to verify 70 | that the correct required method is present, so we only use the name. 71 | Including a signature in the forward declaration might be self-documenting, 72 | but for now, we'd prefer to omit it because this might impact forward 73 | compatibility. 74 | 75 | ## 14.2.4 4. Various "convenience" attributes - 76 | ``` 77 | field :reader :writer :accessor :mutator 78 | field :weak 79 | field :param 80 | method :overrides() 81 | ``` 82 | 83 | At this stage, most of the basics are in place and we have a useful system. 84 | 85 | ## 14.2.5 5. Field initialiser blocks 86 | A postfix `{ expression }` block on a field can set its default value. 87 | 88 | ``` 89 | field $answer :param = 42; 90 | ``` 91 | 92 | ## 14.2.6 6. MOP 93 | The metaobject protocol, similar to `Class::MOP`. 94 | 95 | ## 14.2.7 7. Method modifiers (around, before, after) 96 | They behave similarly to the `around`, `before`, and `after` method modifiers 97 | from Moo/se. 98 | 99 | # 14.3 Missing Features 100 | Obviously, quite a few features are missing from this RFC of Corinna. Our 101 | intent is to roll them out as quickly as is feasible, but to ensure the 102 | foundation is stable. 103 | 104 | # 14.4 Potentially Breaking Changes 105 | The following features are not planned for the MMVP and might break your code 106 | in subsequent releases. 107 | 108 | * Error on unknown constructor parameters 109 | * Deterministic destruction might cause issues when introduced 110 | * Many other features (see the github repo) 111 | 112 | There are a ton of other feature omitted, but have been not mentioned here 113 | because they're not even part of the Corinna MVP (role exclusion and aliasing 114 | being a perfect example). 115 | 116 | 117 | --- 118 | 119 | Prev: [Changes](major-changes.md) 120 | Next: [FAQ](faq.md) 121 | -------------------------------------------------------------------------------- /rfc/phasers.md: -------------------------------------------------------------------------------- 1 | Prev: [Roles](roles.md) 2 | Next: [Method Modifiers](method-modifiers.md) 3 | 4 | --- 5 | 6 | # Section 9: Phasers 7 | 8 | **This file is automatically generated. If you wish to submit a PR, do not 9 | edit this file directly. Please edit 10 | [templates/rfc/phasers.md](https://github.com/Ovid/Cor/tree/master/templates/rfc/phasers.md) instead. Use `bin/generate_rfc.pl` to regenerate the RFCs.** 11 | 12 | --- 13 | 14 | At the present time, some final details of phasers are still being decided. 15 | For Corinna, we have two new phasers, `ADJUST` and `DESTRUCT`. 16 | 17 | # 9.1 `ADJUST` 18 | The `ADJUST` phaser is called just after object construction (`new(..)`) but 19 | before the object is returned. This allows the developer to apply additional 20 | logic which cannot be cleanly represented by merely assigning values to fields. 21 | 22 | All class and instance data is available in the `ADJUST` phaser. 23 | 24 | # 9.2 `DESTRUCT` 25 | The `DESTRUCT` phaser is called when the current instance goes out of scope. 26 | 27 | ```perl 28 | DESTRUCT { 29 | if ( 'DESTRUCT' eq ${^GLOBAL_PHASE} ) { 30 | ... 31 | } 32 | ... 33 | } 34 | ``` 35 | 36 | There is ongoing discussion about whether a boolean "in global destruction" 37 | argument should be provided, or a possible destruction object should be 38 | provided. However, no phases currently take arguments, so it's an open 39 | question. 40 | 41 | One possible use of a destruction object is that it could check if a 42 | `PERL_DESTRUCT_TRACE` (or similar) environment variable is true and capture a 43 | stack trace of where it went out of scope. This would be very useful for 44 | debugging. 45 | 46 | ## 9.2.1 Deterministic Destruction 47 | Currently, `DESTROY` run on standard Perl objects tends to destroy your state 48 | in random order. It's easy to try to use the object in `DESTROY` and find that 49 | it's incomplete. 50 | 51 | In Corinna, fields are indended to be destroyed in reverse order of 52 | declaration, instance fields and then class (common) fields, from parent to 53 | child. Consider the following (note that the syntax for `:common` might 54 | change): 55 | 56 | ```perl 57 | class Parent { 58 | field $one; 59 | field $two; 60 | field $three :common; 61 | } 62 | 63 | class Child :isa(Parent) { 64 | field $four; 65 | field $five :common; 66 | field $six; 67 | } 68 | ``` 69 | 70 | The destruction order should be guaranteed to be: 71 | 72 | 1. `$six` 73 | 2. `$four` 74 | 3. `$five` # class data 75 | 4. `$two` 76 | 5. `$one` 77 | 6. `$three` # class data 78 | 79 | **Important**: at the current time, class data will only be destroyed in 80 | global destruction. In the future, if Corinna classes can become "first class" 81 | in the Perl language, if a class can fall out of scope prior to global 82 | destruction, then yes, class data can be destroyed prior to global 83 | construction (e.g, for an anonymous class created at runtime). 84 | 85 | ## 9.2.2 Incomplete Destruction 86 | Sometimes you have code that *must* use a `DESTRUCT`, but it's unclear if the 87 | object has been set up properly (perhaps it through an exception in `ADJUST`, 88 | for example). Running `DESTRUCT` on data that doesn't exist is fraught with 89 | bugs. The following is one way of handling that: 90 | 91 | ```perl 92 | class MyClass { 93 | field $constructed; 94 | 95 | ADJUST { 96 | ... 97 | $constructed = 1; 98 | } 99 | 100 | DESTRUCT { 101 | return unless $constructed; 102 | ... 103 | } 104 | } 105 | ``` 106 | 107 | # 9.3 Phaser Call Order 108 | Class `ADJUST` phasers are called on the root class before its roles `ADJUST` 109 | phasers which are called before child `ADJUST` phasers and its roles phaswers. 110 | 111 | `DESTRUCT` role phasers are called before class `DESTRUCT` phasers which are 112 | called before parent `DESTRUCT` phasers. 113 | 114 | **Important**: for a given level of the inheritance hierarchy, if more than 115 | one role is consumed, the order in which its `ADJUST` and `DESTRUCT` phasers 116 | are called is not guaranteed. This is deliberate to prevent people from 117 | assuming they can rely on role consumption order. 118 | 119 | 120 | --- 121 | 122 | Prev: [Roles](roles.md) 123 | Next: [Method Modifiers](method-modifiers.md) 124 | -------------------------------------------------------------------------------- /rfc/questions.md: -------------------------------------------------------------------------------- 1 | Prev: [Method Modifiers](method-modifiers.md) 2 | Next: [Quotes](quotes.md) 3 | 4 | --- 5 | 6 | # Section 11: Questions 7 | 8 | **This file is automatically generated. If you wish to submit a PR, do not 9 | edit this file directly. Please edit 10 | [templates/rfc/questions.md](https://github.com/Ovid/Cor/tree/master/templates/rfc/questions.md) instead. Use `bin/generate_rfc.pl` to regenerate the RFCs.** 11 | 12 | --- 13 | 14 | # 11.1 Open issues for the RFC 15 | ## 11.1.1 Corinna v Other objects 16 | What would be the easiest way to distinguish between Corinna and other kinds 17 | of objects? I think having a base class of `OBJECT` solves this. 18 | 19 | Thus, anyone could simply ask if `$thing isa OBJECT` and find out of it's 20 | Corinna or not. This would be very useful if they want to use the MOP and 21 | discover it doesn't work on a regular blessed reference. 22 | 23 | ## 11.1.2 Multiple Variables Types In A Field 24 | Can fields have more than one kind of variable? 25 | 26 | ```perl 27 | field ($x, @y); 28 | ``` 29 | 30 | If so, do we disallow attributes? 31 | 32 | ## 11.1.3 Twigils? 33 | ```perl 34 | field $:x; 35 | 36 | method inc ($x) { 37 | $:x += $x; 38 | } 39 | ``` 40 | 41 | Pros: 42 | 43 | * You can't accidentally shadow class/instance data. 44 | * Great Huffman encoding for "this is not a regular variable" 45 | * Easy to syntax highlight 46 | 47 | Cons: 48 | 49 | * No direct parser support for twigils 50 | * Is somewhat controversial 51 | * May contribute to "line-noise" complaints 52 | 53 | ## 11.1.4 Overridding Fields 54 | A method can override a parent method explicitly to avoid a warning: 55 | 56 | ```perl 57 | method move($x,$y) :overrides {...} 58 | ``` 59 | 60 | Should methods generated via `field` attributes be allowed to override parents? 61 | If so, how do we signal this? 62 | 63 | ## 11.1.5 `can`, `does`, and `isa` 64 | It has been suggested that we offer new versions of `can`, `does`, and `isa`. 65 | They would not take arguments. 66 | 67 | * `can`: returns all methods the current class can do (including inherited) 68 | * `does`: returns all roles the current class does 69 | * `isa`: returns the iteheritance list 70 | 71 | Because these methods currently do not take arguments, this might be extending 72 | them instead of modifiying them. However, this would still be modifying 73 | current behavior. Or we could put this in an `OBJECT` base class for Corinna. 74 | However, this would mean the external behaviors would be different for Corinna 75 | and other objects. 76 | 77 | I think this is probaby out of scope for Corinna. 78 | 79 | ## 11.1.6 Inline POD? 80 | Because we need a postfix block, many people will be disappointed that we 81 | won't have inline POD quite as neat as what we had: 82 | 83 | ```perl 84 | class Foo { 85 | 86 | =head1 METHODS 87 | 88 | =head2 C 89 | 90 | This method does something 91 | 92 | =cut 93 | 94 | method bar() { ... } 95 | } 96 | ``` 97 | 98 | 99 | --- 100 | 101 | Prev: [Method Modifiers](method-modifiers.md) 102 | Next: [Quotes](quotes.md) 103 | -------------------------------------------------------------------------------- /rfc/quotes.md: -------------------------------------------------------------------------------- 1 | Prev: [Questions](questions.md) 2 | Next: [Changes](major-changes.md) 3 | 4 | --- 5 | 6 | # Section 12: Quotes 7 | 8 | **This file is automatically generated. If you wish to submit a PR, do not 9 | edit this file directly. Please edit 10 | [templates/rfc/quotes.md](https://github.com/Ovid/Cor/tree/master/templates/rfc/quotes.md) instead. Use `bin/generate_rfc.pl` to regenerate the RFCs.** 11 | 12 | --- 13 | 14 | # 12.1 Overview 15 | As Corinna has gotten closer to submitting an RFC to P5P, people have been 16 | asking about the people who developed or influenced Corinna. This list is not 17 | exhaustive and changes are to be expected. 18 | 19 | Names are in alphabetical order. 20 | 21 | ## 12.1.1 [Curtis Poe, a.k.a. Ovid](https://metacpan.org/author/OVID/), Perl book author and lead designer of Corinna 22 | > We love Perl, but it's struggling. We know that. Corinna is not 23 | > going to save it. Nor is a better code of conduct. Nor is Perl 7, 24 | > concurrency, types, or \. Instead, it's 25 | > all of these things, and many more, with a strong community to support the 26 | > language, that will continue to help Perl thrive. Let's start being the 27 | > people we tell our children to be. 28 | 29 | ## 12.1.2 [Damian Conway, a.k.a. _That_ Damian](https://metacpan.org/author/DCONWAY), Computer scientist and (too many accolades to list) 30 | > When people ask me whether I support the Corinna proposal, my response is: 31 | > OH, HELL YES! I’ve literally been waiting two decades to see Perl gain a 32 | > proper, built-in, declarative, encapsulated, and performant OO system. And 33 | > this, I firmly believe, is it. 34 | 35 | [Read his full statement at blogs.perl.org](http://blogs.perl.org/users/damian_conway/2021/08/a-dream-realized.html). 36 | 37 | ## 12.1.3 [Dan Book, a.k.a. Grinnz](https://metacpan.org/author/DBOOK), CPAN author and docs wrangler 38 | > Moose and its derivatives provide a comprehensive and popular object 39 | > framework for Perl, but some parts have always been workarounds for the 40 | > limitations of the hash-based object format and existing Perl syntax and 41 | > semantics. Corinna is a methodical reimagination of what a modern built-in 42 | > Perl object framework could look like without those limits, informed by the 43 | > decades of object-oriented applications within and without. 44 | 45 | ## 12.1.4 [Matt Trout, a.k.a. mst](https://metacpan.org/author/MSTROUT), Creator of Moo 46 | > The Corinna process is highly promising, and I'm very much enjoying the fact 47 | > that almost everybody involved in the design process is a prolific and 48 | > highly experienced Moo/se user and yet we still manage to argue about 49 | > absolutely everything. 50 | 51 | ## 12.1.5 [Sawyer X, a.k.a. Sawyer X](https://metacpan.org/author/XSAWYERX), former Perl pumpking 52 | > Corinna is a feature-rich, modern OO framework. It has a clean, minimal, yet 53 | > expressive syntax, keeping OO best practices as its defaults. It comes from 54 | > a veteran of OO in general and in Perl in particular (Moo, Moose, and 55 | > everything under the sun before and between those), showing how to design a 56 | > framework that avoids the pitfalls we only discover years into practice. It 57 | > is not a competitor to Moose. It is the next level from it. 58 | > 59 | > Corinna is how Perl should look like and how we should write it. Expressive, 60 | > minimal and exact, clean, and powerful. 61 | 62 | ## 12.1.6 [Stevan Little, a.k.a. "Damnit, Stevan"](https://metacpan.org/author/STEVAN), creator of Moose 63 | > I am glad Ovid is working on this, better him than me 😛 64 | 65 | 66 | --- 67 | 68 | Prev: [Questions](questions.md) 69 | Next: [Changes](major-changes.md) 70 | -------------------------------------------------------------------------------- /rfc/roles.md: -------------------------------------------------------------------------------- 1 | Prev: [Methods](methods.md) 2 | Next: [Phasers](phasers.md) 3 | 4 | --- 5 | 6 | # Section 8: Roles 7 | 8 | **This file is automatically generated. If you wish to submit a PR, do not 9 | edit this file directly. Please edit 10 | [templates/rfc/roles.md](https://github.com/Ovid/Cor/tree/master/templates/rfc/roles.md) instead. Use `bin/generate_rfc.pl` to regenerate the RFCs.** 11 | 12 | --- 13 | 14 | # 8.1 Background 15 | Roles in Corinna are based on the [Traits, composable units of 16 | behavior](http://scg.unibe.ch/archive/papers/Scha02bTraits.pdf) 17 | paper, we particularly find interest in [Traits: the formal 18 | model](http://scg.unibe.ch/archive/papers/Scha02cTraitsModel.pdf). 19 | 20 | In particular, according to the formal model, traits are designed to be both 21 | commutative (`a+b=b+a`) and associative (`(a+b)+c=a+(b+c)`). While we cannot 22 | perfectly guarantee this in the face of method modifiers, we will see to 23 | return to this model. 24 | 25 | # 8.2 Overview 26 | Roles are designed to share small sets of behaviors which address 27 | cross-cutting concerns. Roles may consume other roles, but they may not 28 | inherit from classes, nor may they be inherited from. 29 | 30 | Roles may require one or more methods to be implemented. All abstract methods 31 | in roles are considered to be required. For Corinna, any forward declaration 32 | (a method declared without a body: `method foo;`) of a method is considered an 33 | abstract method. 34 | 35 | ```perl 36 | role SomeRole { 37 | method foo; 38 | method bar :common; 39 | ... 40 | } 41 | ``` 42 | 43 | Any non-private methods with method bodies are considered to be methods the 44 | role provides. These may be both class and instance methods. 45 | 46 | *Important* required methods must not be listed with arguments. These are a 47 | syntax errors: 48 | 49 | ```perl 50 | method foo (); 51 | method bar ($baz); 52 | ``` 53 | 54 | ```perl 55 | role SomeRole { 56 | method foo () { ... } # instance method provided 57 | method bar :common () { ... } # class method provided 58 | method baz :private () { ... } # private methods are not provided 59 | } 60 | ``` 61 | 62 | Any fields declared in the role are completely private unless standard 63 | field attributes are used to expose them 64 | 65 | ```perl 66 | role SomeRole { 67 | field $name :reader; # ->name is provided 68 | field $age; # private to this role 69 | } 70 | ``` 71 | 72 | Roles may _not_ access the fields or methods of the class the 73 | role is consumed into unless those have already been exposed in the public 74 | interface. 75 | 76 | # 8.3 Example 77 | It is entirely possible, for example, to want to have an identical mechanism 78 | to provide unique, repeatable UUIDs to different classes. It might look like 79 | this: 80 | 81 | ```perl 82 | role Role::UUID { 83 | use Data::UUID; 84 | 85 | # these are private to this role 86 | my $uuid = Data::UUID->new; 87 | my $namespace_uuid = $uuid->create_str; 88 | 89 | # abtract methods in roles are required 90 | method name; 91 | 92 | method uuid () { 93 | return $uuid->create_from_name_str( $namespace_uuid, $self->name ); 94 | } 95 | } 96 | ``` 97 | 98 | And to use that in your class: 99 | 100 | ```perl 101 | class Person :does(Role::UUID) { 102 | field $name :param :reader; 103 | } 104 | ``` 105 | 106 | And using that: 107 | 108 | ```perl 109 | my $alice = Person->new(name => 'Alice'); 110 | say $alice->uuid; 111 | ``` 112 | 113 | The above will create a unique, repeatable UUID for a given `Person.name` 114 | (only repeats in a single process due to how UUIDs work). 115 | 116 | Roles may consume other roles and classes may consume one or more roles. Any 117 | method name conflicts are fatal, if and only if the methods come from 118 | different namespaces. 119 | 120 | ```perl 121 | role A :does(C) { method a () { ... } } 122 | role B :does(C) { method b () { ... } } 123 | role C { method c () { ... } } 124 | 125 | class SomeClass :does(A, B) { ... } 126 | ``` 127 | 128 | Thus, in the above example, though A and B both pull in the `c()` method from 129 | role C, there is no conflict because it is the same method. 130 | 131 | However, if `SomeClass` defined a `c()` method, there will be a conflict. 132 | 133 | # 8.4 Aliasing and Excluding 134 | For the MVP, we will not be providing aliasing and excluding of methods. 135 | 136 | # 8.5 `ADJUST` and `DESTRUCT` 137 | Both the `ADJUST` and `DESTRUCT` [phasers](phasers.md) will be allowed in 138 | roles. Class `ADJUST` phasers are called before its roles `ADJUST` phasers 139 | which are called before child `ADJUST` phasers and its roles phaswers. 140 | 141 | `DESTRUCT` role phasers are called before class `DESTRUCT` phasers which are 142 | called before parent `DESTRUCT` phasers. 143 | 144 | **Important**: for a given level of the inheritance hierarchy, if more than 145 | one role is consumed, the order in which its `ADJUST` and `DESTRUCT` phasers 146 | are called is not guaranteed. 147 | 148 | # 8.6 Conflicts 149 | Consuming multiple roles may result in method name conflicts. This is a fatal 150 | error. If a class consumes a role with a method name that conflicts with a 151 | role method, the class method wins, but a warning is issued. 152 | 153 | # 8.7 Questions 154 | ## 8.7.1 Changing access level of role methods? 155 | No role method marked as `private` should be composed into the consuming class 156 | or role. However, the consumer may need the behavior, but not want to expose 157 | it. 158 | 159 | If a method exported by a role is public but the consumer does not wish to 160 | expose that part of its interface, should it have a way to adjust the access 161 | level to C? 162 | 163 | 164 | --- 165 | 166 | Prev: [Methods](methods.md) 167 | Next: [Phasers](phasers.md) 168 | -------------------------------------------------------------------------------- /rfc/toc.md: -------------------------------------------------------------------------------- 1 | Prev: [README](/README.md) 2 | Next: [Overview](overview.md) 3 | 4 | --- 5 | 6 | # Section 1: Table of Contents 7 | 8 | **This file is automatically generated. If you wish to submit a PR, do not 9 | edit this file directly. Please edit 10 | [templates/rfc/toc.md](https://github.com/Ovid/Cor/tree/master/templates/rfc/toc.md) instead. Use `bin/generate_rfc.pl` to regenerate the RFCs.** 11 | 12 | --- 13 | 14 | # 1.1 Sections 15 | 16 | # [Section: 1: Table of Contents](toc.md) 17 | 18 | * `..` 1.1 Sections 19 | 20 | # [Section: 2: Overview](overview.md) 21 | 22 | * `..` 2.1 Preamble 23 | * `..` 2.2 Abstract 24 | * `..` 2.3 Motivation 25 | * `..` 2.4 Rationale 26 | * `..` 2.5 Specification 27 | * `..` 2.6 Backwards Compatibility 28 | * `....` 2.6.1 Syntax 29 | * `....` 2.6.2 Tooling 30 | * `....` 2.6.3 Feature Guard 31 | * `..` 2.7 Security Implications 32 | * `..` 2.8 Examples 33 | * `..` 2.9 Prototype Implementation 34 | * `..` 2.10 Benefits of this approach 35 | * `....` 2.10.1 Compile-time failures 36 | * `....` 2.10.2 Encapsulation 37 | * `....` 2.10.3 Less Code 38 | * `....` 2.10.4 Cleaner Interfaces 39 | * `....` 2.10.5 No MRO Pain 40 | * `..` 2.11 FAQ 41 | * `....` 2.11.1 Why not Moo/se or alternatives in the core? 42 | * `..` 2.12 Open Issues 43 | * `..` 2.13 Scope for future work 44 | * `..` 2.14 Contributors 45 | * `..` 2.15 Copyright 46 | 47 | # [Section: 3: Grammar](grammar.md) 48 | 49 | * `..` 3.1 Class and Role Grammar 50 | * `..` 3.2 Method Grammar 51 | * `..` 3.3 Field Grammar 52 | 53 | # [Section: 4: Classes](classes.md) 54 | 55 | * `..` 4.1 Overview 56 | * `..` 4.2 Discussion 57 | * `....` 4.2.1 Versions 58 | * `....` 4.2.2 Inheritance 59 | * `....` 4.2.3 Roles 60 | * `....` 4.2.4 Abstract Classes 61 | * `....` 4.2.5 Subroutines versus Methods 62 | 63 | # [Section: 5: Class Construction](class-construction.md) 64 | 65 | * `..` 5.1 Overview 66 | * `..` 5.2 Steps 67 | * `....` 5.2.1 Step 1 Verify even-sized list of args 68 | * `....` 5.2.2 Step 2 Constructor keys may not be references 69 | * `....` 5.2.3 Step 3 Find constructor args 70 | * `....` 5.2.4 Step 4 Err out on unknown keys 71 | * `....` 5.2.5 Step 5 `new()` 72 | * `....` 5.2.6 Step 6 `ADJUST` 73 | * `..` 5.3 MOP Pseudocode 74 | 75 | # [Section: 6: Fields](attributes.md) 76 | 77 | * `..` 6.1 Overview 78 | * `..` 6.2 Field Creation 79 | * `....` 6.2.1 Field Initialization 80 | * `....` 6.2.2 Field Destruction 81 | * `....` 6.2.3 Field Attributes 82 | * `......` 6.2.3.1 `:param(optional_identifier)` 83 | * `......` 6.2.3.2 `:reader(optional_identifier)` 84 | * `......` 6.2.3.3 `:writer(optional_identifier)` 85 | * `......` 6.2.3.4 `:predicate(optional_identifier)` 86 | * `......` 6.2.3.5 `:name(optional_identifier)` 87 | * `......` 6.2.3.6 `:handles(...)` 88 | * `........` 6.2.3.6.1 List of Identifiers and Identifier:Identifier Mappings 89 | * `......` 6.3.1.1 `:common` 90 | 91 | # [Section: 7: Methods](methods.md) 92 | 93 | * `..` 7.1 Overview 94 | * `..` 7.2 Instance Methods 95 | * `..` 7.3 Class Methods 96 | * `..` 7.4 Overridden Methods 97 | * `..` 7.5 Abstract Methods 98 | * `....` 7.5.1 Checking Abstract Methods 99 | * `..` 7.6 Private Methods 100 | * `....` 7.6.1 Private Methods in Roles 101 | 102 | # [Section: 8: Roles](roles.md) 103 | 104 | * `..` 8.1 Background 105 | * `..` 8.2 Overview 106 | * `..` 8.3 Example 107 | * `..` 8.4 Aliasing and Excluding 108 | * `..` 8.5 `ADJUST` and `DESTRUCT` 109 | * `..` 8.6 Conflicts 110 | * `..` 8.7 Questions 111 | * `....` 8.7.1 Changing access level of role methods? 112 | 113 | # [Section: 9: Phasers](phasers.md) 114 | 115 | * `..` 9.1 `ADJUST` 116 | * `..` 9.2 `DESTRUCT` 117 | * `....` 9.2.1 Deterministic Destruction 118 | * `....` 9.2.2 Incomplete Destruction 119 | * `..` 9.3 Phaser Call Order 120 | 121 | # [Section: 10: Method Modifiers](method-modifiers.md) 122 | 123 | * `..` 10.1 Overview 124 | * `..` 10.2 Why use them? 125 | * `..` 10.3 Before, after, and around modifiers 126 | * `....` 10.3.1 Before and after Modifiers 127 | * `....` 10.3.2 Around modifiers 128 | * `....` 10.3.3 Execution order of method modifiers and inheritance 129 | * `..` 10.4 Exceptions and stack traces 130 | * `..` 10.5 Caveats 131 | 132 | # [Section: 11: Questions](questions.md) 133 | 134 | * `..` 11.1 Open issues for the RFC 135 | * `....` 11.1.1 Corinna v Other objects 136 | * `....` 11.1.2 Multiple Variables Types In A Field 137 | * `....` 11.1.3 Twigils? 138 | * `....` 11.1.4 Overridding Fields 139 | * `....` 11.1.5 `can`, `does`, and `isa` 140 | * `....` 11.1.6 Inline POD? 141 | 142 | # [Section: 12: Quotes](quotes.md) 143 | 144 | * `..` 12.1 Overview 145 | * `....` 12.1.1 [Curtis Poe, a.k.a. Ovid](https://metacpan.org/author/OVID/), Perl book author and lead designer of Corinna 146 | * `....` 12.1.2 [Damian Conway, a.k.a. _That_ Damian](https://metacpan.org/author/DCONWAY), Computer scientist and (too many accolades to list) 147 | * `....` 12.1.3 [Dan Book, a.k.a. Grinnz](https://metacpan.org/author/DBOOK), CPAN author and docs wrangler 148 | * `....` 12.1.4 [Matt Trout, a.k.a. mst](https://metacpan.org/author/MSTROUT), Creator of Moo 149 | * `....` 12.1.5 [Sawyer X, a.k.a. Sawyer X](https://metacpan.org/author/XSAWYERX), former Perl pumpking 150 | * `....` 12.1.6 [Stevan Little, a.k.a. "Damnit, Stevan"](https://metacpan.org/author/STEVAN), creator of Moose 151 | 152 | # [Section: 13: Changes](major-changes.md) 153 | 154 | 155 | # [Section: 14: P5P MVP](mvp.md) 156 | 157 | * `..` 14.1 MMVP 158 | * `..` 14.2 The Seven Stages 159 | * `....` 14.2.1 1. Classes 160 | * `....` 14.2.2 2. Class inheritance - class :isa() attr 161 | * `....` 14.2.3 3. Roles, and class/role :does() attr 162 | * `....` 14.2.4 4. Various "convenience" attributes - 163 | * `....` 14.2.5 5. Field initialiser blocks 164 | * `....` 14.2.6 6. MOP 165 | * `....` 14.2.7 7. Method modifiers (around, before, after) 166 | * `..` 14.3 Missing Features 167 | * `..` 14.4 Potentially Breaking Changes 168 | 169 | # [Section: 15: FAQ](faq.md) 170 | 171 | * `..` 15.1 Overview 172 | * `....` 15.1.1 Why can't I inherit from blessed objects? 173 | * `....` 15.1.2 But I _need_ to inherit from a blessed object! 174 | * `....` 15.1.3 Why can't I use subroutines for methods? 175 | * `....` 15.1.4 Will `class` break existing code? 176 | * `....` 15.1.5 How can I refactor existing objects with `class`? 177 | * `....` 15.1.6 Are there any interesting projects being written with `class`? 178 | * `....` 15.1.7 Are a class and a package the same thing? 179 | * `....` 15.1.8 You keep writing classes with a postfix block. Is that required? 180 | * `....` 15.1.9 Is there a tutorial? 181 | * `....` 15.1.10 How do I know which `class` features my Perl supports? 182 | 183 | 184 | --- 185 | 186 | Prev: [README](/README.md) 187 | Next: [Overview](overview.md) 188 | -------------------------------------------------------------------------------- /t/corinna/example/person.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | package TestsFor::Corinna::Example::Person; 4 | use Keyword::DEVELOPMENT; 5 | use Test::Most; 6 | use Time::HiRes 'nanosleep'; 7 | use Corinna::Example::Person; 8 | 9 | my $villain = 10 | Corinna::Example::Person->new( title => 'Dr.', name => 'Zacharary Smith' ); 11 | is $villain->num_people, 1, 'We should have one person'; 12 | 13 | nanosleep 5; 14 | my $boy = Corinna::Example::Person->new( name => 'Will Robinson' ); 15 | is $villain->num_people, 2, 'We should have two people'; 16 | cmp_ok $boy->created, '>', $villain->created, 17 | 'The boy should be created after the villain'; 18 | 19 | is $villain->name, 'Dr. Zacharary Smith', 20 | 'name() should include a title if it exists'; 21 | is $boy->name, 'Will Robinson', '... and skip the title if it does not exist'; 22 | 23 | undef $villain; 24 | is $boy->num_people, 1, 25 | 'We should have one person left after killing the villain'; 26 | 27 | DEVELOPMENT { 28 | # TODO Be be able to call methods on classes 29 | is +Corinna::Example::Person->num_people, 1, 30 | 'We should be able to call class methods on the class'; 31 | undef $boy; 32 | is +Corinna::Example::Person->num_people, 0, 33 | '... and when their are no more instances, we should have no more people'; 34 | } 35 | 36 | done_testing; 37 | -------------------------------------------------------------------------------- /t/corinna/rfc/config/reader.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use Test::Most; 4 | use Corinna::RFC::Config::Reader; 5 | 6 | my $reader = Corinna::RFC::Config::Reader->new( file => 't/test.conf' ); 7 | my $expected = { 8 | rfcs => [ 9 | { value => 'overview.md', key => 'Overview' }, 10 | { key => 'Grammar', value => 'grammar.md' }, 11 | { key => 'Classes', value => 'classes.md' }, 12 | { key => 'Attributes', value => 'attributes.md' }, 13 | { value => 'methods.md', key => 'Methods' } 14 | ], 15 | main => { 16 | toc_marker => '{{TOC}}', 17 | rfc_dir => 'rfc', 18 | template_dir => 'templates', 19 | github => 'https://github.com/Ovid/Cor', 20 | toc => 'toc.md', 21 | readme => 'README.md' 22 | } 23 | }; 24 | 25 | my $config = $reader->config; 26 | eq_or_diff $config, $expected, 'Config structure matches expected'; 27 | delete $config->{rfcs}; 28 | eq_or_diff $reader->config, $expected, 29 | 'Mutating the reference returned by ->config should not mutate the underlying ddta'; 30 | 31 | # Long-term, we should be able to add tests to verify that private methods are 32 | # not returned by ->can, nor should exported functions. 33 | 34 | done_testing; 35 | -------------------------------------------------------------------------------- /t/corinna/rfc/role/file.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use utf8; 4 | use Test::Most; 5 | use Object::Pad 0.58; 6 | use File::Temp 'tempfile'; 7 | 8 | class Example :does(Corinna::RFC::Role::File) { 9 | method write_it( $filename, $string ) { 10 | $self->_splat( $filename, $string ); 11 | } 12 | 13 | method read_it($filename) { 14 | $self->_slurp($filename); 15 | } 16 | } 17 | 18 | my ( $fh, $filename ) = tempfile(); 19 | 20 | my $string = <<'END'; 21 | This is 22 | a test string 23 | with réally 24 | weïrd 25 | content. 26 | END 27 | 28 | explain "As of Object::Pad .52, we cannot call methods on non-instances"; 29 | my $example = Example->new; 30 | ok $example->write_it( $filename, $string ), 31 | 'We should be able to write data to a file'; 32 | ok my $result = $example->read_it($filename), '... and read it back out'; 33 | is $result, $string, '... and have it unchanged'; 34 | 35 | done_testing; 36 | -------------------------------------------------------------------------------- /t/object/types/factory.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use Test::Most; 4 | use Storable 'dclone'; 5 | use Scalar::Util 'refaddr'; 6 | use Object::Types::Factory; 7 | $Object::Types::Factory::Test::Mode = 1; 8 | 9 | subtest 'Any' => sub { 10 | my $Any = Object::Types::Concrete::Any->new; 11 | ok $Any->validate(undef), 'Any() always passes'; 12 | }; 13 | 14 | subtest 'Int' => sub { 15 | my $Int = Object::Types::Concrete::Int->new; 16 | ok $Int->validate(37), 'Int() should validate ints'; 17 | ok !$Int->validate(32.8), '... but not non-ints'; 18 | }; 19 | 20 | subtest 'Num' => sub { 21 | my $Num = Object::Types::Concrete::Num->new; 22 | ok $Num->validate(37), 'Num() should validate ints'; 23 | ok $Num->validate(32.8), '... and floats'; 24 | ok !$Num->validate("Ovid"), '... but not non-numbers'; 25 | }; 26 | 27 | subtest 'Str' => sub { 28 | my $Str = Object::Types::Concrete::Str->new; 29 | ok $Str->validate(37), 'Str() should validate ints'; 30 | ok $Str->validate(32.8), '... and floats'; 31 | ok $Str->validate("Ovid"), '... and non-numbers'; 32 | ok !$Str->validate(' '), '... but not empty strings'; 33 | }; 34 | 35 | subtest 'Regex' => sub { 36 | my $Regex = Object::Types::Concrete::Regex->new( regex => qr/ab*c/ ); 37 | ok $Regex->validate('ac'), 'Strings matching the regex should valiadate'; 38 | ok !$Regex->validate('axc'), 39 | '... and strings not matching the regex should not valiadate'; 40 | $Regex = Object::Types::Concrete::Regex->new( regex => qr/x/ ); 41 | ok !$Regex->validate( [] ), 'Regexes can never validate references'; 42 | }; 43 | 44 | subtest 'Enum' => sub { 45 | my $Enum = 46 | Object::Types::Concrete::Enum->new( elements => [ 1, 2, 'Ovid' ] ); 47 | ok $Enum->validate('Ovid'), 'Values in our enum should validate'; 48 | ok $Enum->validate(1), '... no matter which they are'; 49 | ok !$Enum->validate(42), '... values not in the enum should not validate'; 50 | 51 | my $Enum_with_types = Object::Types::Concrete::Enum->new( 52 | elements => [ 'Ovid', Object::Types::Concrete::Int->new ] ); 53 | ok $Enum_with_types->validate('Ovid'), 'Values in our enum should validate'; 54 | ok $Enum_with_types->validate(7), '... as should values matching types'; 55 | ok !$Enum_with_types->validate(7.3), 56 | '... but not values not matching either values or types'; 57 | }; 58 | 59 | subtest 'ArrayRef' => sub { 60 | my $ArrayRef = Object::Types::Concrete::ArrayRef->new; 61 | ok $ArrayRef->validate( [] ), 62 | 'All arrayrefs match an empty array reference'; 63 | ok $ArrayRef->validate( [ 1, 3.14, {}, [], 'Ovid' ] ), 64 | '... no matter what they contain'; 65 | ok !$ArrayRef->validate( {} ), 66 | '... but they will not validate against things that are not array references'; 67 | 68 | my $ArrayRef_of_ints = Object::Types::Concrete::ArrayRef->new( 69 | contains => Object::Types::Concrete::Int->new ); 70 | ok $ArrayRef_of_ints->validate( [] ), 71 | 'arrayrefs of ints match an empty array reference'; 72 | ok $ArrayRef_of_ints->validate( [ 1, 4 ] ), '... or an arrayref of ints'; 73 | ok !$ArrayRef_of_ints->validate( [ 4, undef ] ), 74 | '... but not an array ref which contains something that is not an int'; 75 | }; 76 | 77 | subtest 'HashRef' => sub { 78 | my $HashRef = Object::Types::Concrete::HashRef->new; 79 | ok $HashRef->validate( {} ), 'HashRef validates against empty hashrefs'; 80 | ok $HashRef->validate( { foo => 1 } ), 81 | 'HashRef validates against anything in a hashref'; 82 | ok !$HashRef->validate( [] ), 83 | 'HashRef does not validate against non-hashrefs'; 84 | 85 | my $HashRef_of_ints = Object::Types::Concrete::HashRef->new( 86 | contains => Object::Types::Concrete::Int->new ); 87 | ok $HashRef_of_ints->validate( {} ), 88 | 'HashRef of ints validates against empty hashrefs'; 89 | ok $HashRef_of_ints->validate( { foo => 1, baz => 2 } ), 90 | 'HashRef of ints validates against a hashref with ints for values'; 91 | ok !$HashRef_of_ints->validate( { foo => 'bar', baz => 2 } ), 92 | 'HashRef of ints validates against a hashref with non-ints for values'; 93 | }; 94 | 95 | subtest 'Maybe' => sub { 96 | my $Maybe = Object::Types::Concrete::Maybe->new( 97 | contains => Object::Types::Concrete::Str->new ); 98 | ok $Maybe->validate(undef), 'Maybe(Str) accepts undef values'; 99 | ok $Maybe->validate('Foo'), '... and non-empty strings'; 100 | ok !$Maybe->validate(''), '... but not non-empty strings'; 101 | }; 102 | 103 | subtest 'Optional' => sub { 104 | my $Optional = Object::Types::Concrete::Optional->new( 105 | contains => Object::Types::Concrete::Str->new ); 106 | ok $Optional->validate(undef), 'Optional(Str) accepts undef values'; 107 | ok $Optional->validate('Foo'), '... and non-empty strings'; 108 | ok !$Optional->validate(''), '... but not non-empty strings'; 109 | }; 110 | 111 | subtest 'Coerce' => sub { 112 | my $true_false = Object::Types::Concrete::Enum->new(elements => [qw/true false/]); 113 | my $Coerce = Object::Types::Concrete::Coerce->new( contains => $true_false, via => sub { $_[0] eq 'true' ? 1 : 0 }); 114 | my $true = 'true'; 115 | ok $Coerce->validate($true), 'We should be able to validate "true"'; 116 | is $true, 1, '... and have it coerced to the number `1`'; 117 | }; 118 | 119 | subtest 'Default values' => sub { 120 | my $h1 = Object::Types::Concrete::HashRef->new; 121 | my $h2 = Object::Types::Concrete::HashRef->new; 122 | explain "There's currently an ADJUST hack in the code for this"; 123 | isnt refaddr( $h1->elements ), refaddr( $h2->elements ), 124 | 'Default references should not be the same reference'; 125 | }; 126 | 127 | done_testing; 128 | -------------------------------------------------------------------------------- /t/object/types/moo/factory.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use Test::Most; 4 | use Storable 'dclone'; 5 | use Object::Types::Moo::Factory; 6 | $Object::Types::Moo::Factory::Test::Mode = 1; 7 | 8 | subtest 'Any' => sub { 9 | my $Any = Object::Types::Moo::Concrete::Any->new; 10 | ok $Any->validate(undef), 'Any() always passes'; 11 | }; 12 | 13 | subtest 'Int' => sub { 14 | my $Int = Object::Types::Moo::Concrete::Int->new; 15 | ok $Int->validate(37), 'Int() should validate ints'; 16 | ok !$Int->validate(32.8), '... but not non-ints'; 17 | }; 18 | 19 | subtest 'Num' => sub { 20 | my $Num = Object::Types::Moo::Concrete::Num->new; 21 | ok $Num->validate(37), 'Num() should validate ints'; 22 | ok $Num->validate(32.8), '... and floats'; 23 | ok !$Num->validate("Ovid"), '... but not non-numbers'; 24 | }; 25 | 26 | subtest 'Str' => sub { 27 | my $Str = Object::Types::Moo::Concrete::Str->new; 28 | ok $Str->validate(37), 'Str() should validate ints'; 29 | ok $Str->validate(32.8), '... and floats'; 30 | ok $Str->validate("Ovid"), '... and non-numbers'; 31 | ok !$Str->validate(' '), '... but not empty strings'; 32 | }; 33 | 34 | subtest 'Regex' => sub { 35 | my $Regex = Object::Types::Moo::Concrete::Regex->new( regex => qr/ab*c/ ); 36 | ok $Regex->validate('ac'), 'Strings matching the regex should valiadate'; 37 | ok !$Regex->validate('axc'), 38 | '... and strings not matching the regex should not valiadate'; 39 | $Regex = Object::Types::Moo::Concrete::Regex->new( regex => qr/x/ ); 40 | ok !$Regex->validate( [] ), 'Regexes can never validate references'; 41 | }; 42 | 43 | subtest 'Enum' => sub { 44 | my $Enum = 45 | Object::Types::Moo::Concrete::Enum->new( elements => [ 1, 2, 'Ovid' ] ); 46 | ok $Enum->validate('Ovid'), 'Values in our enum should validate'; 47 | ok $Enum->validate(1), '... no matter which they are'; 48 | ok !$Enum->validate(42), '... values not in the enum should not validate'; 49 | 50 | my $Enum_with_types = Object::Types::Moo::Concrete::Enum->new( 51 | elements => [ 'Ovid', Object::Types::Moo::Concrete::Int->new ] ); 52 | ok $Enum_with_types->validate('Ovid'), 'Values in our enum should validate'; 53 | ok $Enum_with_types->validate(7), '... as should values matching types'; 54 | ok !$Enum_with_types->validate(7.3), 55 | '... but not values not matching either values or types'; 56 | }; 57 | 58 | subtest 'ArrayRef' => sub { 59 | my $ArrayRef = Object::Types::Moo::Concrete::ArrayRef->new; 60 | ok $ArrayRef->validate( [] ), 61 | 'All arrayrefs match an empty array reference'; 62 | ok $ArrayRef->validate( [ 1, 3.14, {}, [], 'Ovid' ] ), 63 | '... no matter what they contain'; 64 | ok !$ArrayRef->validate( {} ), 65 | '... but they will not validate against things that are not array references'; 66 | 67 | my $ArrayRef_of_ints = Object::Types::Moo::Concrete::ArrayRef->new( 68 | contains => Object::Types::Moo::Concrete::Int->new ); 69 | ok $ArrayRef_of_ints->validate( [] ), 70 | 'arrayrefs of ints match an empty array reference'; 71 | ok $ArrayRef_of_ints->validate( [ 1, 4 ] ), '... or an arrayref of ints'; 72 | ok !$ArrayRef_of_ints->validate( [ 4, undef ] ), 73 | '... but not an array ref which contains something that is not an int'; 74 | }; 75 | 76 | subtest 'HashRef' => sub { 77 | my $HashRef = Object::Types::Moo::Concrete::HashRef->new; 78 | ok $HashRef->validate( {} ), 'HashRef validates against empty hashrefs'; 79 | ok $HashRef->validate( { foo => 1 } ), 80 | 'HashRef validates against anything in a hashref'; 81 | ok !$HashRef->validate( [] ), 82 | 'HashRef does not validate against non-hashrefs'; 83 | 84 | my $HashRef_of_ints = Object::Types::Moo::Concrete::HashRef->new( 85 | contains => Object::Types::Moo::Concrete::Int->new ); 86 | ok $HashRef_of_ints->validate( {} ), 87 | 'HashRef of ints validates against empty hashrefs'; 88 | ok $HashRef_of_ints->validate( { foo => 1, baz => 2 } ), 89 | 'HashRef of ints validates against a hashref with ints for values'; 90 | ok !$HashRef_of_ints->validate( { foo => 'bar', baz => 2 } ), 91 | 'HashRef of ints validates against a hashref with non-ints for values'; 92 | }; 93 | 94 | subtest 'Maybe' => sub { 95 | my $Maybe = Object::Types::Moo::Concrete::Maybe->new( 96 | contains => Object::Types::Moo::Concrete::Str->new ); 97 | ok $Maybe->validate(undef), 'Maybe(Str) accepts undef values'; 98 | ok $Maybe->validate('Foo'), '... and non-empty strings'; 99 | ok !$Maybe->validate(''), '... but not non-empty strings'; 100 | }; 101 | 102 | subtest 'Optional' => sub { 103 | my $Optional = Object::Types::Moo::Concrete::Optional->new( 104 | contains => Object::Types::Moo::Concrete::Str->new ); 105 | ok $Optional->validate(undef), 'Optional(Str) accepts undef values'; 106 | ok $Optional->validate('Foo'), '... and non-empty strings'; 107 | ok !$Optional->validate(''), '... but not non-empty strings'; 108 | }; 109 | 110 | subtest 'Coerce' => sub { 111 | my $true_false = 112 | Object::Types::Moo::Concrete::Enum->new( elements => [qw/true false/] ); 113 | my $Coerce = Object::Types::Moo::Concrete::Coerce->new( 114 | contains => $true_false, 115 | via => sub { $_[0] eq 'true' ? 1 : 0 } 116 | ); 117 | my $true = 'true'; 118 | ok $Coerce->validate($true), 'We should be able to validate "true"'; 119 | is $true, 1, '... and have it coerced to the number `1`'; 120 | }; 121 | 122 | done_testing; 123 | -------------------------------------------------------------------------------- /t/object/types/moose/factory.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use Test::Most; 4 | use Storable 'dclone'; 5 | use Object::Types::Moose::Factory; 6 | $Object::Types::Moose::Factory::Test::Mode = 1; 7 | 8 | subtest 'Any' => sub { 9 | my $Any = Object::Types::Moose::Concrete::Any->new; 10 | ok $Any->validate(undef), 'Any() always passes'; 11 | }; 12 | 13 | subtest 'Int' => sub { 14 | my $Int = Object::Types::Moose::Concrete::Int->new; 15 | ok $Int->validate(37), 'Int() should validate ints'; 16 | ok !$Int->validate(32.8), '... but not non-ints'; 17 | }; 18 | 19 | subtest 'Num' => sub { 20 | my $Num = Object::Types::Moose::Concrete::Num->new; 21 | ok $Num->validate(37), 'Num() should validate ints'; 22 | ok $Num->validate(32.8), '... and floats'; 23 | ok !$Num->validate("Ovid"), '... but not non-numbers'; 24 | }; 25 | 26 | subtest 'Str' => sub { 27 | my $Str = Object::Types::Moose::Concrete::Str->new; 28 | ok $Str->validate(37), 'Str() should validate ints'; 29 | ok $Str->validate(32.8), '... and floats'; 30 | ok $Str->validate("Ovid"), '... and non-numbers'; 31 | ok !$Str->validate(' '), '... but not empty strings'; 32 | }; 33 | 34 | subtest 'Regex' => sub { 35 | my $Regex = Object::Types::Moose::Concrete::Regex->new( regex => qr/ab*c/ ); 36 | ok $Regex->validate('ac'), 'Strings matching the regex should valiadate'; 37 | ok !$Regex->validate('axc'), 38 | '... and strings not matching the regex should not valiadate'; 39 | $Regex = Object::Types::Moose::Concrete::Regex->new( regex => qr/x/ ); 40 | ok !$Regex->validate( [] ), 'Regexes can never validate references'; 41 | }; 42 | 43 | subtest 'Enum' => sub { 44 | my $Enum = 45 | Object::Types::Moose::Concrete::Enum->new( elements => [ 1, 2, 'Ovid' ] ); 46 | ok $Enum->validate('Ovid'), 'Values in our enum should validate'; 47 | ok $Enum->validate(1), '... no matter which they are'; 48 | ok !$Enum->validate(42), '... values not in the enum should not validate'; 49 | 50 | my $Enum_with_types = Object::Types::Moose::Concrete::Enum->new( 51 | elements => [ 'Ovid', Object::Types::Moose::Concrete::Int->new ] ); 52 | ok $Enum_with_types->validate('Ovid'), 'Values in our enum should validate'; 53 | ok $Enum_with_types->validate(7), '... as should values matching types'; 54 | ok !$Enum_with_types->validate(7.3), 55 | '... but not values not matching either values or types'; 56 | }; 57 | 58 | subtest 'ArrayRef' => sub { 59 | my $ArrayRef = Object::Types::Moose::Concrete::ArrayRef->new; 60 | ok $ArrayRef->validate( [] ), 61 | 'All arrayrefs match an empty array reference'; 62 | ok $ArrayRef->validate( [ 1, 3.14, {}, [], 'Ovid' ] ), 63 | '... no matter what they contain'; 64 | ok !$ArrayRef->validate( {} ), 65 | '... but they will not validate against things that are not array references'; 66 | 67 | my $ArrayRef_of_ints = Object::Types::Moose::Concrete::ArrayRef->new( 68 | contains => Object::Types::Moose::Concrete::Int->new ); 69 | ok $ArrayRef_of_ints->validate( [] ), 70 | 'arrayrefs of ints match an empty array reference'; 71 | ok $ArrayRef_of_ints->validate( [ 1, 4 ] ), '... or an arrayref of ints'; 72 | ok !$ArrayRef_of_ints->validate( [ 4, undef ] ), 73 | '... but not an array ref which contains something that is not an int'; 74 | }; 75 | 76 | subtest 'HashRef' => sub { 77 | my $HashRef = Object::Types::Moose::Concrete::HashRef->new; 78 | ok $HashRef->validate( {} ), 'HashRef validates against empty hashrefs'; 79 | ok $HashRef->validate( { foo => 1 } ), 80 | 'HashRef validates against anything in a hashref'; 81 | ok !$HashRef->validate( [] ), 82 | 'HashRef does not validate against non-hashrefs'; 83 | 84 | my $HashRef_of_ints = Object::Types::Moose::Concrete::HashRef->new( 85 | contains => Object::Types::Moose::Concrete::Int->new ); 86 | ok $HashRef_of_ints->validate( {} ), 87 | 'HashRef of ints validates against empty hashrefs'; 88 | ok $HashRef_of_ints->validate( { foo => 1, baz => 2 } ), 89 | 'HashRef of ints validates against a hashref with ints for values'; 90 | ok !$HashRef_of_ints->validate( { foo => 'bar', baz => 2 } ), 91 | 'HashRef of ints validates against a hashref with non-ints for values'; 92 | }; 93 | 94 | subtest 'Maybe' => sub { 95 | my $Maybe = Object::Types::Moose::Concrete::Maybe->new( 96 | contains => Object::Types::Moose::Concrete::Str->new ); 97 | ok $Maybe->validate(undef), 'Maybe(Str) accepts undef values'; 98 | ok $Maybe->validate('Foo'), '... and non-empty strings'; 99 | ok !$Maybe->validate(''), '... but not non-empty strings'; 100 | }; 101 | 102 | subtest 'Optional' => sub { 103 | my $Optional = Object::Types::Moose::Concrete::Optional->new( 104 | contains => Object::Types::Moose::Concrete::Str->new ); 105 | ok $Optional->validate(undef), 'Optional(Str) accepts undef values'; 106 | ok $Optional->validate('Foo'), '... and non-empty strings'; 107 | ok !$Optional->validate(''), '... but not non-empty strings'; 108 | }; 109 | 110 | subtest 'Coerce' => sub { 111 | my $true_false = 112 | Object::Types::Moose::Concrete::Enum->new( elements => [qw/true false/] ); 113 | my $Coerce = Object::Types::Moose::Concrete::Coerce->new( 114 | contains => $true_false, 115 | via => sub { $_[0] eq 'true' ? 1 : 0 } 116 | ); 117 | my $true = 'true'; 118 | ok $Coerce->validate($true), 'We should be able to validate "true"'; 119 | is $true, 1, '... and have it coerced to the number `1`'; 120 | }; 121 | 122 | done_testing; 123 | -------------------------------------------------------------------------------- /t/test.conf: -------------------------------------------------------------------------------- 1 | # see Corinna::RFC::Config::Reader for syntax 2 | [@rfcs] ; the @ means 'preserve the order of these values 3 | Overview=overview.md 4 | Grammar=grammar.md 5 | Classes=classes.md 6 | Attributes=attributes.md 7 | Methods=methods.md 8 | 9 | [main] ; k/v pairs: $config->{main}{readme} = README.md 10 | template_dir=templates ; where the templates are stored 11 | rfc_dir=rfc ; where the rfcs will be saved 12 | readme=README.md ; name of the README.md file 13 | toc=toc.md ; name we'll use for our table of contents file 14 | toc_marker={{TOC}} ; the marker in the toc file for post-process insertion of table of contents 15 | github=https://github.com/Ovid/Cor ; url of this repo 16 | -------------------------------------------------------------------------------- /templates/README.md: -------------------------------------------------------------------------------- 1 | # Corinna 2 | 3 | ## RFC 4 | 5 | **This file is automatically generated. If you wish to submit a PR, do not 6 | edit this file directly. Please edit 7 | [templates/README.md]([% config.github %]/tree/master/templates/README.md) instead.** 8 | 9 | --- 10 | 11 | # Bringing Modern OOP to the Perl Core 12 | 13 | ```perl 14 | class Cache::LRU v0.1.0 { 15 | use Hash::Ordered; 16 | use Carp 'croak'; 17 | 18 | field $num_caches :common = 0; 19 | field $cache :handles(exists delete) = Hash::Ordered->new; 20 | field $max_size :param :reader = 20; 21 | field $created :reader = time; 22 | 23 | ADJUST { # called after new() 24 | $num_caches++; 25 | if ( $max_size < 1 ) { 26 | croak(...); 27 | } 28 | } 29 | DESTRUCT { $num_caches-- } 30 | 31 | method num_caches :common () { $num_caches } 32 | 33 | method set( $key, $value ) { 34 | $cache->unshift( $key, $value ); # new values in front 35 | if ( $cache->keys > $max_size ) { 36 | $cache->pop; 37 | } 38 | } 39 | 40 | method get($key) { 41 | return unless $cache->exists($key); 42 | my $value = $cache->get($key); 43 | $self->unshift( $key, $value ); # put it at the front 44 | return $value; 45 | } 46 | } 47 | ``` 48 | 49 | This repository is to track the RFC for the Corinna MVP OOP proposal. 50 | 51 | [% FOREACH template IN templates -%] 52 | [% template.index %]. [[% template.name %]]([% template.file %]) 53 | [% END %] 54 | 55 | **Important**: All of the above represent works in progress. Please do not 56 | consider this the final RFC. We're writing down the shell of the RFC and will 57 | fine-tune. Anything in the Wiki should be considered "rough drafts." 58 | 59 | ## Section Numbers 60 | 61 | As you navigate the various RFC pages, you'll note that it's broken down into 62 | fine-grained section numbers such as 3.2.2. Prior to the RFC being filed, 63 | these are fluid and will likely change without warning. After the RFC is 64 | filed, we may make minor changes to the sections, but the section numbers 65 | themselves should be frozen to make it easy to refer to a given section. 66 | 67 | ## Not a Tutorial 68 | 69 | This is not a tutorial on OO programming. That could easily fill a book. It's 70 | assumed you're already very familiar with Perl's built-in OO. It's very useful 71 | if you're also familiar with Moo/se. 72 | 73 | However, [here's a basic tutorial for Corinna if you'd like to skip the 74 | detail](https://github.com/Ovid/Cor/blob/master/pod/perlclasstut.pod). 75 | 76 | ## Principle of Parsimony 77 | 78 | Many things in the proposal are _deliberately_ restrictive, such as Corinna 79 | only allowing single inheritance. This is to allow Corinna to be cautious in 80 | not promising too much. If we later find this too restrictive, we can allow 81 | multiple inheritance. However, if we start with multiple inheritance and 82 | discover we don't need or want multiple inheritance, we would break existing 83 | code by taking it away. 84 | 85 | Any proposals to change the RFC must consider the principle of parsimony. 86 | 87 | **Note**: it's been brought to my attention that the [Principle of 88 | Parsimony](https://www.oxfordreference.com/display/10.1093/oi/authority.20110803100346221) 89 | is a phrase used in biology and has a different meaning. Oops. 90 | 91 | # KIM 92 | 93 | We are trying to adhere to the 94 | [KIM](https://ovid.github.io/articles/language-design-consistency.html) 95 | principle. In short, features should try to follow this syntax: 96 | 97 | ``` 98 | KEYWORD IDENTIFIER MODIFIER? SETUP? 99 | ``` 100 | 101 | So instead of this: 102 | 103 | ```perl 104 | class Foo isa Bar does SomeRole v1.2.3 { 105 | overrides method some_method() { 106 | ... 107 | } 108 | } 109 | ``` 110 | 111 | We're doing this: 112 | 113 | ```perl 114 | class Foo :isa(Bar) :does(SomeRole) :version(v1.2.3) { 115 | method some_method :overrides () { 116 | ... 117 | } 118 | } 119 | ``` 120 | 121 | By trying to have all syntax arranged like this we reduce the core of Corinna 122 | down to four new keywords: 123 | 124 | * `class` 125 | * `role` 126 | * `field` 127 | * `method` 128 | 129 | Everything which tweaks those behaviors should be a modifier. 130 | 131 | This part is still a work in progress and may evolve. 132 | 133 | ## This Repository 134 | 135 | This repository is not for code. Instead, it’s to have a central place to 136 | discuss the Corinna proposal to bring modern OO to the core of the Perl 137 | language. 138 | 139 | You may be looking for the [Wiki](https://github.com/Ovid/Cor/wiki) as that 140 | has much historical discussion of this project. However, the documents in the 141 | `rfc/` folder will be considered the canonical ones. 142 | 143 | [You can file issues](https://github.com/Ovid/Cor/issues) to address your 144 | particular concerns and get feedback. 145 | 146 | ## MVP 147 | 148 | This work is to drive us to v0.1.0—not v1.0.0—the “minimum viable product.” The 149 | intent is to get the subset of features we really need into the language, make 150 | sure it’s stable, and then iterate on top of that. I don’t want us building on 151 | top of this foundation only to discover the foundation is not stable. 152 | 153 | I’ve been getting some pushback from people being quite insistent that if I 154 | don‘t support one or more of the features that _they_ like to use, that it will 155 | be so terribly hobbled that it will be of limited use to them. 156 | 157 | Please keep in mind that if this MVP does get into core, I will have lost 158 | control over it (yay!) and I’ll have no particularly greater voice than others. 159 | That means: 160 | 161 | **If this gets into the core and you want a feature, lobby for it.** 162 | 163 | I’m not a gatekeeper. I don’t want to be a gatekeeper. I want to get modern OO 164 | into the Perl core and the more things added to the MVP to scratch people’s 165 | personal itches, the more bugs there will be and the less likely it is to be 166 | stable. Let’s focus on the MVP and getting this in core. 167 | 168 | After that, it will be up to the community to decide where it wants to go. I 169 | will have _zero_ authority to stop things I disagree with if P5P decides to 170 | implement them. This will effectively be a democracy. A level playing field. 171 | If you won’t support Corinna because your personal favorite feature isn’t in 172 | the MVP, I don’t know what to say to that. You, like everyone else, will be 173 | able to lobby for that feature. 174 | 175 | # DEDICATION 176 | 177 | This project is dedicated to the memory of both Jeff Goff and David Adler, 178 | two Perl developers who also loved Raku and worked to create a better 179 | language. They were both wonderful people and will be missed. 180 | -------------------------------------------------------------------------------- /templates/rfc/class-construction.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | For object construction, we provide a list of needed steps and then show 4 | pseudocode to make the construction process explicit. 5 | 6 | **Note**: because Corinna is single inheritance, MRO order is simply child to 7 | parent. 8 | 9 | Anything which can be removed from this will make object construction 10 | faster. Anything which can be pushed to compile-time will make object 11 | construction faster. 12 | 13 | This is almost certainly incorrect, but it's a start. 14 | 15 | Also, roles get an ADJUST phaser now 16 | 17 | 1. Check that the even-sized list of args to new() are not duplicated 18 | (stops the new( this => 1, this => 2 ) error) 19 | 2. And that the keys are not references 20 | 3. Walk through classes in reverse MRO order. Croak() if any field 21 | name is reused 22 | 4. After previous step, if we have any extra keys passed to new() which cannot be 23 | allocated to a field, throw an exception 24 | 5. For the internal NEW phaser, assign all values to their correct fields in 25 | reverse mro order 26 | 6. Call all ADJUST phasers in reverse MRO order (no need to validate here because 27 | everything should be checked at this point) 28 | 29 | # Steps 30 | 31 | ## Step 1 Verify even-sized list of args 32 | 33 | Check that the even-sized list of args to `new()` are not duplicated (stops 34 | the `new( this => 1, this => 2` ) error). 35 | 36 | ```perl 37 | my @args = ...; # get list passed to new() 38 | if ( @args % 2 ) { 39 | croak("even-sized list required"); 40 | } 41 | ``` 42 | 43 | ## Step 2 Constructor keys may not be references 44 | 45 | Keys are not references (duh). 46 | 47 | ```perl 48 | my %arg_for; 49 | while (@args) { 50 | my ( $key, $value ) = (shift @args, shift @args); 51 | ref $key and croak qq{'$key' must not be a ref}; 52 | exists $arg_for{$key} and croak qq{duplicate key '$key' detected}; 53 | $arg_for{$key} = $value; 54 | } 55 | ``` 56 | 57 | ## Step 3 Find constructor args 58 | 59 | Walk through classes from parent to child. `croak()` if any 60 | constructor argument is reused. Different roles and classes can 61 | consume the same role, their constructor arguments only count once. 62 | 63 | ```perl 64 | my %orig_args = %arg_for; # shallow copy 65 | my %constructor_args; 66 | 67 | 68 | my @duplicate_constructor_args; 69 | my %seen_roles; 70 | foreach my $class (@reverse_mro) { 71 | my @roles = grep { ! $seen_roles{$_} } roles_from_class($class); 72 | @seen_roles{ @roles } = (1) x @roles; 73 | foreach my $thing ( $class, @roles ) { 74 | foreach my $name ( get_fields_with_param_attributes($thing) ) { 75 | if ( my $other_class = $constructor_args{$name} ) { 76 | # XXX Warning! This may be a bad thing 77 | # If you don't happen to notice that some parent class has done 78 | # `field $cutoff :param = 42;` 79 | # then you might accidentally write: 80 | # `field $cutoff :param = DateTime->now->add(days => 7);` 81 | # instead, we probably need some way of signaling this to the 82 | # programmer. A compile-time error would be good. 83 | push @duplicate_constructor_args 84 | => "Arg '$name' in '$thing' already used in '$other_class'"; 85 | } 86 | $constructor_args{$name} = $class; 87 | } 88 | } 89 | } 90 | if (my $error = join ' ' => @duplicate_constructor_args) { 91 | croak($error); 92 | } 93 | ``` 94 | 95 | **Note**: "reused" constructor arguments refers to the public name for the 96 | field. You can reuse `field $message;` in subclasses because it's not public. 97 | However, you cannot reuse `field $message :param;` in a subclass because the 98 | field name default to `message`. Instead, you would need to rename it: `field 99 | $message :param :name(client_message);`. 100 | 101 | We have this restriction to enforce encapsulation of logic in a class. If the 102 | parent class has `field $error :param;` and expects that to contain an error 103 | _object_ and a child class has a `field $error :param;` and expects that to 104 | contain an error _string_, you're in trouble. Until such time that we can 105 | squeeze types into Corinna (and to Perl in general), this restriction makes 106 | the code safer, albeit at the cost of some annoyance. 107 | 108 | ## Step 4 Err out on unknown keys 109 | 110 | 111 | After the previous step, if we have any extra keys passed to `new()` which cannot 112 | be allocated to a field, throw an exception. This works because by the time we 113 | get to the final class, all keys should be accounted for. Stops the issue of 114 | `Class->new(feild => 4)` when the field is `field $field :param = 3;` 115 | 116 | ```perl 117 | my @bad_keys; 118 | foreach my $key ( keys %arg_for ) { 119 | exists $constructor_args{$key} 120 | or push @bad_keys, $key; 121 | } 122 | if (@bad_keys) { 123 | croak(...); 124 | } 125 | ``` 126 | 127 | ## Step 5 `new()` 128 | 129 | For the internal NEW phaser, assign all values to their correct fields from 130 | parent to child. 131 | 132 | ```perl 133 | my @field_values; 134 | foreach my $this_class (@reverse_mro) { 135 | my @roles = roles_from_class($class); 136 | foreach my $thing ( $class, @roles ) { 137 | foreach my $field_name ( get_fields_in_initialization_order($thing) ) { 138 | push @field_values => $arg_for{$field_name}; 139 | } 140 | } 141 | } 142 | 143 | # PSEUDOCODE! In no way is this meant to suggest that this will be the 144 | # underlying representation of Corinna objects. 145 | my $self = bless \@field_values => $class; 146 | ``` 147 | 148 | ## Step 6 `ADJUST` 149 | 150 | Call all `ADJUST` phasers from parent to children (no need to validate here because 151 | everything should be checked at this point). 152 | 153 | ```perl 154 | foreach my $class (@reverse_mro) { 155 | my @roles = roles_from_class($class); 156 | foreach my $thing ( $class, @roles ) { 157 | 158 | # the prefix is just pseudo-code to show the idea 159 | $thing::ADJUST(); # phaser, not a method 160 | } 161 | } 162 | ``` 163 | 164 | # MOP Pseudocode 165 | 166 | MOP stuff 167 | 168 | ```perl 169 | class MOP { 170 | method get_fields_with_param_attributes($class_or_role) { 171 | return 172 | grep { $self->has_attribute( ':param', $_ ) } 173 | get_all_fields($class_or_role); 174 | } 175 | 176 | method get_fields_in_initialization_order($class_or_role) { 177 | # get_all_fields($class_or_role) should return them in declaration order 178 | my @fields = get_all_fields($class_or_role); 179 | my @ordered; 180 | my $constructor_args_processed = 0; 181 | while (@fields) { 182 | my $field = shift @fields; 183 | if ( $self->has_attribute( ':param', $field ) ) { 184 | push @ordered => $fields; 185 | my @remaining; 186 | foreach my $field (@fields) { 187 | if ( $self->has_attribute( ':param', $field ) ) { 188 | push @ordered => $field; 189 | } 190 | else { 191 | push @remaining => $field; 192 | } 193 | } 194 | @fields = @remaining; 195 | } 196 | else { 197 | push @ordered => $field; 198 | } 199 | } 200 | } 201 | } 202 | ``` 203 | -------------------------------------------------------------------------------- /templates/rfc/classes.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | Corinna classes are single-inheritance, data is declared with `field` (instance 4 | data) or `my` (class data), and methods use the `method` keyword instead of 5 | `sub`. All methods require signatures, even methods which take no arguments. 6 | 7 | Methods are not subroutines. Corinna cannot call methods without an invocant 8 | and cannot call subroutines with an invocant. 9 | 10 | For the MVP, Corinna classes cannot inherit from non-Corinna classes. It 11 | remains unclear in practice how problematic this will be. Using 12 | `:handles` to explicitly delegate to methods you need will help. Remember that. 13 | 14 | # Discussion 15 | 16 | As per [the grammar](grammar.md), the smallest possible class is `class A 17 | {}` and you could instantiate it with `my $object = A->new`. Not very useful, but 18 | it's there. Note that you do not need to specify a constructor. 19 | 20 | Here's a somewhat more interesting class. `field` declares a field (data) and the 21 | `/:\w+/` attributes provide additional behavior. See 22 | [Fields](attributes.md) for more information. 23 | 24 | ```perl 25 | class Person { 26 | field $name :param; # must be passed to constructor (:param) 27 | field $title :param = undef; # optionally passed to constructor (:param, but with default) 28 | 29 | method name () { # instance method 30 | return defined $title ? "$title $name" : $name; 31 | } 32 | } 33 | ``` 34 | 35 | And here's how you would use this class. 36 | 37 | ```perl 38 | my $villain = Person->new( title => 'Dr.', name => 'Zachary Smith' ); 39 | my $boy = Person->new( name => 'Will Robinson' ); 40 | 41 | say $villain->name; # Dr. Zachary Smith 42 | say $boy->name; # Will Robinson 43 | ``` 44 | 45 | In the above, the `$name` and `$title` fields are completely 46 | encapsulated. If you want to expose them to the outside world, you would use 47 | the `:reader` and `:writer` attributes. 48 | 49 | We have a `Person` class with a required name and an optional 50 | title. The constructor is `new` and accepts an even-sized list of key/value 51 | pairs. Per the [class construction specification](class-construction.md), 52 | providing duplicate keys to the constructor is not allowed. Nor is a hash reference. If 53 | you wish to provide an alternate set of arguments to the constructor, write 54 | an alternate constructor (the behavior replaces Moo/se `BUILDARGS`). 55 | 56 | ```perl 57 | method from_name :common ($name) { # ':common' means it's a class method 58 | $class->new( name => $name ); # all methods have an immutable $class variable injected 59 | } 60 | my $boy = Person->from_name('Will Robinson'); 61 | say $boy->name; # Will Robinson 62 | ``` 63 | 64 | Let's make the class more interesting. 65 | 66 | ```perl 67 | class Person { 68 | use DateTime; 69 | field $name :param; # must be passed to constructor (:param) 70 | field $title :param = undef; # optionally passed to constructor (:param, but with default) 71 | field $created = DateTime->now; # cannot be passed to constructor (no :param) 72 | field $num_people :common = 0; # class data, defaults to 0 (:common, with hand-rolled reader method) 73 | method num_people :common () { $num_people } 74 | 75 | ADJUST { $num_people++ } # called after new(), but before returning to consumer (BUILD) 76 | DESTRUCT { $num_people-- } # destructor 77 | 78 | method name () { 79 | return defined $title ? "$title $name" : $name; 80 | } 81 | } 82 | ``` 83 | 84 | And using it. 85 | 86 | ```perl 87 | say Person->num_people; # 0 88 | my $villain = Person->new( title => 'Dr.', name => 'Zachary Smith' ); 89 | say $villain->name; # Dr. Zachary Smith 90 | say Person->num_people; # 1 91 | say $villain->num_people; # 1 class methods can be called on instances 92 | # but instance methods can't be called from classnames or methods 93 | my $boy = Person->new( name => 'Will Robinson' ); 94 | say $boy->name; # Will Robinson 95 | say Person->num_people; # 2 96 | undef $villain; 97 | say Person->num_people; # 1 98 | undef $boy; 99 | say Person->num_people; # 0 100 | ``` 101 | 102 | `ADJUST` and `DESTRUCT` are [phasers](phasers.md). 103 | 104 | ## Versions 105 | 106 | Just add the version number after the class name. This should accept any 107 | standard version number. 108 | 109 | ```perl 110 | class My::Class 3.14 { 111 | ... 112 | } 113 | ``` 114 | 115 | ## Inheritance 116 | 117 | Corinna supports single inheritance via the `:isa` attribute. You may 118 | optionally add a version number to the name of the class you're inheriting 119 | from to show the minimum allowed version of the class. 120 | 121 | ```perl 122 | class Customer v2.1.0 :isa(Person 3.14) { 123 | field $customer_id :param; 124 | 125 | method name :overrides () { 126 | my $name = $self->next::method(); 127 | $name .= " (#$customer_id)"; 128 | return $name; 129 | } 130 | } 131 | ``` 132 | 133 | Usage: 134 | 135 | ```perl 136 | say Customer->num_people; # 0 (inherited) 137 | my $customer = Customer->new( name => 'Ford Prefect', customer_id => 42 ); 138 | say Customer->num_people; # 1 139 | say $customer->name; # Ford Prefect (#42) 140 | ``` 141 | 142 | Let's look at the method a bit more closely. 143 | 144 | ``` 145 | 01: method name :overrides () { 146 | 02: my $name = $self->next::method(); 147 | 03: $name .= " (#$customer_id)"; 148 | 04: return $name; 149 | 05: } 150 | ``` 151 | 152 | On line 1, the `:overrides` tells Corinna we're overriding a parent method. This should: 153 | 154 | * Suppress overridden warnings when overriding a parent method 155 | * Generate a compile-time error if there is no parent method 156 | 157 | The compile-time error is to prevent cases like the method above, where 158 | `$self->next::method()` would ordinarily generate a runtime exception because 159 | the method does not exist. 160 | 161 | On line 2, we see two interesting things. First is the immutable `$self` 162 | variable automatically injected into the body of the method. There is also a 163 | `$class` variable availabe, but it's not shown in this example. 164 | 165 | Next is the `next::method` part. Usually we see that as part of the [C3 Method Resolution Order](https://en.wikipedia.org/wiki/C3_linearization). It's here because of the 166 | [SUPER-bug in Perl](http://modernperlbooks.com/mt/2009/09/when-super-isnt.html). 167 | 168 | ## Roles 169 | 170 | Corinna allows roles to be consumed via `:does`. Here's a simple role. 171 | 172 | ```perl 173 | role RoleStringify { 174 | method to_string() { return $self->name } 175 | } 176 | ``` 177 | 178 | And a class consuming it. 179 | 180 | ```perl 181 | class Customer :isa(Person) :does(RoleStringify) { 182 | field $customer_id :param; 183 | 184 | method name :overrides () { 185 | my $name = $self->next::method(); 186 | $name .= " (#$customer_id)"; 187 | return $name; 188 | } 189 | } 190 | ``` 191 | 192 | Usage. 193 | 194 | ```perl 195 | my $customer = Customer->new( name => 'Ford Prefect', customer_id => 42 ); 196 | say $customer->to_string(); # Ford Prefect (#42) 197 | ``` 198 | 199 | Multiple roles may be consumed. 200 | 201 | ```perl 202 | class Customer 1.2 :isa(Person) :does(RoleStringify, RoleJSON) { 203 | ... 204 | } 205 | ``` 206 | 207 | Role semantics will be covered more thoroughly in [roles](roles.md). 208 | 209 | ## Abstract Classes 210 | 211 | Abstract classes are classes which are designed to be subclassed and not 212 | instantiated directly. They are declared by using an `:abstract` modifier in 213 | the definition: 214 | 215 | ```perl 216 | class Employee :abstract { 217 | ... 218 | } 219 | 220 | class Employee::Manager :isa(Employee) { 221 | ... 222 | } 223 | 224 | class Employee::Hourly :isa(Employee) { 225 | ... 226 | } 227 | ``` 228 | 229 | Attempting to instantiate an abstract class is a fatal error. 230 | 231 | ## Subroutines versus Methods 232 | 233 | In Corinna, methods and subs are not the same thing. Here's a silly example. 234 | 235 | ```perl 236 | class Iterator::Number { 237 | use List::Util 'sum'; 238 | field $i = 0; 239 | field @numbers; # only the `:common` attribute is allowed on non-scalars 240 | 241 | method push ($num) { push @numbers => $num } 242 | method pop { pop @numbers } 243 | method shift () { return shift @numbers } 244 | method unshift ($num) { unshift @numbers => $num } 245 | method sum () { return sum(@numbers) } # !!! 246 | method reset () { $i = 0 } 247 | method exhausted () { return $i > $#numbers } 248 | method next () { 249 | if ( not $self->exhausted ) { 250 | return $numbers[$i]; 251 | $i++; 252 | } 253 | } 254 | } 255 | ``` 256 | 257 | Usage. 258 | 259 | ```perl 260 | my $iter = Iterator::Number->new; 261 | $iter->push($_) for 1,2,3; 262 | say $iter->sum; # 6 263 | ``` 264 | 265 | In Corinna, methods and subroutines are not the same thing. If we did _not_ 266 | have a `sum` method in the above code, attempting to call `$iter->sum` (or 267 | `$self->sum` internally) would generate a 'method not found' error. 268 | -------------------------------------------------------------------------------- /templates/rfc/faq.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | You have questions? We have answers. Note that this is a work-in-progress. 4 | More may be added later. 5 | 6 | ## Why can't I inherit from blessed objects? 7 | 8 | Post-MVP this decision might be revisited, but for the MVP, we needed to 9 | constrain the problem space. Just some of the issues being faced: 10 | 11 | * `class` will understand the difference between methods and subroutines and 12 | when the MVP is more fleshed out, will not allow you to call subroutines as 13 | methods. What do you do when the parent class is `blessed`ed and all you 14 | have are subs? 15 | * `class` is single-inheritance and `bless` is multiple inheritance. Having to 16 | switch the MRO back and forth as you walk the inheritance hierarchy is 17 | begging for bugs. 18 | * Ultimately, `class` might need a different base class, such as 19 | `UNIVERSAL::Class`. If we inherit from a `bless`ed object, which base class 20 | wins? 21 | * Many awesome tools have been written for 22 | [Moose](https://metacpan.org/pod/Moose) and they assume 23 | [Class::MOP](https://metacpan.org/pod/Class::MOP) works. We cannot make that 24 | assumption, so we don't. 25 | 26 | Post-MVP, we might revision this decision, but for the MVP, there's a huge 27 | amount of work to do and trying to make sure we didn't screw up anywhere is 28 | much harder if we try to slurp in the entire `bless`ed ecosystem. Bear with 29 | us. 30 | 31 | Otherwise, you can try the [Object::Pad](https://metacpan.org/pod/Object::Pad) 32 | module. That's been the test bed for Corinna and it _does_ allow inheriting 33 | from legacy objects. Caveat Emptor. 34 | 35 | ## But I _need_ to inherit from a blessed object! 36 | 37 | No, you can't. Sorry. You can try `Object::Pad`, not use `class`, or 38 | investigate composition over inheritance: 39 | 40 | ```perl 41 | class My::Class { 42 | use Some::Bless::Class; 43 | 44 | field $arg_for_blessed :param(arg); 45 | field $delegate = Some::Bless::Class->new($arg_for_blessed); 46 | 47 | method to_delegate (@args) { 48 | return $delegate->some_method(@args); 49 | } 50 | } 51 | ``` 52 | 53 | None of those answers are satisfactory, but this is an MVP. 54 | 55 | ## Why can't I use subroutines for methods? 56 | 57 | If you write this: 58 | 59 | ```perl 60 | class My::Class { 61 | field $name; 62 | sub get_name ($self) { $name } 63 | } 64 | ``` 65 | 66 | That's a syntax error because we can't access the instance variable `$name` 67 | from a subroutine. They don't know what instance variables are. Or consider 68 | these; 69 | 70 | ```perl 71 | 72 | class Example1 { 73 | sub sum ($self) { ... } 74 | } 75 | 76 | class Example2 { 77 | use List::Util 'sum'; 78 | } 79 | ``` 80 | 81 | In both cases, we have a `sum` subroutine, but for the first, it's clearly 82 | intended to be a method and not a helper function. From the outside, we have 83 | no way of knowing that. `class` makes a clear distinction between methods and 84 | subroutines and when the MVP is complete, `$class->can('sum')` should return 85 | false if `sum` is a subroutine and not a method. 86 | 87 | ## Will `class` break existing code? 88 | 89 | No. Well, if you tried to slap it into an existing procedural code and you had subroutines 90 | with conflicting names to the keywords, _maybe_. Or you could use a lexical 91 | block and it should be perfectly safe: 92 | 93 | ```perl 94 | # lots of code here 95 | { 96 | use feature 'class'; 97 | class Foo ... 98 | } 99 | # more code here, but it doesn't see `class` behavior 100 | ``` 101 | 102 | Otherwise, `class` objects can call methods on `bless` objects and vice-versa 103 | without problem. 104 | 105 | ## How can I refactor existing objects with `class`? 106 | 107 | Er, you probably shouldn't. Rewriting is okay, but refactoring is a different 108 | story. 109 | 110 | If you wanted to experiment, keep this in mind: a `class` cannot inherit from 111 | `bless`ed objects, but as of this writing, a `bless`ed object _can_ inherit 112 | from a `class` object. So go to the root of your object hierarchy and look at 113 | converting that to a `class` and see what happens. 114 | 115 | For any decent-sized system, you're going to have too many edge cases for this 116 | to be easy (or sane). For example, `class` constructors require an even-sized 117 | list of key/value pairs. Many other constructors don't. You may need to 118 | rethink your constructor strategy. 119 | 120 | At the end of the day, trying to gradually mix-and-match an OOP hierarchy from 121 | `bless` to `class` is like trying to use motorcycle parts to fix a car. Maybe 122 | you'll get lucky and it works, but probably not for larger codebases (at least, 123 | not without a lot of heavy lifting). 124 | 125 | The above is for refactoring. However, you could _rewrite_ existing objects via 126 | the new `class` syntax. If your tests are sane, that may not be too hard. 127 | However, if people are using your code, they may not be in a position to 128 | upgrade to v5.38.0. You could investigate 129 | [Feature::Compat::Class](https://metacpan.org/pod/Feature::Compat::Class), but 130 | read the docs carefully. There are potential compatibility issues. 131 | 132 | ## Are there any interesting projects being written with `class`? 133 | 134 | Yes! Check out Chris "peregrin" Prather's [Roguelike 135 | tutorial](https://chris.prather.org/menu/roguelike). It's pretty amazing and 136 | shows that `class` is more powerful than your author suspected (I thought we'd 137 | need more features to get this far. I was wrong). 138 | 139 | Stevan "Damn it" Little is writing [Stella](https://github.com/stevan/Stella), 140 | an actor model written with `class`. So far, he's been very pleased with how 141 | easy `class` is to work with. 142 | 143 | ## Are a class and a package the same thing? 144 | 145 | Not quite. A `class` keyword declares a class _and_ a namespace. A `package` 146 | keyword just declares the namespace. They both have the same scoping rules. 147 | 148 | What this means, however, is that you can use `class` similarly to how you 149 | would use a `package`. 150 | 151 | For example, if you have a file named `lib/My/Awesome/Class.pm`, you won't need 152 | a `package` statement inside if you use `class` instead: 153 | 154 | ```perl 155 | use experimental 'class'; 156 | 157 | class My::Awesome::Class; 158 | # more code here ... 159 | ``` 160 | 161 | ## You keep writing classes with a postfix block. Is that required? 162 | 163 | No, it's not. Like the `package` keyword, the postfix block is optional. I 164 | prefer it for the extremely clear scoping. You don't need to use it if you 165 | don't want to. 166 | 167 | ## Is there a tutorial? 168 | 169 | There's a tutorial at 170 | [perlclasstut.pod](https://github.com/Ovid/Cor/blob/master/pod/perlclasstut.pod). 171 | 172 | Note that this tutorial is for the full MVP. If you're reading this before the 173 | full MVP is released, some of the features in that tutorial won't yet be 174 | working. I don't know what version of Perl you have installed, so you'll need 175 | to consult your documentation. 176 | 177 | If you prefer, [here's a gist of the tutorial, formatted via 178 | markdown](https://gist.github.com/Ovid/4cc649c1eb3142b6a856d94c54b1d4ed). It's 179 | not guaranteed to be kept up-to-date. 180 | 181 | ## How do I know which `class` features my Perl supports? 182 | 183 | The experimental `class` feature was first added in Perl v5.38.0. You'll want 184 | to check `perldoc perlclass` for your version of Perl. You can also [read it 185 | online](https://perldoc.perl.org/perlclass), but be sure to select the correct 186 | version of Perl from the menu at the top. 187 | -------------------------------------------------------------------------------- /templates/rfc/grammar.md: -------------------------------------------------------------------------------- 1 | To make this more manageable and not define a grammar for all of Perl, we will break the grammar out into separate components for simplicity. 2 | 3 | Note that due to the adoption of the [KIM 4 | syntax](https://ovid.github.io/articles/language-design-consistency.html) 5 | (keyword, identifier, modifier), we only introduce four new keywords to the 6 | Perl language: 7 | 8 | * `class` 9 | * `role` 10 | * `field` 11 | * `method` 12 | 13 | # Class and Role Grammar 14 | 15 | The primary grammar looks like: 16 | 17 | ``` 18 | Corinna ::= CLASS | ROLE 19 | CLASS ::= 'class' NAMESPACE DECLARATION? BLOCK 20 | ROLE ::= 'role' NAMESPACE ROLES? BLOCK 21 | NAMESPACE ::= IDENTIFIER { '::' IDENTIFIER } 22 | DECLARATION ::= ':abstract'? PARENT? ROLES? VERSION? 23 | PARENT ::= ':isa(' NAMESPACE ')' 24 | ROLES ::= ':does(' NAMESPACE { ',' NAMESPACE } ','? ')' 25 | IDENTIFIER ::= [_:alpha:] {[_:alnum:]} 26 | VERSION ::= ':version(' VERSION_NUMBER ') 27 | VERSION_NUMBER ::= # all allowed Perl version numbers 28 | BLOCK ::= # Perl +/- Extras 29 | ``` 30 | 31 | We recommend [semantic versioning](https://semver.org/), but we allow all 32 | existing Perl version formats to facilitate upgrading existing modules. 33 | 34 | # Method Grammar 35 | 36 | The method grammar (skipping some bits to avoid defining a grammar for Perl): 37 | 38 | ``` 39 | METHOD ::= 'method' ACCESS_LEVELS SIGNATURE '{' (perl code) '}' 40 | SIGNATURE ::= IDENTIFIER '(' current sub argument structure + extra work from Dave Mitchell ')' 41 | ACCESS_LEVELS ::= ACCESS_LEVEL { ACCESS_LEVEL } 42 | ACCESS_LEVEL ::= ':' ( 'private' | 'overrides' | 'common' ) 43 | SIGNATURE ::= # currently allowed Perl signatures 44 | ``` 45 | 46 | # Field Grammar 47 | 48 | "Fields" in Corinna parlance are the variables where class and instance data are stored. 49 | 50 | For simplicity: `SCALAR`, `ARRAY`, and `HASH` refer to their corresponding variable names. `PERL_EXPRESSION` means what it says. `IDENTIFIER` is a valid Perl identifier. 51 | 52 | ``` 53 | FIELD ::= 'field' ( 54 | SCALAR ATTRIBUTES? DEFAULT? 55 | | ( ARRAY | HASH ) ':common'? DEFAULT? # only the :common attribute is 56 | # currently supported for array/hash fields 57 | ) 58 | DEFAULT ::= '{' PERL_EXPRESSION '}' 59 | ATTRIBUTES ::= { ATTRIBUTE } 60 | ATTRIBUTE ::= ':' ( 61 | 'param' NAME? # allowed in constructor 62 | | 'name' NAME? # alternate name (defaults to field name minus the sigil) 63 | | 'reader' NAME? # $field method to read the field 64 | | 'writer' NAME? # set_$field method to write the field 65 | | 'predicate' NAME? # has_$field method to test if field is defined 66 | | 'common' # identifies field as class method 67 | | HANDLES 68 | ) 69 | HANDLES ::= 'handles' '(' 70 | IDENTIFIER { ',' IDENTIFIER } # list of methods this field handles 71 | | PAIR { ',' PAIR } # map of methods (to, from) this field handles 72 | | '*' # this field handles all unknown methods, but inheritance takes precedence 73 | ')' 74 | PAIR ::= IDENTIFIER ':' IDENTIFIER 75 | NAME ::= '(' IDENTIFIER ')' 76 | ``` 77 | -------------------------------------------------------------------------------- /templates/rfc/major-changes.md: -------------------------------------------------------------------------------- 1 | Due to the fast-moving nature of this project, we won't note every little 2 | change to the RFC. You can always clone the repo and read the commits. 3 | Instead, we'll cover major changes here. 4 | 5 | # Change Log 6 | 7 | # August 17, 2022 8 | 9 | - `field` attributes are now formally known as attributes. Previously this was 10 | ambiguous. The grammar and RFCs have been updated to reflect this. 11 | 12 | # December 8, 2021 13 | 14 | - Injected `$class` and `$self` variables are now documented as being 15 | immutable. That prevents this bug: 16 | 17 | ```perl 18 | method foo () { 19 | $self = 42; 20 | } 21 | ``` 22 | 23 | - Class data and methods are agreed to be declared with `:common` 24 | 25 | 26 | ```perl 27 | field $foo :common; # class data 28 | method bar :common () { ... } # class method 29 | ``` 30 | 31 | - Class data (declared with `:common` can accept defaults (see the next 32 | change) and only allows the `:reader` attribute with it. 33 | 34 | - Field initialization no longer allows `=`. You must wrap the default in 35 | curly braces (an anonymous sub). This is because of this problem: 36 | 37 | ```perl 38 | field $colors = [qw/blue white red/]; 39 | ``` 40 | 41 | Every `$color` would get a reference to the same anonymous array. Instead, write 42 | it like this: 43 | 44 | ```perl 45 | field $colors { [qw/blue white red/] }; 46 | ``` 47 | 48 | With that, you can change the colors of a particular instance without changing 49 | the others. Yes, you could do `$foo = [qw/blue white black/]'` in a method, 50 | but if someone did `$foo->[-1] = 'black';`, they might wonder why all other 51 | instances had their value changed. 52 | 53 | We realize this change might seem strange, but we're hopeful people will get 54 | used to it. 55 | 56 | As of this writing, we do not plan to inject `$class` or `$self` into that 57 | block. That avoids this bug: 58 | 59 | ```perl 60 | field $color { $self->get_colors }; 61 | ``` 62 | 63 | Because initialization happens when the instance construction might not be 64 | complete, some fields may not be properly defined, leading to obscure bugs. 65 | 66 | # December 6, 2021 67 | 68 | - After considerable discussion, `slot` has been renamed to `field`. A few 69 | people objected to "slot" because it's an unusual term (borrowed from Lisp). 70 | Some non-native English speakers pointed out that "slot" is also harder for 71 | them to understand, while "field" is very clear. There was discussion about 72 | confusion with the little-used "fields" pragma, but it was generally agreed 73 | this would be unlikely. 74 | 75 | # December 4, 2021 76 | 77 | - We have removed the special `:handles(*)` syntax. It was proving too 78 | problematic and, in fact, would not have allowed us to simulate inheritance 79 | because once you enter the delegate, you can't override its methods because 80 | it's not in your inheritance hierarchy. 81 | 82 | # November 23, 2021 83 | 84 | - Clarify that the `:handles(*)` delegation will not auto-delegate to methods 85 | beginning with underscores to avoid those becoming part of the public 86 | interface. Of course, internally you can still call those methods directly 87 | on the slot variable calling the object. 88 | 89 | # November 15, 2021 90 | 91 | - After many suggestions from Paul Evans and later by Damian Conway, Corinna 92 | has been switched over to 93 | [KIM](https://ovid.github.io/articles/language-design-consistency.html) 94 | (Keyword, Identifier, Modifier) syntax. See also [Damian Conway's post on 95 | the 96 | topic](http://blogs.perl.org/users/damian_conway/2021/11/a-dream-resyntaxed.html). 97 | 98 | # November 2, 2021 99 | 100 | - Method modifiers RFC section added. 101 | - Classes documentation now shows we use any legal version numbers, not just 102 | semver triples. 103 | 104 | ## September 22, 2021 105 | 106 | - Abtract methods in classes and required methods in roles are no longer 107 | allowed to declare their argument lists. This gives us room to reconsider 108 | this behavior post-MVP. 109 | 110 | ## September 21, 2021 111 | 112 | - `:name` attribute for slots removed from MVP. Might be returned later. 113 | - Version numbers no longer limited to semver. All current Perl version 114 | formats intended to be supported. 115 | - Classes which both inherit and consume roles must now declare the parent 116 | before the roles (previously, the order was not relevant). 117 | - Method access levels such as `common`, `private`, and `overrides` are now 118 | attributes that come between the method name and the argument list: 119 | 120 | ```perl 121 | method foo :overrides ($bar, $baz) { ... } 122 | ``` 123 | 124 | ## August 26, 2021 125 | 126 | - Class slots now declared with `my`. They do not take attributes. 127 | 128 | ## August 19, 2021 129 | 130 | - Slot declaration keyword renamed from `has` to `slot` 131 | 132 | ## August 14, 2021 133 | 134 | - First draft of RFC released as markdown in github repo so that pull requests 135 | can be received. Wiki is noted to primarily be of historical interest. 136 | -------------------------------------------------------------------------------- /templates/rfc/method-modifiers.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | Note: the bulk of this document is translated almost verbatim from [the Moose 4 | documentation](https://metacpan.org/dist/Moose/view/lib/Moose/Manual/MethodModifiers.pod). 5 | 6 | --- 7 | 8 | Corinna provides a feature called "method modifiers" via attributes. You can 9 | also think of these as "hooks" or "advice". 10 | 11 | It's probably easiest to understand this feature with a few examples: 12 | 13 | ```perl 14 | role Some::Role { 15 | method foo :before () { print "about to call foo\n"; } 16 | method foo :after () { print "just called foo\n"; } 17 | 18 | method foo :around () { 19 | print " I'm around foo\n"; 20 | 21 | $self->$ORIG(@_); 22 | 23 | print " I'm still around foo\n"; 24 | } 25 | } 26 | 27 | class Example :does(Some::Role) { 28 | method foo () { 29 | print " foo\n"; 30 | } 31 | } 32 | ``` 33 | 34 | Now if we call `Example->new->foo` I'll get the following output: 35 | 36 | ``` 37 | about to call foo 38 | I'm around foo 39 | foo 40 | I'm still around foo 41 | just called foo 42 | ``` 43 | 44 | You probably could have figured that out from the names `:before`, 45 | `:after`, and `:around`. 46 | 47 | Also, as you can see, the `:before` modifiers come before `:around` modifiers, and 48 | `:after` modifiers come last. 49 | 50 | When there are multiple modifiers of the same type, the `:before` and 51 | `:around` modifiers run from the last added to the first, and `:after` 52 | modifiers run from first added to last: 53 | 54 | ``` 55 | before 2 56 | before 1 57 | around 2 58 | around 1 59 | primary 60 | around 1 61 | around 2 62 | after 1 63 | after 2 64 | ``` 65 | 66 | # Why use them? 67 | 68 | Method modifiers have many uses. They are often used in roles to alter the 69 | behavior of methods in the classes that consume the role. See [this RFC 70 | page](https://github.com/Ovid/Cor/blob/master/rfc/roles.md) for more 71 | information about roles. 72 | 73 | Since modifiers are mostly useful in roles, some of the examples below 74 | are a bit artificial. They're intended to give you an idea of how 75 | modifiers work, but may not be the most natural usage. 76 | 77 | # Before, after, and around modifiers 78 | 79 | Method modifiers can be used to add behavior to methods without modifying the 80 | definition of those methods. 81 | 82 | ## Before and after Modifiers 83 | 84 | Method modifiers can be used to add behavior to a method that Corinna 85 | generates for you, such as an field accessor: 86 | 87 | ```perl 88 | field $size :reader :writer; 89 | 90 | method set_size :before ($size) { 91 | Carp::cluck('Someone is setting size'); 92 | } 93 | ``` 94 | 95 | Another use for the `:before` modifier would be to do some sort of 96 | prechecking on a method call. For example: 97 | 98 | ```perl 99 | method set_size :before ($size) { 100 | die 'Cannot set size while the person is growing' 101 | if $self->is_growing; 102 | } 103 | ``` 104 | 105 | This lets us implement logical checks that don't make sense as type 106 | constraints. In particular, they're useful for defining logical rules 107 | about an object's state changes. 108 | 109 | Similarly, an `:after` modifier could be used for logging an action that 110 | was taken. 111 | 112 | Note that the return values of both `:before` and `:after` modifiers are 113 | ignored. 114 | 115 | ## Around modifiers 116 | 117 | An `:around` modifier is more powerful than either a `:before` or 118 | `:after` modifier. It can modify the arguments being passed to the 119 | original method, and you can even decide to simply not call the 120 | original method at all. You can also modify the return value with an 121 | `:around` modifier. 122 | 123 | An `:around` modifier receives the original method injected into it via the 124 | `$ORIG` variable. 125 | 126 | ```perl 127 | method set_size :around ($size) { 128 | return $self->$ORIG() 129 | unless @_; 130 | 131 | $size = $size / 2 132 | if $self->likes_small_things(); 133 | 134 | return $self->$ORIG($size); 135 | } 136 | ``` 137 | 138 | **Important**: Note that while the `$ORIG` variable is injected directly into 139 | the `around` method, this behavior and name is provisional and may be changed. 140 | 141 | ## Execution order of method modifiers and inheritance 142 | 143 | When both a superclass and an inheriting class have the same method modifiers, 144 | the method modifiers of the inheriting class are wrapped around the method 145 | modifiers of the superclass, as the following example illustrates: 146 | 147 | Here is the parent class: 148 | 149 | ```perl 150 | class Superclass { 151 | method rant () { printf " RANTING!\n" } 152 | method rant :before () { printf " In %s before\n", __PACKAGE__ } 153 | method rant :after () { printf " In %s after\n", __PACKAGE__ } 154 | method rant :around () { 155 | printf " In %s around before calling original\n", __PACKAGE__; 156 | $self->$ORIG; 157 | printf " In %s around after calling original\n", __PACKAGE__; 158 | } 159 | } 160 | ``` 161 | 162 | And the child class: 163 | 164 | ```perl 165 | class Subclass :isa(Superclass) { 166 | method rant :before () { printf "In %s before\n", __PACKAGE__ } 167 | method rant :after () { printf "In %s after\n", __PACKAGE__ } 168 | method rant :around () { 169 | printf " In %s around before calling original\n", __PACKAGE__; 170 | $self->$ORIG; 171 | printf " In %s around after calling original\n", __PACKAGE__; 172 | } 173 | } 174 | ``` 175 | 176 | And here's the output when we call the wrapped method (`Child->rant`): 177 | 178 | ``` 179 | $ perl -MSubclass -e 'Subclass->new->rant' 180 | 181 | In Subclass before 182 | In Subclass around before calling original 183 | In Superclass before 184 | In Superclass around before calling original 185 | RANTING! 186 | In Superclass around after calling original 187 | In Superclass after 188 | In Subclass around after calling original 189 | In Subclass after 190 | ``` 191 | 192 | # Exceptions and stack traces 193 | 194 | An exception thrown in a `:before` modifier will prevent the method it 195 | modifies from being called at all. An exception in an `:around` modifier may 196 | prevent the modified method from being called if it's thrown before 197 | `$self->$ORIG` is called, but not after. An exception in an `:after` modifier 198 | obviously cannot prevent the method it wraps from being called. 199 | 200 | From the caller's perspective, an exception in a method modifier will look 201 | like the method it called threw an exception. However, method modifiers are 202 | just standard Perl subroutines. This means that they end up on the stack in 203 | stack traces as an additional frame. 204 | 205 | # Caveats 206 | 207 | Be extremely careful if you use method modifiers to alter the output. If 208 | multiple modifiers are used and one adds $10 to a total and another one adds 209 | 20% VAT (tax), the final result will depend on the order the modifiers have been 210 | applied. Because this order is not guaranteed, you cannot be sure of what the 211 | result will be. Instead, write a `final_total` method (or something similar) 212 | which guarantees the order of application: 213 | 214 | ```perl 215 | method final_total () { 216 | my $total = ... calculate total 217 | $total = $self->some_surcharge($total); 218 | $total = $self->add_vat($total); 219 | return $total 220 | } 221 | ``` 222 | 223 | A method modifier implicitly adds the method to the list of required methods. 224 | 225 | Modifiers do _not_ get applied to methods until class/role composition is 226 | finished. Otherwise, the modifiers could be applied to the wrong method. 227 | -------------------------------------------------------------------------------- /templates/rfc/methods.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | Corinna offers class methods and instance methods. You must also specify if 4 | they override a parent method. 5 | 6 | # Instance Methods 7 | 8 | Instance methods are defined via `method $identifier (@args) { ... }`. They 9 | automatically have immutable `$class` and `$self` variables injected into 10 | them. `$class` contains the name of the class from which this method was 11 | called. `$self` is an instance of the current class. 12 | 13 | ```perl 14 | method name () { 15 | return defined $title ? "$title $name" : $name; 16 | } 17 | ``` 18 | 19 | Instance methods can access both class data and instance data and call both 20 | instance and class methods. 21 | 22 | # Class Methods 23 | 24 | Class methods are defined via `method $identifier :common (@args) { ... }`. 25 | They automatically have an immutable `$class` variable injected into them. 26 | This contains the name of the class from which this method was called. 27 | 28 | ```perl 29 | method foo :common () { 30 | say "We were called via the $class class"; 31 | } 32 | ``` 33 | 34 | Class methods cannot call instance methods (since they have no `$self`) and 35 | referencing instance data in a class method should be a compile-time error. 36 | Ths includes trying to reference `$self` in a class method. 37 | 38 | # Overridden Methods 39 | 40 | If a method in the current class overrides a method in a parent class, a warning 41 | will be issued. To suppress that warning, use `:overrides`. 42 | 43 | ```perl 44 | method name :overrides () { 45 | ... 46 | } 47 | ``` 48 | 49 | Note that instance methods can only override instance methods and class 50 | methods can only override class methods. 51 | 52 | # Abstract Methods 53 | 54 | Abstract methods are declared as forward declarations. That is, methods 55 | without a method body and without a signature. 56 | 57 | ```perl 58 | method foo; 59 | method bar :common; 60 | ``` 61 | 62 | They have two uses. Any class with an abstract method must declare itself as 63 | abstract. Failure to do so would be a compile-time failure. 64 | 65 | Abstract methods declared in [roles](roles.md) are "required" methods that 66 | must be implemented by the consuming class or by other roles consumed at the 67 | same time. 68 | 69 | *Important* abstract methods must not be listed with arguments. These are a 70 | syntax errors: 71 | 72 | ```perl 73 | method foo (); 74 | method bar ($baz); 75 | ``` 76 | 77 | ## Checking Abstract Methods 78 | 79 | When compiling code containing abstract methods, the check to see if the 80 | method is overridden it should be compile time for a class and composition 81 | time for a role. You should be able to compile an abstract class itself with a 82 | standard `perl -c` check, but any class which inherits from the abstract class 83 | and doesn't override the methods would either need to be declared `abstract` 84 | or have a compile-time failure. 85 | 86 | # Private Methods 87 | 88 | Private methods are declared with the `private` keyword: 89 | 90 | ```perl 91 | method foo :private () {...} 92 | method bar :private :common () {...} 93 | ``` 94 | 95 | Private methods can only be called from methods defined in the same 96 | namespace and file at _compile time._ 97 | 98 | * Private methods are not inherited 99 | * If a class or role has a `:private` method with the name matching the name of 100 | the method being called, the dispatch is to that method. 101 | * Even if a parent class has a public or private method with the same signature, 102 | the methods in a class will call its private method, not the inherited one 103 | * Roles and classes cannot call each other's private methods 104 | 105 | Note that this means: 106 | 107 | * Roles cannot require private methods 108 | * A role's private methods can never conflict with another role or class's private methods 109 | * You cannot use `:overrides` and `:private` on the same method 110 | 111 | ## Private Methods in Roles 112 | 113 | There is nothing special about private methods in roles, but they are _not_ 114 | flattened into the consuming class and cannot conflict with class methods. 115 | Private methods are bound to the namespace in which they are declared. This 116 | gives us great encapsulation, but does it require the method be bound at 117 | compile-time rather than runtime? If so, does Perl even support that? Or do we 118 | need to do a runtime check every time? 119 | 120 | Thus: 121 | 122 | ``` 123 | role SomeRole { 124 | method role_method () { $self->do_it } 125 | method do_it :private () { say "SomeRole" } 126 | } 127 | class SomeClass :does(SomeRole) { 128 | method class_method () { $self->do_it } 129 | method do_it :private () { say "SomeClass" } 130 | } 131 | my $object = SomeClass->new; 132 | say $object->class_method; # prints "SomeClass" 133 | say $object->role_method; # prints "SomeRole" 134 | ``` 135 | 136 | The above `do_it` methods do not conflict because it's not provided by the role. 137 | It's strictly internal. Further, it cannot be aliased, excluded, or renamed by 138 | the consumer. This gives role authors the ability to write a role and have 139 | fine-grained control over its behavior. 140 | -------------------------------------------------------------------------------- /templates/rfc/mvp.md: -------------------------------------------------------------------------------- 1 | # MMVP 2 | 3 | The Corinna MVP has been accepted by The Perl Steering Committee and work on 4 | integrating Corinna into the Perl core has begun. 5 | 6 | This is an MMVP (Minimally Minimal Viable Product). This is written after 7 | guidance provided by the Perl Steering committee. This addresses a few 8 | concerns. 9 | 10 | * The more we push into core, the more bugs there will be 11 | * The more we push into core, the more time it will take 12 | * The more we push into core, the more people might rely on misfeatures 13 | 14 | Thus, Corinna development for core will be staged, with subsequent work to 15 | realize the full MVP. 16 | 17 | Thus, we want the simplest thing that could possibly work in the first release. 18 | 19 | What follows is a minimal description of what we'd like for the MMVP. The plan 20 | is to implement this in seven stages. Each stage will be pushed separately, 21 | giving us time to test and verify that it does what we need. Note that some 22 | features are specified, in terms of semantics, but in the spirit of "no plan 23 | survives first contact with the enemy," we will nail some of the syntax down as 24 | we write tests to verify the behavior and solicit feedback from those playing 25 | with it. 26 | 27 | # The Seven Stages 28 | 29 | ## 1. Classes 30 | 31 | Initial `use feature 'class'` to add basic `class`, `field`, and `method` keywords. 32 | This will include `ADJUST` and `ADJUSTPARAMS` phasers. 33 | 34 | No roles, no class/field/method attributes, no MOP. 35 | 36 | ## 2. Class inheritance - class :isa() attr 37 | 38 | Single-inheritance only. 39 | 40 | 41 | ``` 42 | class Employee :isa(Person) { 43 | field $employee_id :param; 44 | } 45 | ``` 46 | 47 | ## 3. Roles, and class/role :does() attr 48 | 49 | ``` 50 | role Role::Created { 51 | field $created :reader = time; 52 | } 53 | 54 | class SomeClass :does(Role::Created) { 55 | ... 56 | } 57 | ``` 58 | 59 | Note: for roles, the current implementation of required methods is to simply 60 | create a forward declaration: `method foo;` without listing the signature. 61 | Signatures are currently not introspectable, so we cannot use them to verify 62 | that the correct required method is present, so we only use the name. 63 | Including a signature in the forward declaration might be self-documenting, 64 | but for now, we'd prefer to omit it because this might impact forward 65 | compatibility. 66 | 67 | ## 4. Various "convenience" attributes - 68 | 69 | ``` 70 | field :reader :writer :accessor :mutator 71 | field :weak 72 | field :param 73 | method :overrides() 74 | ``` 75 | 76 | At this stage, most of the basics are in place and we have a useful system. 77 | 78 | ## 5. Field initialiser blocks 79 | 80 | A postfix `{ expression }` block on a field can set its default value. 81 | 82 | ``` 83 | field $answer :param = 42; 84 | ``` 85 | 86 | ## 6. MOP 87 | 88 | The metaobject protocol, similar to `Class::MOP`. 89 | 90 | ## 7. Method modifiers (around, before, after) 91 | 92 | They behave similarly to the `around`, `before`, and `after` method modifiers 93 | from Moo/se. 94 | 95 | # Missing Features 96 | 97 | Obviously, quite a few features are missing from this RFC of Corinna. Our 98 | intent is to roll them out as quickly as is feasible, but to ensure the 99 | foundation is stable. 100 | 101 | # Potentially Breaking Changes 102 | 103 | The following features are not planned for the MMVP and might break your code 104 | in subsequent releases. 105 | 106 | * Error on unknown constructor parameters 107 | * Deterministic destruction might cause issues when introduced 108 | * Many other features (see the github repo) 109 | 110 | There are a ton of other feature omitted, but have been not mentioned here 111 | because they're not even part of the Corinna MVP (role exclusion and aliasing 112 | being a perfect example). 113 | -------------------------------------------------------------------------------- /templates/rfc/phasers.md: -------------------------------------------------------------------------------- 1 | At the present time, some final details of phasers are still being decided. 2 | For Corinna, we have two new phasers, `ADJUST` and `DESTRUCT`. 3 | 4 | # `ADJUST` 5 | 6 | The `ADJUST` phaser is called just after object construction (`new(..)`) but 7 | before the object is returned. This allows the developer to apply additional 8 | logic which cannot be cleanly represented by merely assigning values to fields. 9 | 10 | All class and instance data is available in the `ADJUST` phaser. 11 | 12 | # `DESTRUCT` 13 | 14 | The `DESTRUCT` phaser is called when the current instance goes out of scope. 15 | 16 | ```perl 17 | DESTRUCT { 18 | if ( 'DESTRUCT' eq ${^GLOBAL_PHASE} ) { 19 | ... 20 | } 21 | ... 22 | } 23 | ``` 24 | 25 | There is ongoing discussion about whether a boolean "in global destruction" 26 | argument should be provided, or a possible destruction object should be 27 | provided. However, no phases currently take arguments, so it's an open 28 | question. 29 | 30 | One possible use of a destruction object is that it could check if a 31 | `PERL_DESTRUCT_TRACE` (or similar) environment variable is true and capture a 32 | stack trace of where it went out of scope. This would be very useful for 33 | debugging. 34 | 35 | ## Deterministic Destruction 36 | 37 | Currently, `DESTROY` run on standard Perl objects tends to destroy your state 38 | in random order. It's easy to try to use the object in `DESTROY` and find that 39 | it's incomplete. 40 | 41 | In Corinna, fields are indended to be destroyed in reverse order of 42 | declaration, instance fields and then class (common) fields, from parent to 43 | child. Consider the following (note that the syntax for `:common` might 44 | change): 45 | 46 | ```perl 47 | class Parent { 48 | field $one; 49 | field $two; 50 | field $three :common; 51 | } 52 | 53 | class Child :isa(Parent) { 54 | field $four; 55 | field $five :common; 56 | field $six; 57 | } 58 | ``` 59 | 60 | The destruction order should be guaranteed to be: 61 | 62 | 1. `$six` 63 | 2. `$four` 64 | 3. `$five` # class data 65 | 4. `$two` 66 | 5. `$one` 67 | 6. `$three` # class data 68 | 69 | **Important**: at the current time, class data will only be destroyed in 70 | global destruction. In the future, if Corinna classes can become "first class" 71 | in the Perl language, if a class can fall out of scope prior to global 72 | destruction, then yes, class data can be destroyed prior to global 73 | construction (e.g, for an anonymous class created at runtime). 74 | 75 | ## Incomplete Destruction 76 | 77 | Sometimes you have code that *must* use a `DESTRUCT`, but it's unclear if the 78 | object has been set up properly (perhaps it through an exception in `ADJUST`, 79 | for example). Running `DESTRUCT` on data that doesn't exist is fraught with 80 | bugs. The following is one way of handling that: 81 | 82 | ```perl 83 | class MyClass { 84 | field $constructed; 85 | 86 | ADJUST { 87 | ... 88 | $constructed = 1; 89 | } 90 | 91 | DESTRUCT { 92 | return unless $constructed; 93 | ... 94 | } 95 | } 96 | ``` 97 | 98 | # Phaser Call Order 99 | 100 | Class `ADJUST` phasers are called on the root class before its roles `ADJUST` 101 | phasers which are called before child `ADJUST` phasers and its roles phaswers. 102 | 103 | `DESTRUCT` role phasers are called before class `DESTRUCT` phasers which are 104 | called before parent `DESTRUCT` phasers. 105 | 106 | **Important**: for a given level of the inheritance hierarchy, if more than 107 | one role is consumed, the order in which its `ADJUST` and `DESTRUCT` phasers 108 | are called is not guaranteed. This is deliberate to prevent people from 109 | assuming they can rely on role consumption order. 110 | -------------------------------------------------------------------------------- /templates/rfc/questions.md: -------------------------------------------------------------------------------- 1 | # Open issues for the RFC 2 | 3 | ## Corinna v Other objects 4 | 5 | What would be the easiest way to distinguish between Corinna and other kinds 6 | of objects? I think having a base class of `OBJECT` solves this. 7 | 8 | Thus, anyone could simply ask if `$thing isa OBJECT` and find out of it's 9 | Corinna or not. This would be very useful if they want to use the MOP and 10 | discover it doesn't work on a regular blessed reference. 11 | 12 | ## Multiple Variables Types In A Field 13 | 14 | Can fields have more than one kind of variable? 15 | 16 | ```perl 17 | field ($x, @y); 18 | ``` 19 | 20 | If so, do we disallow attributes? 21 | 22 | ## Twigils? 23 | 24 | ```perl 25 | field $:x; 26 | 27 | method inc ($x) { 28 | $:x += $x; 29 | } 30 | ``` 31 | 32 | Pros: 33 | 34 | * You can't accidentally shadow class/instance data. 35 | * Great Huffman encoding for "this is not a regular variable" 36 | * Easy to syntax highlight 37 | 38 | Cons: 39 | 40 | * No direct parser support for twigils 41 | * Is somewhat controversial 42 | * May contribute to "line-noise" complaints 43 | 44 | ## Overridding Fields 45 | 46 | A method can override a parent method explicitly to avoid a warning: 47 | 48 | ```perl 49 | method move($x,$y) :overrides {...} 50 | ``` 51 | 52 | Should methods generated via `field` attributes be allowed to override parents? 53 | If so, how do we signal this? 54 | 55 | ## `can`, `does`, and `isa` 56 | 57 | It has been suggested that we offer new versions of `can`, `does`, and `isa`. 58 | They would not take arguments. 59 | 60 | * `can`: returns all methods the current class can do (including inherited) 61 | * `does`: returns all roles the current class does 62 | * `isa`: returns the iteheritance list 63 | 64 | Because these methods currently do not take arguments, this might be extending 65 | them instead of modifiying them. However, this would still be modifying 66 | current behavior. Or we could put this in an `OBJECT` base class for Corinna. 67 | However, this would mean the external behaviors would be different for Corinna 68 | and other objects. 69 | 70 | I think this is probaby out of scope for Corinna. 71 | 72 | ## Inline POD? 73 | 74 | Because we need a postfix block, many people will be disappointed that we 75 | won't have inline POD quite as neat as what we had: 76 | 77 | ```perl 78 | class Foo { 79 | 80 | =head1 METHODS 81 | 82 | =head2 C 83 | 84 | This method does something 85 | 86 | =cut 87 | 88 | method bar() { ... } 89 | } 90 | ``` 91 | -------------------------------------------------------------------------------- /templates/rfc/quotes.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | As Corinna has gotten closer to submitting an RFC to P5P, people have been 4 | asking about the people who developed or influenced Corinna. This list is not 5 | exhaustive and changes are to be expected. 6 | 7 | Names are in alphabetical order. 8 | 9 | ## [Curtis Poe, a.k.a. Ovid](https://metacpan.org/author/OVID/), Perl book author and lead designer of Corinna 10 | 11 | > We love Perl, but it's struggling. We know that. Corinna is not 12 | > going to save it. Nor is a better code of conduct. Nor is Perl 7, 13 | > concurrency, types, or \. Instead, it's 14 | > all of these things, and many more, with a strong community to support the 15 | > language, that will continue to help Perl thrive. Let's start being the 16 | > people we tell our children to be. 17 | 18 | ## [Damian Conway, a.k.a. _That_ Damian](https://metacpan.org/author/DCONWAY), Computer scientist and (too many accolades to list) 19 | 20 | > When people ask me whether I support the Corinna proposal, my response is: 21 | > OH, HELL YES! I’ve literally been waiting two decades to see Perl gain a 22 | > proper, built-in, declarative, encapsulated, and performant OO system. And 23 | > this, I firmly believe, is it. 24 | 25 | [Read his full statement at blogs.perl.org](http://blogs.perl.org/users/damian_conway/2021/08/a-dream-realized.html). 26 | 27 | ## [Dan Book, a.k.a. Grinnz](https://metacpan.org/author/DBOOK), CPAN author and docs wrangler 28 | 29 | > Moose and its derivatives provide a comprehensive and popular object 30 | > framework for Perl, but some parts have always been workarounds for the 31 | > limitations of the hash-based object format and existing Perl syntax and 32 | > semantics. Corinna is a methodical reimagination of what a modern built-in 33 | > Perl object framework could look like without those limits, informed by the 34 | > decades of object-oriented applications within and without. 35 | 36 | ## [Matt Trout, a.k.a. mst](https://metacpan.org/author/MSTROUT), Creator of Moo 37 | 38 | > The Corinna process is highly promising, and I'm very much enjoying the fact 39 | > that almost everybody involved in the design process is a prolific and 40 | > highly experienced Moo/se user and yet we still manage to argue about 41 | > absolutely everything. 42 | 43 | ## [Sawyer X, a.k.a. Sawyer X](https://metacpan.org/author/XSAWYERX), former Perl pumpking 44 | 45 | > Corinna is a feature-rich, modern OO framework. It has a clean, minimal, yet 46 | > expressive syntax, keeping OO best practices as its defaults. It comes from 47 | > a veteran of OO in general and in Perl in particular (Moo, Moose, and 48 | > everything under the sun before and between those), showing how to design a 49 | > framework that avoids the pitfalls we only discover years into practice. It 50 | > is not a competitor to Moose. It is the next level from it. 51 | > 52 | > Corinna is how Perl should look like and how we should write it. Expressive, 53 | > minimal and exact, clean, and powerful. 54 | 55 | ## [Stevan Little, a.k.a. "Damnit, Stevan"](https://metacpan.org/author/STEVAN), creator of Moose 56 | 57 | > I am glad Ovid is working on this, better him than me 😛 58 | -------------------------------------------------------------------------------- /templates/rfc/roles.md: -------------------------------------------------------------------------------- 1 | # Background 2 | 3 | Roles in Corinna are based on the [Traits, composable units of 4 | behavior](http://scg.unibe.ch/archive/papers/Scha02bTraits.pdf) 5 | paper, we particularly find interest in [Traits: the formal 6 | model](http://scg.unibe.ch/archive/papers/Scha02cTraitsModel.pdf). 7 | 8 | In particular, according to the formal model, traits are designed to be both 9 | commutative (`a+b=b+a`) and associative (`(a+b)+c=a+(b+c)`). While we cannot 10 | perfectly guarantee this in the face of method modifiers, we will see to 11 | return to this model. 12 | 13 | # Overview 14 | 15 | Roles are designed to share small sets of behaviors which address 16 | cross-cutting concerns. Roles may consume other roles, but they may not 17 | inherit from classes, nor may they be inherited from. 18 | 19 | Roles may require one or more methods to be implemented. All abstract methods 20 | in roles are considered to be required. For Corinna, any forward declaration 21 | (a method declared without a body: `method foo;`) of a method is considered an 22 | abstract method. 23 | 24 | ```perl 25 | role SomeRole { 26 | method foo; 27 | method bar :common; 28 | ... 29 | } 30 | ``` 31 | 32 | Any non-private methods with method bodies are considered to be methods the 33 | role provides. These may be both class and instance methods. 34 | 35 | *Important* required methods must not be listed with arguments. These are a 36 | syntax errors: 37 | 38 | ```perl 39 | method foo (); 40 | method bar ($baz); 41 | ``` 42 | 43 | ```perl 44 | role SomeRole { 45 | method foo () { ... } # instance method provided 46 | method bar :common () { ... } # class method provided 47 | method baz :private () { ... } # private methods are not provided 48 | } 49 | ``` 50 | 51 | Any fields declared in the role are completely private unless standard 52 | field attributes are used to expose them 53 | 54 | ```perl 55 | role SomeRole { 56 | field $name :reader; # ->name is provided 57 | field $age; # private to this role 58 | } 59 | ``` 60 | 61 | Roles may _not_ access the fields or methods of the class the 62 | role is consumed into unless those have already been exposed in the public 63 | interface. 64 | 65 | # Example 66 | 67 | It is entirely possible, for example, to want to have an identical mechanism 68 | to provide unique, repeatable UUIDs to different classes. It might look like 69 | this: 70 | 71 | ```perl 72 | role Role::UUID { 73 | use Data::UUID; 74 | 75 | # these are private to this role 76 | my $uuid = Data::UUID->new; 77 | my $namespace_uuid = $uuid->create_str; 78 | 79 | # abtract methods in roles are required 80 | method name; 81 | 82 | method uuid () { 83 | return $uuid->create_from_name_str( $namespace_uuid, $self->name ); 84 | } 85 | } 86 | ``` 87 | 88 | And to use that in your class: 89 | 90 | ```perl 91 | class Person :does(Role::UUID) { 92 | field $name :param :reader; 93 | } 94 | ``` 95 | 96 | And using that: 97 | 98 | ```perl 99 | my $alice = Person->new(name => 'Alice'); 100 | say $alice->uuid; 101 | ``` 102 | 103 | The above will create a unique, repeatable UUID for a given `Person.name` 104 | (only repeats in a single process due to how UUIDs work). 105 | 106 | Roles may consume other roles and classes may consume one or more roles. Any 107 | method name conflicts are fatal, if and only if the methods come from 108 | different namespaces. 109 | 110 | ```perl 111 | role A :does(C) { method a () { ... } } 112 | role B :does(C) { method b () { ... } } 113 | role C { method c () { ... } } 114 | 115 | class SomeClass :does(A, B) { ... } 116 | ``` 117 | 118 | Thus, in the above example, though A and B both pull in the `c()` method from 119 | role C, there is no conflict because it is the same method. 120 | 121 | However, if `SomeClass` defined a `c()` method, there will be a conflict. 122 | 123 | # Aliasing and Excluding 124 | 125 | For the MVP, we will not be providing aliasing and excluding of methods. 126 | 127 | # `ADJUST` and `DESTRUCT` 128 | 129 | Both the `ADJUST` and `DESTRUCT` [phasers](phasers.md) will be allowed in 130 | roles. Class `ADJUST` phasers are called before its roles `ADJUST` phasers 131 | which are called before child `ADJUST` phasers and its roles phaswers. 132 | 133 | `DESTRUCT` role phasers are called before class `DESTRUCT` phasers which are 134 | called before parent `DESTRUCT` phasers. 135 | 136 | **Important**: for a given level of the inheritance hierarchy, if more than 137 | one role is consumed, the order in which its `ADJUST` and `DESTRUCT` phasers 138 | are called is not guaranteed. 139 | 140 | # Conflicts 141 | 142 | Consuming multiple roles may result in method name conflicts. This is a fatal 143 | error. If a class consumes a role with a method name that conflicts with a 144 | role method, the class method wins, but a warning is issued. 145 | 146 | # Questions 147 | 148 | ## Changing access level of role methods? 149 | 150 | No role method marked as `private` should be composed into the consuming class 151 | or role. However, the consumer may need the behavior, but not want to expose 152 | it. 153 | 154 | If a method exported by a role is public but the consumer does not wish to 155 | expose that part of its interface, should it have a way to adjust the access 156 | level to C? 157 | -------------------------------------------------------------------------------- /templates/rfc/toc.md: -------------------------------------------------------------------------------- 1 | # Sections 2 | 3 | {{TOC}} 4 | --------------------------------------------------------------------------------