Tip:
Highlight text to annotate it
X
Jenny Donnelly: I'm so excited to announce our final keynote speaker of the day. Ariya
is one of the most wisest and deepest engineering talents I've ever met. This is the gentleman
that brought us the eminently useful Esprima. He's going to be talking to us about design
principles for JavaScript APIs. Please give him a warm welcome. Thank you.
[applause]
Ariya Hidayat: Thank you so much. Glad to be here. I realize that I stand here between
you and whatever party you're going to have tonight, so let's make it quick and painless.
I work for a startup called Shape Security dealing with web content security, and I'm
a big fan of web platform. You might have seen or used or hated some of my handiworks
over there.
When I moved to the web platform for the first time a couple of years ago, I already know
some of the other programming environments. I know C++, I know Java. In that particular
world there has been already good references, very good, excellent references on how to
design API. For example, with Java most people refer to that fantastic work from Joshua Bloch.
For C++ there's a very nice Qt API Design Principles that a lot of people adopt in their
projects.
Now you might think that, well, I don't build libraries, I don't build YUI or Alloy or any
other kind of frameworks, so why should I care about API? It turns out that API stands
for Application Program Interface, and as long as you adopt the best practices of modular
application architectures you will have coupling between modules. Your application won't be
monolithic, and therefore it's very important to pay attention to those kind of interfaces.
I don't think I need to convince you why we need good API. First of all, nobody reads
API documentations. At least I can speak for myself. If I see a shiny library I'll download
it, I'll pick an example, and I'll tweak it to suit my needs. Whenever I have problems
then I'll look at the documentation.
One thing that you might notice a lot is that you usually write the code once, or even a
couple of times if there are bugs there or if you are very sloppy, but you're trying
to read the code many, many times. I don't know about you but I happen to read some piece
of code and realize that it's garbage and then I try to figure out which idiot wrote
that kind of garbage, and it turns out to be me from five years ago or two years ago.
In some cases if you do a pull request workflow, you need to review someone else's code, you
need to read that code. Although that guy might have written it only once. It's very
important for the subsequent discussion to realize what kind of impact that you need
to take into account if you ahead of time know that your code will be read many, many
times.
When I go round and ask people, hey, I'm new to this JavaScript world, I want to write
these beautiful application libraries, what kind of advice can you give to me to make
a very nice API? This is a typical answer that I get. Your API needs to be readable,
it has to be consistent, and it has to be crystal clear.
These are clichés and slogans at best, or wishful thinking. Because who doesn't want
to have readable API? I never want intentionally my API to be completely unreadable. So that's
not what I'm going to show here.
Some of the collection of rituals that I've practiced for a while and digesting from someone
else's experience are just three things. To ensure consistency we can apply the concept
called static polymorphisms. When you already have consistency in place, the next step is
to watch for all kind of traps caused by many, many kinds of shortcuts. Last but not least
is you already have consistent API, you don't have any crazy shortcuts, how can we make
sure that that kind of code makes sense? In other words there's no confusion.
Without further ado, let's go to polymorphism. When we speak about polymorphism in object
oriented programming usually that means a particular function or properties that you
can apply to different type of objects. With static polymorphism we use the polymorphism
term here loosely, meaning that we observe those function properties etcetera as part
of different types.
A quick example. If I have these three radio buttons and I have one check box and I have
one push button, a bad API will force me to write this kind of thing if I want to change
all the text that corresponds to those form elements. Because I line it up nicely here
you can see that they are not consistent in the sense that if I need to change the text
for a radio button I need to use a property called value, but apparently it's different
if it's check box, and it's also different if it's a push button.
Once you see that kind of piece of code then you realize that you need to change or tweak
the property name so it looks like this.
It might seem obvious, but the lesson learned from this is that whenever you try to create
new objects, new components, new widgets, and you search for the name of the properties,
look at the existing names. Don't try to invent new names unless there's a very, very strong
reason to do so.
This doesn't apply only for buttons or button-like form elements. Progress bar versus slider.
They serve different purposes. For example you can't really change the value of progress
bar unless you change it programmatically. There's no user input to change that. But
there should not be any differences in the way that you change the appearance of a progress
bar as opposed to a slider.
I've seen an API that you need to use a different property name if you change the value for
slider, and when you look at the progress bar it's also different property names. When
you apply the polymorphism to properties then a set of property names that the user or the
developer might use tend to convert to a particular pattern.
What about functions? Let's say you have pointer. You need to move it to somewhere else, and
you also have a rectangle you need to move it somewhere else. It's a matter of applying
translation to this point and rectangle. But in some cases they might have different names.
In this bad example apparently to move a rectangle you have to use translateBy. There's an additional
two letters there. That's completely unnecessary.
In this example, again, it's very obvious because it's one after another, but those
might be separated by several thousand lines, they may live in different files, and therefore
it's not so obvious.
If you pick function names, try to look at existing objects that have similar functionalities
or similar types, and then try to infer static polymorphism from there.
We see the choice of naming properties and functions, but there are also many cases where
you have to have a little bit of different ways to create an object. A rectangle can
be characterized by a corner point and the dimension, the width and the height. I can
write the code like that where I create the corner first and I create the dimension, and
then I go on and create the rectangle that takes the corner position and the dimension
as the parameters.
But then there's also another constructor in that rectangle that accepts four numbers,
as opposed to one object that represents a corner and one object that represents dimensions.
Apparently in that bad API example, if you create the rectangle by passing four numbers,
it has to be the top left position and the bottom right position. It's easy to fix if
you've seen this code because all you need to do is to change that particular constructor
to accept also four numbers that more or less represent the corner position and the width
and height as the dimension. Again, that's the difference.
On the left side it's x1, y1, x2, y2, which doesn't really match with the corner and dimension
arguments in the first type of constructor. Whereas on the right side you have XY and
width and height that loosely match as if you will have written the construction by
using corner and dimension.
This is the perfect moment to inject my complained about JavaScript. You can have a variable
that contains NaN, not a number. Oh, I want to check whether this X is NaN or not so you
can use isNaN which gives you true. But apparently if you compare X to NaN it gives you false.
So this is some stuff that really, really confusing in JavaScript. This is why I have
an annual tweet about this. NaN stands for Not a NaN.
[laughter]
This is an example of a bad API where you will expect that it's NaN and comparing X
with NaN will return the same value, but they don't.
The world is fantastic. You have double rainbows, no inconsistency. But as programmers we are
lazy so we tend to write shortcuts written to make ourselves feel convenient by avoiding
the need to write a lot of lines of code. Therefore we invent shortcuts.
If you ever play Romeo and Juliet, what's in a name? That which we call a rose by any
other name would smell as sweet. It's obvious that Romeo and Juliet neither one of them
was a computer programmer, because name plays a huge role, plays an important role there.
If you ever have the joy of working with Symbian S60, what Android and iOS developers refer
to as webview, it was called BrCtl, which stands for Browser Control. I don't know the
limitation of the number of characters that you can use, but not having any vowels at
all there makes it really difficult. If you communicate to your fellow programmers, hey,
I just created a new example using BrCtl, it doesn't make any sense. Names are very
important.
The biggest offences probably with respect to shortcut is DOM event. Yeah, this is really
crazy examples. Anyone can tell me what this true, true, no, no, false, false, false, false,
9, 0 there means?
[laughter]
This is a case where you want to pack as much information in the constructor so you don't
need to set the properties or call function at a later stage. You want to put everything
in one line. But there's no way for any normal mortals to understand what this code means
as soon as you don't have your DOM API documentation next to you. If you try to review some code
like this, well good luck.
But there are also other types of shortcuts which is popular and is called Boolean traps.
Say I want to create a slider. There's two possible appearances of sliders, horizontal
and vertical. Anyone ever seen a diagonal slider?
If you have two possible choices then usually the logical way to implement this is by using
Boolean. You choose true for probably horizontal and you choose false for vertical. You might
end up having this kind of code. s1=new Slider (true). If you don't put the comment line
there, there's no way for someone else later on to figure out that true there means horizontal.
Same thing with vertical.
Now it doesn't only apply to constructor. It could be any other functions. I know that
YUI adopted config objects, so this can be solved easily by having a special non-Boolean
object that sort of gives meaning to the particular slider.
If you have a function that accepts one Boolean, be careful. Be suspicious. Think about how
people are going to use that function.
Another bad case of Boolean is if you want to modify the behavior of a particular function.
I have a rectangle here, or widget, or component, anything that is rectangular, and I want to
resize it. Because my application needs to be very cool I want to animate while that
particular component resizes. Then I came up with an idea, oh, let's just put additional
Boolean values there to indicate whether I should animate that resize or not. You end
up having this code. Resize, some numbers, and then true.
Without looking up the API doc, who will imagine that true here means please animate this.
Even more confusing if you put false because does that mean we should not resize the size?
It's totally confusing.
Of course the easiest way to solve this, again, to pass a rich object there, or probably create
a completely different function that indicates that you also want to animate as the user
resizes particular components.
Another typical code smell, if you see some particular code that has Boolean as the last
parameters, whether 1 or 2 or 3, you need to think twice and see what the code is doing.
If you create an API that promotes that kind of behavior you might want to introduce different
function or different countermeasures to the particular behavior.
If one is fantastic, how about two? Imagine if you have this tree and the tree item can
collapse or expand. You also want to animate that because your application is really cool.
This is like having an object with two personalities. treeItem.setState(true, true), so that means
you expand it and you animate it. That's still probably not so much confusing compared to
the next line where it says treeItem.setStat( true, false). If someone reads that code and
doesn't understand the meaning of those Booleans, it's like do we set the state to true and
then change it to false or whatever? The next one is as confusing as the second one as well.
If you have a function that accepts only Boolean parameters, think again. It might be a huge
code smell.
Forgive my lack of modesty for a while, but when John Carmack learned about this Boolean
trap, my friend suggested to me that you should not do anything else because it's going downhill
from there. Even the legendary John Carmack nearly falls into the trap of Boolean traps,
so what chance do we have?
The last thing is about confusion, ambiguities, etcetera. Now that we don't have any more
Boolean traps, every name follows the principle of static polymorphisms, does that mean that
our code is not confusing anymore? Apparently not.
Here I'm going to show you some examples.
Double negatives. This is one of my favorites. For a non-native speaker like myself, it's
always a problem. When I moved to the US four years ago I never lived in an English speaking
country before. Therefore as a non-native speaker you tend to think differently than
a native speaker. For example if Tilo asked me hey, Ariya, would you might if I borrow
my pen? My answer would be yes. Yes you can borrow my pen. I don't mean yes, I do mind
that you borrow my pen, I answer to the spirit of the request, not the particular grammatical
semantics of that request. Because I don't think that way. My mind doesn't think that
way.
That means in some cases you might want to consult your fellow non-native speaker to
see if your API makes sense. Like X.disabled false. We can just write X.enabled true. Or
Y.setHidden(true), you just put setVisible(false). Or caseInsensitive = true. This is even confusing.
Does that mean capital A equals lower case A if caseInsensitive = true?
It's even more confusing if every component, every object adopts different behaviors. Some
has enabled, some has disabled, so if you want to control the state of this by this
property then you have to put a Boolean node up in front of that. That's just really, really
messy. So be careful about double negatives.
We usually talk about the existence of some things, not the lack of existence. 20 per
cent translucency is a bit difficult to understand compared to 80 per cent opaque. If you're
a big fan of Starbucks or Peets try to do AB testing to your barrista. Go there and
then say my cup is three quarters empty, or my cup is one quarter full, can you refill
it. I want to see which one is easier to understand. Usually we understand the concept of we have
heat not we don't have lack of coal.
It's very important to choose particular words that give the existence of things.
Nonverbal actions. This is like the passive aggressiveness world of API design. Again,
non-native speakers usually have problems with this. If I see status.message ready it
doesn't tell a lot about what does this function do. Can it be more explicit? Or in typical
navigation API, forward and backward.
If you think these are not explicit then you can change it so it indicates some sort of
action is happening there. goForward and goBackward, they are much more explicit then just forward
or backward. Same with showMessage, because then you can have hideMessage for example.
So there are APIs where if you pass nothing to message then it hides the status. This
is all confusing. But if you have status.showMessage and status.hideMessage then it's easier to
follow. It causes less confusion.
Conversational style that is adopted into APIs is also really bad. If you have a calendar
picker and then you choose the property names as yearFrom and yearTo, it kind of makes sense
when you discuss this with your fellow programmers. But what you should really choose as the property
names is something that makes sense. You have to be able to say the minimum year of this
particular calendar picker is this number, or the maximum year and this number.
A little bit more different is like this. If you try to check in you can say hey, my
name is Adam, my flight is from San Francisco to New York. You can write this kind of code.
this.callMe Adam, and flight.from and flight.to. But according to the previous example that
I've shown, it will be easier if you can read it aloud. You may need to change the choice
of your property names there. It should indicate that the name of this person is Adam and anything
that involves source and target departure or destination airports and so on. Just because
you can converse in that style doesn't mean that you have to write it that way.
String extraction. This is really, really crazy. JavaScript has this function called
substr, and you pass two numbers. If you pass 3 and 8 you get this string. Conf2013 because
you start from the third position which means fourth because the index starts from zero,
and you get 8 characters. But if we flip the arguments then obviously you start from the
eighth position and you get 3 characters. So far so good.
But apparently there's another function called substring. Substring works differently. You
still need to pass the numbers, but the first number indicates the beginning and the second
indicates the end. If you pass 3 and 8 to substring you'll get conf2 instead of conf13
because you stopped at the eighth position which means you have only 5 characters there.
If you flip the order then substring is smart, it automatically swaps it. Substring(8, 3)
is exactly like substring(3, 8). You still get conf2. This is getting better.
There is a function called slice. Slice, if you pass 3 and 8, works exactly like substring.
But if you pass 8 and 3, apparently it's not that smart. You try to slice something from
the position that is behind the beginning so it returns an empty string.
If you have your documentation handy, it's easier to realize what's wrong with this.
If you Google for this there's tons of Stack Overflow questions just explaining this question.
Slice is probably not that dangerous because it has a complete different meaning than substr
and substring, but substr and substring it's always confusing which one should I use, because
they accept the same amount of arguments, they behave almost the same, getting portion
of strings. But the outcome can be day and night.
If you try to invent an additional function that has almost the same name, be very careful.
Apparently string is not as worse as array. If I have an array it has three elements and
I do a slice on that , my original array remains the same. But I get the new array from the
second line of code, which means slice from the position 1 to 2, which means I get only
one element. This is straightforward, easy to understand.
But apparently there's another function called splice. Just one character difference. The
impact is huge. First of all, splice modifies your array. If I call a.splice(1, 2), a changes.
It mutates my original array. What I get from splice, which is b, is also completely different
from what I get from slice.
This is the problem of immutable versus mutable. If you just look at the name of the function
you will not know whether hey if I call this function am I safe? Am I going to be changed
by this function? Because nothing there tells you about that. And of course the fact that
it returns the different outcome although they may have the same name refers to the
original problem of string functions.
I don't know about you but I have fixed bugs just because of this and because of the confusion
between substring and substr.
If you have a function that is a verb, it makes sense to make sure that it implies some
actions. If I have a point and I call translate, you would expect that that point moves because
I apply that translation there. But if you try to infer that behavior to different things,
like trim, what do you think is going to happen to s?
This particular JavaScript trim comes from Java implementation of trim which works on
immutable strings. Nothing will change in s. It returns a new string that has all the
space of string. If I read that code, p.translate, it modifies p and I write the code. s.string,
well apparently it doesn't modify s.
One way to solve this is by having an explicit immutability feature or mutability if you
may say so. For example, p.translate means because it's a verb implies an action on that
object, but if I want to return a new object that is p but translated then I have to call
it p.translated. Obviously I need to assign this to different variables.
If we have that luxury then we can work on trim, like trim and trimmed, because the second
one returns a fresh new string that's exactly as but with all the spaces removed.
Now that you've seen static polymorphism, then just shortcuts and then consistency or
confusion because of semantics, let's see some of the best practices. I'm hesitant to
call these best practices just because there are so many best practices out there. But
more like what kind of processes and frameworks that you can put in place just to make sure
that you don't have this hall of shame of bad API.
First is you have to make everything private by default. Never, ever, ever expose anything,
because once it's public you cannot take it back.
There is a nice story about this particular Windows API function, shell function called
StripMneumonic because if you can see there's a typo there. There's a whole story behind
this. The idea is that, or the short version of the story is that this was internal function
but it gets used by a lot of teams in Microsoft. Therefore the order came that this function
needs to be public and that escapes a final review. Also because once a function that
has a wrong name is being used by one hundred applications, you need to change all those
hundreds. It's a mess.
So when you change, oh I'll just write a simple function, nobody's going to use it, also please
be careful with the naming.
It's like the toothpaste. If it's out of the tube then good luck trying to put it back.
And you can only make it public by some sort of justification. A good guide that a lot
of people practice is that if you create a function then write examples that use that
function. Don't just invent a particular property just because you think someone will use it.
Maybe nobody's going to use it. Maybe you think highly of yourself.
But if you write the function then you get to see all the codes, all the bad examples
that I've seen before you can catch it immediately as soon as you write examples that use your
particular API. If you create the function then make sure you come up with examples.
Maybe you can force it even in a pull request. Hey, you created a new public function, where
are those examples. Otherwise reject it.
Mandatory API review is also very useful. Before every release, before you create even
preview release, it's very nice to just print out all the names of new functions or that
are introduced or new properties or new objects, and then have a lot of eyes check that and
see that, review that and get the feeling whether that's good or not.
In the worst case if you need the functionality but you're not sure about the naming then
you can still make it private.
The third time's the charm, because I have this theory that you're going to fail your
first two attempts anyway, so make sure you write really quickly and therefore in the
hope that the third time will be fantastic.
You probably might have seen this in a marketing twist of any organization because they will
launch the first version as super revolutionary, and then the second one as super fast, it's
10 times faster than version one, which is a way to say we screwed up. Then the third
time is designed from the ground up to be better than the previous first two versions.
Again, another way to say the engineering team screwed up again.
If you have this API review then you can have your Gandalf moment. You want to make sure
that no bad API will escape to the public.
The last best practice is API tools because tools separate us from this Neanderthal JavaScript
programmer. He didn't use tools and therefore he is gone.
The basic idea of tools is to get the semantic understanding of your code. Usually you can
do this by having a parser. If you have a build parser the first state just tokenization
splits your code into multiple tokens. In this example if our answer equals 42 I need
to identify which one is the keyword, which one is an identifier, punctuators, such as
equal signs, numbers, etcetera. From there we can create the syntax tree that more or
less represents the syntactical structure of your particular code.
There is a nice standard called SpiderMonkey Parser API. Again, same examples. You can
get this object from JSON that is the textual representation in a tree of the diagram that
I have shown in the previous slide. You can play with this visualized tree version. Just
go to that URL and pass your code and then you're going to see the syntax tree as a tree.
Now if you already have this then you can do a lot of analysis on the syntax tree level.
For example, you can get the meta information or metadata or meta-object about your particular
module. This is just a hypothetical framework. If this is the way I create a module then
you can parse the code on the left side, traverse the syntax tree and then extract all the property
definition functions belonging to this object, the object name, etcetera.
One of the particular uses of this is that you can, for example, blacklist all those
double negatives if they show up in the property names. Or even you release new version, you
have to make sure that no function got deleted accidentally. So you compare the meta object
of both versions. This is very important for backwards compatibility, or even forward compatibility.
If we talk about blacklisting, in particular a specific set of property names, if I have
a code foo.bar and you parse it and you get the syntax tree, some portion of the syntax
tree might look like this. There's a MemberExpression. This is the terminology used by the ECMAScript
standard as well. The object for that MemberExpression is foo and the property name is bar. You could
totally write some code that traversed the syntax tree and will figure out if that node
is a MemberExpression or not. If that's the case then you can print out the property name.
If you do this on your whole program, it's going to print out all the name of the property.
Then you can do grab or whatever to analyze whether it's blacklisted or not. You can collect
all those expressions.
You can also detect Boolean traps that I mentioned before. This is reload(x, y, false). If you
look at the syntax tree produced by that code it will have the callee, which is reload,
and then it has three arguments. First argument is just a normal identifier, x, y, and the
last one is the one that we're concerned with, which is a Boolean literal. You can have a
function that checks the node, sees if that node has an arguments property, and if the
few last ones are Boolean literals. You can do this to one argument function, two argument
functions, etcetera.
There's also a way to blacklist function names, just like those double negatives. setDisabled(false)
will generate this kind of syntax tree. You have the callee which is setDisabled, and
we don't really care about the arguments. The code to do this is left as an exercise
for you.
What about API usage? We talk about the design of API but if the API is already being used
by tons of applications and libraries, how do we detect that? I'm not really aware of
some tools that allow you to do that right now, so this falls into the category next
generation for our future.
For example, it would be lovely to have these two warnings. You call this immutable function
but you never sign its written value. This is an example where I trim string but I didn't
do anything with the outcome of that trimming action. Or translated something. But that
requires knowing that if you have this function name that is immutable then you have to assign
it to somewhere. You need to understand the convention behind that.
There is another tool in the C++ world that can do the second thing, which is you check
the return values of this function a gazillion times but one time you forgot it. Was that
intentional or was it an honest mistake? It would be lovely if we can have that kind of
feature.
To recap, make sure you apply static polymorphism. If you invent shortcuts for your convenience,
be careful, it may be dangerous. Judge every single possible shortcuts. And read your code,
because that makes a huge difference with respect to confusion and its semantics.
If you do a code review with respect to API, for example you want to detect all Boolean
traps, you might want to collect all those kind of API tools or API detection problems
in your CI system. For example someone creates a pull request then when that job gets delegated
to the Travis CI it will tell you if this particular patch suddenly creates a function
that has Boolean traps. Therefore before a human reviews that code there's already a
warning that that might not be the best thing to do.
Thank you very much.
[applause]
Andrew: We do have some time for some questions. Do we have any questions from the audience
for Ariya?
Audience member: Testing, testing. There we go. Emily, you're on.
Emily: Regarding the double negatives type thing, you mentioned disabled as being something
that's kind of frowned upon in your view. Do you have any suggestions for if you are
working with a very large code base and you find the sudden need that you need to be able
to disable an action but it's used everywhere and you can't realistically add an enabled
flag everywhere, so instead you decide to say disabled true. Therefore at a high level
when you say are checking for whether or not this thing is disabled, you can just check
to see is disabled true.
Ariya: Yeah, you mean a flag that applies to the entire thing so you don't need to disable
each individual components. I don't think I have an answer for that. I think it may
require completely different properties that signify the nature of everything that's being
disabled. It could be a function name so you need to call it potentially.
As for the Boolean check, personally I still prefer isEnabled, just because it doesn't
have a double negative. I think it boils down to preference. If in some scenarios the code
makes sense then it's fine, but what would you want to avoid at all costs is to have
a mixture of both, inconsistency between the use of negative and double negative.
Audience member: Thanks, it was a good talk. Good points. There was a discussion on Rebecca
Murphey's blog some months ago. Maybe a year or more ago. It was about the distinction...
At what point do you have too many parameters in your function? I was wondering if you have
an opinion on that, taking individual parameters versus taking an object with property names.
Is there a certain breaking point that you feel is a good point?
Ariya: I don't have an opinion. Am I live? Yes. The number of parameters before it becomes
too long, I don't have any specific threshold for that. I think it depends on the team.
If you have experienced programmers, if you still pass four or five arguments, it may
still survive. It may not.
Audience member: Sorry I'm going to cut you off there too.
Ariya: Sure.
Audience member: Then the other question I had was that you didn't mention anything about
optional parameters. Do you have opinions about optional parameters, where they should
be? Should they be there, should they not? That sort of thing.
Ariya: Optional parameters in my opinion is not that bad, meaning that it may not cause
crazy disasters like some of the examples that I've shown before. I would totally leave
it up to the team's decision as to whether you want to use that practice or not.
Audience member: But let's say you had a function that had four parameters, three of which were
optional. Does that mean you pass in a value for the first one... Well, so the first parameter
is always required as opposed to one of the four being required.
And then do you lock down the signature to say you pass in the optional one in the index
that it must be placed in? Or do you scan the signature to see if something was passed
in the first two then it means this, and if something was passed in the first three then
it means this other thing.
Ariya: This is kind of solved with ECMAScript 6 because you can have syntax that tells that
argument to be optional. Then you can build static analysis tools that looks for that
particular usage and see whether you call it or not. But whether I want to lockdown
or not... I've used both. I also abuse optional arguments in some cases. It doesn't cause
a lot of damage so that's fine. I think it needs to be on a case by case basis.
Audience member: I know your talk was primarily to do with the public API. In YUI generally
every method is public. We just denote privacy with an underscore prefix. But I've been guilty
of doing a couple of these things for some of these private methods because I feel like
I'm the only one who'll be using them. Especially having a lot of arguments or having optional...
Is that something I shouldn't be doing? Do you think I should take... At what point do
I use these concepts? Is it just for the public API, or even if I'm making these internal
methods which I'm calling and I don't expect anyone else to call. Should I be this rigid
in that sort of set up as well?
Ariya: It's more like an undocumented function. It exists, but you should not use it. At least
the public should not use it because we may change it. It would be fantastic if you apply
all kinds of good API practices to that, but the worst case is still not supposed to be
used by the world. It's not the end of the world.
However, at least collect all those new APIs during the API review and then prioritize
the one that needs to be public. If you have resource time etcetera then include the view
of all those functions marked with underscore. Other than that, if you tell the customers
or the users we may change this, we mean it, then it's not really your fault if you have
to change that.
Andrew: Do we have additional questions? Any more questions? Okay, go ahead.
Audience member: Am I on? Alright. Do you make a distinction between public but documented
to be private versus true private? Because in my own experience true privates are a pain
in the neck when it comes time to debug or hotswap or anything like that. If you need
to do any sort of customization then true private's really difficult to work with, or
test actually.
Ariya: In an ideal world, yes, we should leave it true private, but we also have other priorities
so I can... Personally I can totally understand if you set aside this is something that we
will fix in the near future and therefore doesn't fall into the analysis of good API.
I think as long as you create something that exposes modules and objects, whatever, then
at the very least you want to make sure that the usage is not confusing. Yes it may have
Boolean traps, yes it may have other kinds of possible shortcuts that you have, but the
disaster like slice and splice, that should be in your radar. That's the thing that may
cause bugs as opposed to it just looks ugly.
Audience member: Just as a side note, substr, substring and slice also deal with negative
indexes differently, which is bonus fun.
Audience member: Okay, so great talk. We had a lot of fun I think, everyone had a lot of
fun. I just have a question. What's your take on functions versus classes? For instance
you said that for instance it could be a way to solve the problem in which you have some
methods that mutate the object, some others that don't. For instance instead of using
trimmed you could say I can have a function trim that takes the string and returns a string.
Ariya: Functions versus plain vanilla function calls?
Audience member: Yeah, so function versus classes for solving this kind of problem.
Ariya: For the trim example, I think having a class is a bit of over kill.
Audience member: The string class array has it, so...
Ariya: Any similar things where you just want to apply a certain operation to the object
that you have... Having a consistent set of two different groups of functions that this
is going to be mutable and this is not, I think it's easier. Yes it's nice to have another
additional or optionally mutator object that works on your object, but I think it's too
much of an effort. I don't think that gives a lot of benefit.
Andrew: Okay, we have time for one more question. Alright, thank you so much Ariya for coming
this evening.
Ariya: Thank you.
[applause]