Ah, good.
So if you're on YouTube, you probably just missed the first five minutes of this.
I said nothing.
Don't worry about it.
So I decided rather than do what I had done previously, I'm just going to give an overview
of all the major features of Karina for the minimum viable product that we're putting
together.
So it can be, you can have a fairly complete idea in your mind what's going to happen,
because I actually haven't done that talk before, and you probably don't want to go
and read a multi-section RFC and all the work we did to put that together.
So since PURL 5, object-oriented syntax here was just less in IZA.
There's a little bit more than that, but this is primarily the bulk of it.
The model was mostly stolen from Python, and I also do Python programming.
I can see the similarities.
Larry regrets stealing it from Python.
I can understand why, even though I like Python, I'm wrong.
But blessing is that all they do is say, we have methods, and where are those methods?
I'm taking the short version of this, because we're not going to spend a lot of time talking
about the original version of object-oriented programming at PURL.
Because it didn't give you much.
Basically if you wanted everything that you want out of a class-based OO system, then
you've got to write your own constructors.
You've got destroy method in PURL, but destruction is non-deterministic, so that's kind of a
frustration.
It doesn't work as well as you'd like.
If you want to maintain state, if you want encapsulation, all the sorts of things that
you expect to have out of an out-of-the-box OO system you don't have with bless and IZA.
And everyone had to redo it themselves every single time, and if you're a programmer, you
know you don't want to do that.
You want to abstract that away.
So people have abstracted that away a lot.
It's going to depend upon your definition of what a class is or support for a class is.
Well over 80 modules.
This is not an exhaustive list.
I just decided to order them alphabetically by link.
Have fun picking out the one that you happen to like.
If you're familiar with the Lisp Curse, or if you're not familiar with it, go out here,
your favorite search engine for the Lisp Curse.
It will be the top hit, and it will explain how that mess came about and what we're trying
to fix.
So let me make that a bit larger because I can't read that.
Okay, so not everything that you see here is implemented, and not all of it's going
to be implemented, but you do want to see object pad that Paul Evans put together.
That's a test bed for many of the ideas of Karina.
So we can make sure that it actually does what we want it to do.
And there are companies who are using this in production.
It is so valuable to them.
So some of the things you might see will change.
It's work in progress, but I think I've tried to strip out anything really problematic.
I'll call out the things which are what you're saying is work in progress, but this is pretty
close to what we can expect.
A simple class.
It's very simple.
It's not exciting.
You create a new person.
Name is Ovid.
You print the name Ovid.
Here you give them a title.
You print the name.
It automatically pre-pens it with the title.
So there's Dr. McCoy.
Very simple.
This is not complex.
On the left-hand side, that's how you're going to do that using Bless in Old Style Pearl.
Here's how you do this in Karina.
Note that almost all of this is very declarative in nature.
You might quibble on one point.
We'll come back to that later.
But it's very short, very concise.
You probably didn't notice this.
That will mean your code's not going to work correctly because you misspelled the name.
It's not even going to give you a warning.
It's just going to silently fail.
Sort of bugs we love to have, silent failures in code.
In Karina, because that's a lexical variable field title, that's going to be a compile time
error if you misspell it.
That's Moose, by the way.
Moose didn't gain us a lot.
Not true.
It does have Izzah.
Izzah string for those various things.
You could do non-empty string for one of them might be better.
We argue about that all day long.
But basically, Moose is not more terse.
And it also has a lot of startup overhead.
It's not slow per se anymore, but it's not the fastest thing in the world.
But it does make writing an OO code better.
In Karina, same thing, much more terse with the exception of the Izzah.
So let's just walk through this so you can understand what's going on.
To declare a class, you just say class, person.
It used to be to declare a class, you couldn't.
You would say package, person.
And then you would bless a reference into that package.
And it wasn't really a class or package.
It was kind of this thing.
Now they can be separate.
They have a future where we can truly disambiguate these things.
I might add, you can also do it this way with the post-fix syntax.
I prefer this syntax.
I will have it on the slides.
I argued strongly, as the lead designer, I thought I could get away with this, that
we're going to require the post-fix syntax.
I lost that fight.
And so everyone basically almost everyone disagreed with me.
So I went ahead and said, OK, we'll go ahead and make this optional.
But a lot of my examples, well, the post-fix syntax,
absolutely not required.
So don't stress about it, because I know people gave me grief at first a lot.
Field, dollar, name, colon, param.
That is an instance attribute, or instance field, instance slot,
depending upon the language you're coming from.
That's just a piece of data tied to the instance after you construct it.
Because it has colon, param, it is required in the constructor.
You cannot not pass that, or else it will blow up.
Same thing with field, dollar, title, except it has the equals on death.
That means it is optional in the constructor.
You do not need to pass it in.
Or you can use equals misses or something.
You can give it a fake default title if you want to.
Anything after the equals, you can just evaluate and run it,
and that will be assigned as a default value.
And then we have our name method down here,
where we just access those variables directly.
This gives us a chance for a lot of performance benefits.
It also tremendously encapsulates this data,
something which has been traditionally very, very hard to do with older
Perl, because you could always reach inside the object and do stuff.
Many languages make it easy to reach inside the object and do stuff.
When we eventually get around to implementing a meta object protocol,
you will be able to reach inside the object and do stuff.
But we're going to make it harder.
And the intent is you will be allowed to do it, but when you're doing things you
shouldn't do, you got to put some more effort in there.
It's going to be easier to show up on code reviews or just with grep.
Karina, out of the box provides constructors, destructor, state,
composition, encapsulation, private methods, and so on.
The private stuff might actually not make it in the MVP.
We won't cover that.
But basically, most of what you want out of a class-based OO system
is there in a very short declarative syntax.
Just like that, very easy.
But there's more than one way to do it.
So I mentioned this is mostly declarative.
You see the method down there and you're going, I don't have any way I can change
the name and title.
Everything by default is pretty much immutable externally with Karina.
So I'm not mutating that.
So why am I even calculating it every time?
I could just make that a field.
Reader equals if defined title, title name, else name.
And that's computed once and only once at object construction time.
And fields are generally evaluated in the order that they are declared,
which makes it much easier to reason.
In Moose, I think it's evaluated alphabetically.
No, hash order.
Hash order.
Oh, sweet.
Thank you, Steven, for just making me feel even worse about it.
But I've long wanted to submit a patch to see if I could fix that, but
they've said no more patches.
Which is fine, I totally get why.
So because they're constructed in the order that they're found,
you can now have the potential for deterministic destruction because you
can track that order and unwind them in last in, first out order.
I don't know that that will be in the MVP either.
Okay, there's only four keywords.
By the way, class, field, method, and role.
We actually had a lot more originally and then Damian Conway came along and
did a deep dive into the spec.
And he pointed out a way we could reorganize everything just by having four
keywords, class, field, method, and role.
And then attributes to modify their behavior.
Tremendously simplified the code, made the logic much easier to follow,
made the structure much easier to follow.
And now I apologize, this is a much bigger slide,
probably harder for some of you in the back to read.
Class character is a person, that means we've inherited from person.
Karina is single inheritance only.
You'll notice there's a number of OO languages out there which allow no
inheritance.
Some of them allow only single inheritance, they almost invariably give you a way to
work around that, such as interfaces or mix-ins or something else.
Or you can do that with delegation, which delegation is much more powerful than
people think, but there's not a talk about that.
So I've now declared this class.
And you'll notice I have an underscore defense for my reader.
I don't have readers or writers for anything.
Reader means that you can call target arrow underscore defense and read that value.
There's something called trusted methods where you want methods to be
callable by other classes, but you don't want people outside to be able to call them.
We have done a lot of bike shedding on how to get there, and
it's not gonna happen anytime soon.
So for now, I punted and thought this is a reasonable compromise.
We use a familiar pearl syntax for saying underscore defense.
That is, think of it as a trusted method or a private method.
And as a result, you can call that and people outside know not to.
Notice the only methods we have public are isDead, adjust hit points, and
attack, because you want your interfaces to be as small as possible.
Because later on, if you have to change your interfaces,
you're stuck if you've exposed everything publicly.
So, Karina by default forces you to add the colon reader and
colon writer keywords to fields because you have to choose,
you have to opt in to making your contract public.
Rather than with moose and moo and others, the default is everything's public.
And if you want a private, too bad.
And we have this constrain function.
I'll talk more about subroutines being imported.
But basically constrain is a function.
Again, this is something I don't think we're gonna get to in the MVP.
The intent is methods and subroutines are not the same thing.
And you should not be able to call a subroutine as a method.
You should not be able to call a method as a subroutine.
And you can disambiguate them even if they have the same name.
But just something to think about for later work.
So, we did our subclassing, there's a little Dorothy there.
And we create a new dothvader object, a captain Kirk object.
And while not Kirk is dead, Vader beats him with his lightsaber until Kirk is dead.
It's just very simple, it's easy.
It works, yes, Vader will kill Kirk.
I'm sorry, I do for Star Trek to Star Trek to Star Wars.
But in this case, yeah, Vader, yeah, he wins.
Very simple, very easy, and there's nothing when you get down to it,
there's nothing really complicated about the code.
It's simpler, it's easier to write, it's well encapsulated.
But I want to talk about constructors a little bit so
you understand some of the design work that we put in here.
A lot of it we argued, I think it took like three years of arguing to finally
get to something we could agree on.
So, we have key value pairs, named arguments to the constructor,
name, title, and offense.
And it is absolutely required that you do that.
You can create an alternate constructor if you want, called new unnamed and
have a delegate off, but we do this for readability.
And there's also some other benefits.
So right now, here's a constructor in Java, character of Vader equals new character.
And then if you didn't know what those were,
it might not be clear what you're constructing.
And in fact, you've got alternate, you've got optional data for your constructors.
So you have to create multiple constructors.
I won't go into details, but you might have to create multiple, multiple constructors.
If we have in this particular example, or use a hash map and extract it manually.
It's a pain.
Karina, you don't have to do that.
You have a declarative specification at the top of your code.
Here's how our instance data works.
So, writing the manual constructor in Java for a car, that's actually very readable.
It's very easy to read.
Calling it is not.
I don't, I just looked at the code.
I wrote this code and I don't remember it.
I don't know what those numbers necessarily mean.
So, that's why we try to avoid that.
And in Perl, we have named arguments.
Yes, you have to do a little bit more typing.
This is for maintenance.
You absolutely want to make it easier to maintain your code.
And it's gonna kill you a few times.
And you're not gonna be happy about this, but you'll get used to it because it's
gonna become natural, I hope.
So here, that's not character class.
That's a person class.
And we've passed in offense.
Offense is not defined as one of your param fields.
So that's gonna die.
And I've heard people argue, well, I should be able to pass in extra data.
Maybe my son class will use it, or there's some other way I can handle it.
Yes, there is other way you can handle it, like every other authoritarian language does.
Provide something which is actually going to properly capture that.
But the real reason is, remember, title is optional.
So if I misspelled title, it would think it's simply optional data.
Now, because it's mandatory, you can't pass in anything which is not known to the
constructor, then that is going to be a fatal error.
And it's gonna be a very hard to detect bug that you don't have to worry about anymore.
If you want to pass in extra optional data, make a parameter called extra.
Extra column param equals hash ref.
And then just allow them to muddle around with that.
It's much cleaner.
Moose allows you to pass in a hash ref instead of a list.
We do not do that.
We want one way of being able to call the object because it's just simple.
This also preserves the ordering of those in case that becomes necessary in the future.
Also, a hash ref will, any duplicate name in the hash ref will collapse over a previous
one, which is kind of annoying.
There are ways you can work around that if you actually want this behavior for
setting defaults.
But we decided this was the safest way to go, just to make one and
only one way of calling the constructor.
Thank you.
So, I didn't talk fast enough, apparently.
Here, field name, dollar name, dollar name, in both of those, those are lexically scoped.
There is no conflict anymore.
And with bless, if you had a arrow, open print name in your hash ref, but
your parent did too, you're going to blow up.
Here, it's completely encapsulated until you expose it.
Now when you expose it, I have column parameter each, and
I now have two param methods, and that's going to blow up.
You can't override params.
We might restrict that later.
You can override methods.
Sorry, methods automatically generated by param or, sorry, field and other things.
I got ahead of myself.
Never mind.
So I can do this param car name.
That means now you pass that to the instructor's car name, and
there's no longer a conflict with parent class.
Your parent and children classes should always be able to trust their internal
implementation, always.
So when they hit an external implementation, they're making a contract,
and then they've got to negotiate and find out what works.
Here's another example.
Those are also going to blow up.
That's the case where we're actually generating methods, but
we cannot override those directly.
You can create your own little stub method if you want to override it.
Again, you can rename those in order to allow that to be safe.
Class data, field, num characters, colon common means this is class data.
You can also slap colon comma on a method and call that a class method.
Adjust is called after the object is constructed, or actually it's called when
it's hit, sorry, Paul.
Is it called when it's hit or after the object's constructed?
It's called when it's hit, right?
Adjust was run as part of the constructor, yeah.
Okay, destruct will run when the object is destroyed.
So here I can track how many character instances I've created.
It's very simple, works naturally in the language.
And then I have another class, my world class.
I can figure out the difficulty of my world.
I've got my class method available.
I can figure out how many characters and I can tell them how difficult the world is.
Again, it's stuff which is now built into the language and
you don't have to worry about that anymore.
Is there anyone here who does not know what roles are?
Okay, just in case roles are kind of like mixins you'd find in Ruby or
interfaces with default implementations you'd find with other languages.
And these allow you to take a small subset of behavior which doesn't
necessarily belong to a class, a specific class, and move it often to its own role.
And then you can compose it into the class.
And then you will get that behavior.
However, those methods are flattened into the class directly.
There's no tricks with inheritance, there's no funky dispatch or
anything like that, it's actually in the class.
So method as hash ref, because this is what we call a forward declaration,
because it doesn't have a body for the method.
Anything with a forward declaration is required to be implemented by whatever is
calling it.
It can be implemented by the class directly or
if the class consumes other roles as other roles might implement it.
And then to JSON, here's another example where we want to get to the point where
we can disambiguate.
This is probably a terrible example because you don't wanna confuse those.
But the reality is you should be able to call those separately and
have them work correctly, even though you probably shouldn't name them the same.
But it gets you some safety in the code and avoids the odd case where you called
subroutine as a method, and believe me, I've hit that before.
And self is injected directly into the method.
You don't have to declare it in your signature.
If you have a common method, so self, you also get a dollar class variable,
which is the class name of the invocant.
If you have a dollar common attribute, that means it's a shared method,
which means self will not be available, but dollar class will.
And again, those will fail at compile time if you get them spelled wrong.
Which means if you declare something as a class method with a colon common and
you're trying to access dollar self in there, that should be a compile time failure.
You don't wanna use this code, but here, field dollar cash, once again,
my implementation should be able to trust its internals.
So nothing else actually gets to see the dollar cash that I have declared in my role.
You don't wanna use this because this would work if you can guarantee your
objects are immutable, but you can't.
So you actually probably don't wanna cash those.
But this is one way you can have of accessing data inside the role,
which you don't share with others.
And then using a role, it's pretty simple.
So there's my serializable role, this one just does JSON.
My character is a person, does serializable.
All I have to do is define a hash ref method.
And hopefully, when it's called up there,
it will properly serialize into JSON, depending upon.
I did a lot of hand waving there.
But that's basically how it works.
If you're familiar with roles, it's what you expect out of roles.
So here's the various attributes we have.
Class attributes.
We have is a and does.
Is a, again, is single inheritance.
You can put one class in there.
Okay, great, I've got plenty of time.
Does, however, can have a comma separated list of roles that are allowed in there.
If you're familiar with roles, there's ways you can exclude or alias methods.
We don't actually provide that syntax here because we argued too much about how to
make that work, and we just punted on that.
I apologize.
Well, attributes, it simply does.
Roll serializable does some other role, whatever.
Maybe it does a YAML role, an JSON role, and a TAML role, and
can serialize all those different things if it's given the right data structure.
Quite possibly cannot, but that's how roles work.
Roles can consume other roles.
And we do want to make sure we preserve the commutative and
associative behavior so you can mix and match roles any way you want to in any order.
In any combination, and it should work correctly unlike with inheritance and
mixins where if you shuffle the order, you have no guarantee your code's gonna work anymore.
Field attributes, this one's a little bit more.
Reader, or you can rename your reader.
Writer, automatically propends the name with set underscore,
because we're disambiguating between the reading and the writing.
And there's reasons for that dealing with return types and
not being able to overload things properly.
And also wanting to discourage people from writing mutable objects, but
making it easy for them to do if they wish to.
But it's available there.
Param, whether or not it's available in the constructor.
Week, to create a weak reference.
Column common means it's a class data.
Method attributes, do we override a parent method?
If you want a method to be abstract in your parent class, just again,
just declare it as method, method name, do not use a signature.
And do not provide a method body, it's automatically an abstract class.
And it must be overridden in a child class or with luck it will be a compile time error.
Common, so you can have a class method which does not inject the dollar self variable.
Around before and after are the standard method modifiers that you have.
To be honest, I wish we had gone with something like, sorry folks,
Python decorators because it's so much easier to use.
But that would require attributes to be modified and how they actually get handled.
Because right now the data inside of the arguments to an attribute is just a simple
string, can't be parsed effectively or can't be run effectively.
There's some discussion, I think Paul has been handling some of that,
about how to maybe change that in the future.
Some of the things we have already written in just the very beginnings of Karina.
We have Stella, an actor model for Pearl.
An actor model basically means if you have a box of toys,
they know how to play with each other, you don't have to play with them yourself.
That's the simple explanation.
What's that?
Okay, thank you.
I'm very curious to see that.
We also have a yellow cooperative message passing concurrency event loops,
actors, promises.
That one looks like a lot of fun.
That's also done by Steven.
You don't like that?
Okay, these are some of the early prototypes we've been building with this.
I used Karina a lot.
This is a rogue-like tutorial that Chris Prather has been putting together.
You've seen Rogue before, most of you.
And I elated some of those, but basically parts one through six.
He hasn't done more than that.
What amazed me is I thought we would have to have much more of Karina built for
it to actually be useful.
I was wrong.
Even a very tiny subset, properly designed subset of a class-based system works very
well and is very powerful.
I was really surprised by that.
It also might force you to use composition and delegation more often,
which trust me, that's your friend.
I won't go into it right now.
And I'm sorry, that was very fast.
It was an overview.
It was probably one of my least exciting talks, but I wanted to be able to have
something that I can refer people to this and say, look, here's a short overview.
If you want to have a video instead of reading the RFC or something like that.
The actual RFC is at github.com, Perlapallo, Karina, BlavMessor.
I'll put this up a slideshare.
There's the seven stages which are referred to in that MVP of what we're
trying to implement, unknown timeline as to when it's going to be done.
It's already much more powerful than I thought.
Really surprised by that.
There's lots more to be done.
If you want to see this, the single best thing I think you can do is download it,
compile it, start playing around with it, send bug reports to Paul,
give feedback, write tests for it, write documentation for it.
We need that because conceptually it's very small, but under the hood,
there's a lot of stuff which has to happen to make that done.
And anything you could do to help Paul take some of that work off of him means
we will get it out there faster.
Does anyone have any questions?
No, yes, sorry.
Please speak up by the way, I'm a bit hard of hearing.
Yeah, you mentioned the overrides as a way of following my pessimism.
What happens if you have a base method and a derived class method with the same
name without the overrides attribute?
Right now I think that should be a, if the method is defined in the, sorry,
what happens if in a subclass you're overriding a class which already has
that method defined but doesn't, but has a body, so
you're overriding something which already exists.
That's something I, one thing a parent class generally should not know who or
what is subclassing it.
It shouldn't have to know that if that is at all possible, because that winds up
coupling it too tight with the subclass.
And as a result, if we try to put any sort of annotation on the parent class
saying this is sub, subclassable, we might want to be able to allow a final
attribute on something so you can't overwrite it, but we had to get an MVP out
there.
So right now it's a method body's defined.
If you overwrite it in a subclass, adding the override tag is good.
And I would like it to have a warning if you override something and you don't
have the override tag.
Or if it's an abstract method and you don't overwrite it, then it's fatal.
Or maybe if you override, you don't have the override attribute, then it should
be fatal, but we can punt on that.
Any other questions?
Can the rules have a method body?
I'm sorry?
Can the rules have a method body?
If it's a required method in the role, it cannot have a method body.
There are ways you could work around that.
You could create a default method, which has a separate name from the required
method.
And inside of your methods, it's going to, no, you'd still have to have the other
method required.
So it's a yada, yada, yada, operator.
I found a very nice.
Oh, I forgot about that.
So basically you make a method and then you just, the body of method is dot, dot,
dot, which is the yada, yada, yada operator, which was added, I don't know when.
5, 5.
So it's been around forever.
And all it does is it just blows up different times.
It's died with no messages.
But it's very useful for, yeah.
Yeah, that might work.
Any other questions?
Or do we still have time?
Two minutes.
Not you.
You were exporting stuff, or exporting subroutines.
Lexical, exportated.
I've been using it and it's been working quite well with it, Corinna.
And it doesn't seem to conflict.
Oh.
Lexically exporting subroutines.
And then it removes the symbol.
Yeah.
So it's bound but not callable.
Yeah, in the built-in package, there's an export Lexically, right?
And then you put that inside your import, you can export things flexibly.
And then they're entirely different scope.
Nice.
OK.
I very much like that.
I'll show you.
OK.
Actually, talk to Paul, because he's the one who's going to be doing some.
We'll talk 20 minutes and I'll talk about it.
What's that?
Wait 20 minutes and I'll be talking.
OK.
One last question.
OK.
Thank you very much.
Thank you.