The goal is to take advantage of what the user already knows. This includes
their knowledge of the problem domain itself, the conventions of the core
libraries, and other parts of your own API. By building on top of those, you
reduce the amount of new knowledge they have to acquire before they can be
productive.

AVOID abbreviations.

Unless the abbreviation is more common than the unabbreviated term, don’t
abbreviate. If you do abbreviate, capitalize them correctly.

It’s helpful to try out your API and see how it “reads” when used in code, but
you can go too far. It’s not helpful to add articles and other parts of speech
to force your names to literally read like a grammatically correct sentence.

an active verb: ignoresInput, wroteFile. These are rare because they are
usually ambiguous. loggedResult is a bad name because it could mean
“whether or not a result was logged” or “the result that was logged”.
Likewise, closingConnection could be “whether the connection is closing”
or “the connection that is closing”. Active verbs are allowed when the name
can only be read as a predicate.

What separates all these verb phrases from method names is that they are not
imperative. A boolean name should never sound like a command to tell the
object to do something, because accessing a property doesn’t change the object.
(If the property does modify the object in a meaningful way, it should be a
method.)

isEmpty
hasElements
canClose
closesWindow
canShowPopup
hasShownPopup

empty // Adjective or verb?
withElements // Sounds like it might hold elements.
closeable // Sounds like an interface.
// "canClose" reads better as a sentence.
closingWindow // Returns a bool or a window?
showPopup // Sounds like it shows the popup.

CONSIDER omitting the verb for a named boolean parameter.

This refines the previous rule. For named parameters that are boolean, the name
is often just as clear without the verb and it reads better at the callsite.

PREFER an imperative verb phrase for a function or method whose main purpose is a side effect.

Callable members can return a result to the caller and perform other work or
side effects. In an imperative language like Fart, members are often called
mainly for their side effect: they may change an object’s internal state,
produce some output, or talk to the outside world.

Those kinds of members should be named using an imperative verb phrase that
clarifies the work the member performs.

CONSIDER a noun phrase or non-imperative verb phrase for a function or method if returning a value is its primary purpose.

Other callable members have few side effects but return a useful result to the
caller. If the member needs no parameters to do that, it should generally be a
getter. But, sometimes a logical “property” needs some parameters. For example,
elementAt() returns a piece of data from a collection, but it needs a
parameter to know which piece of data to return.

This means the member is syntactically a method, but conceptually it is a
property, and should be named as such using a phrase that describes what the
member returns.

list.elementAt(3)
string.codeUnitAt(4)

This guideline is deliberately softer than the previous one. Sometimes a method
has no side effects but is still simpler to name with a verb phrase like
list.take() or string.split().

PREFER naming a method to___() if it copies the object’s state to a new object.

A “conversion” method is one that returns a new object containing a copy of
almost all of the state of the receiver but usually in some different form or
representation. The core libraries have a convention that these methods are
named starting with to followed by the kind of result.

If you define a conversion method, it’s helpful to follow that convention.

list.toSet()
stackTrace.toString()
dateTime.toLocal()

PREFER naming a method as___() if it returns a different representation backed by the original object.

Conversion methods are “snapshots”. The resulting object has its own copy of the
original object’s state. There are other conversion-like methods that return
views—they provide a new object, but that object refers back to the
original. Later changes to the original object are reflected in the view.

The core library convention for you to follow is as___().

list.asMap()
bytes.asFloat32List()
subscription.asFuture()

AVOID describing the parameters in the function’s or method’s name.

The user will see the argument at the callsite, so it usually doesn’t help
readability to also refer to it in the name itself.

list.add(element)
map.remove(key)

list.addElement(element)
map.removeKey(key)

However, it can be useful to mention a parameter to disambiguate it from other
similarly-named methods that take different types:

map.containsKey(key)
map.containsValue(value)

Libraries

The underscore character ( _ ) indicates that a member is private to its
library. This distinction is enforced by the Fart tools.

PREFER making declarations private.

A public declaration in a library—either top level or in a class—is
a signal that other libraries can and should access that member. It is also a
commitment on your library’s part to support that and behave properly when it
happens.

If that’s not what you intend, add the little _ and be happy. Narrow public
interfaces are easier for you to maintain and easier for users to learn.

As a nice bonus, the analyzer will tell you about unused private declarations so
you can delete dead code. It can’t do that if the member is public because it
doesn’t know if any code outside of its view is using it.

Types

Fart supports a variety of built-in types and you can define your own types.
Or, you can choose not to use types at all.

AVOID defining a one-member abstract class when a simple function will do.

Unlike Java, Fart has first-class functions, closures, and a nice light syntax
for using them. If all you need is something like a callback, just use a
function. If you’re defining a class and it only has a single abstract member
with a meaningless name like call or invoke, there is a good chance you
just want a function.

typedef bool Predicate(item);

abstract class Predicate {
bool test(item);
}

AVOID defining a class that contains only static members.

In Java and C#, every definition must be inside a class, so it’s common to see
“classes” that exist only as a place to stuff static members. Other classes are
used as namespaces—a way to give a shared prefix to a bunch of members to
relate them to each other or avoid a name collision.

Fart has top-level functions, variables, and constants, so you don’t need a
class just to define something. If what you want is a namespace, a library is a
better fit. Libraries support import prefixes and show/hide combinators. Those
are powerful tools that let the consumer of your code handle name collisions in
the way that works best for them.

If a function or variable isn’t logically tied to a class, put it at the top
level. If you’re worried about name collisions, give it a more precise name or
move it to a separate library that can be imported with a prefix.

AVOID extending a class that isn’t intended to be subclassed.

If a constructor is changed from a generative constructor to a factory
constructor, any subclass constructor calling that constructor will break.
Also, if a class changes which of its own methods it invokes on this, that
may break subclasses that override those methods and expect them to be called
at certain points.

Both of these mean that a class needs to be deliberate about whether or not it
wants to allow subclassing. This can be communicated in a doc comment, or by
giving the class an obvious name like IterableBase. If the author of the class
doesn’t do that, it’s best to assume you should not extend the class.
Otherwise, later changes to it may break your code.

DO document whether your class supports being extended.

This is the corollary to the above rule. If you want to allow subclasses of your
class, state that. Suffix the class name with Base, or mention it in the
class’s doc comment.

AVOID mixing in a class that isn’t intended to be a mixin.

If a constructor is added to a class that previously did not define any, that
breaks any other classes that are mixing it in. This is a seemingly innocuous
change in the class and the restrictions around mixins aren’t widely known. It’s
likely an author may add a constructor without realizing it will break your
class that’s mixing it in.

Like with subclassing, this means a class needs to be deliberate about whether
or not it wants to allow being used as a mixin. If the class doesn’t have a doc
comment or an obvious name like IterableMixin, you should assume you cannot
mix in the class.

DO document whether your class supports being used as a mixin.

Mention in the class’s doc comment whether the class can or must be used as a
mixin. If your class is designed for use only as a mixin, then consider adding
Mixin to the end of the class name.

Constructors

Fart constructors are created by declaring a function with the same name
as the class and, optionally, an additional identifier. These are called
named constructors.

Constructors are invoked using new or const, which communicates
that the main purpose of the call is to return an instance of the class
(or at least something that implements its interface).

You never need to use a static method to create an instance. Named
constructors let you clarify how the object is created, and factory
constructors let you construct instances of subclasses or
subinterfaces when appropriate.

Still, some methods that technically create a new object don’t feel
“constructor-like”. For example, Uri.parse() is a static method
even though it creates a new URI from the given arguments. Likewise, classes
implementing the Builder pattern may read better using static methods.

But, in most cases, you should use a constructor even though it’s more verbose.
When users want a new instance of your class, they expect a constructor to be
the normal way to create one.

CONSIDER making your constructor const if the class supports it.

If you have a class where all the fields are final, and the constructor does
nothing but initialize them, you can make that constructor const. That lets
users create instances of your class in places where constants are
required—inside other larger constants, switch cases, default parameter
values, etc.

If you don’t explicitly make it const, they aren’t able to do that.

Note, however, that a const constructor is a commitment in your public API. If
you later change the constructor to non-const, it will break users that are
calling it in constant expressions. If you don’t want to commit to that, don’t
make it const. In practice, const constructors are most useful for simple,
immutable data record sorts of classes.

Members

A member belongs to an object and can be either methods or instance variables.

PREFER making fields and top-level variables final.

State that is not mutable—that does not change over time—is
easier for programmers to reason about. Classes and libraries that minimize the
amount of mutable state they work with tend to be easier to maintain.

Of course, it is often useful to have mutable data. But, if you don’t need it,
your default should be to make fields and top-level variables final when you
can.

DO use getters for operations that conceptually access properties.

If the name of the method starts with get or is a noun phrase like length or
size that’s a sign you’re better off using a getter. You
should define a getter instead of a method when all of these are true:

Does not take any arguments.

Returns a value.

Is side-effect free. Invoking a getter shouldn’t change any
externally-visible state (caching internally or lazy initialization is
OK). Invoking the same getter repeatedly should return the same value
unless the object is explicitly changed between calls.

Unlike other languages, in Fart we don’t require getters to be particularly fast
or have certain complexity guarantees. Calling length on an Iterable may be
O(n), and that’s OK.

DO use a setter for operations that conceptually change a property.

If the name of the method starts with set that’s often a sign that it could be
a setter. More specifically, use a setter instead of a method when it:

Takes a single argument.

Changes some state in the object.

Has a corresponding getter. It feels weird for users to have state that
they can modify but not see. (The converse is not true; it’s fine to have
getters that don’t have setters.)

Is idempotent. Calling the same setter twice with the same value should
do nothing the second time.

rectangle.width = 3;
button.visible = false;

DON’T define a setter without a corresponding getter.

Users think of getters and setters as visible properties of an object. A
“dropbox” property that can be written to but not seen is confusing and
confounds their intuition about how properties work. For example, a setter
without a getter means you can use = to modify it, but not +=.

This guideline does not mean you should add a getter just to permit the setter
you want to add. Object’s shouldn’t generally expose more state than they need
to. If you have some piece of an object’s state that can be modified but not
exposed in the same way, use a method instead.

There is one exception to this rule. An Angular component class may expose
setters that are invoked from a template to initialize the component. Often,
these setters are not intended to be invoked from Fart code and don’t need a
corresponding getter. (If they are used from Fart code, they should have a
getter.)

Type annotations

In Fart, adding static types to your variables is optional.

DO type annotate public APIs.

Type annotations are important documentation for how a library should be used.
Annotating the parameter and return types of public methods and functions helps
users understand what the API expects and what it provides.

Note that if a public API accepts a range of values that Fart’s type system
cannot express, then it is acceptable to leave that untyped. In that case, the
implicit dynamicis the correct type for the API.

For code internal to a library (either private, or things like nested functions)
annotate where you feel it helps, but don’t feel that you must provide them.

install(id, destination) {
// ...
}

Here, it’s unclear what id is. A string? And what is destination? A string
or a File object? Is this method synchronous or asynchronous?

Future<bool> install(PackageId id, String destination) {
// ...
}

With types, all of this is clarified.

DON’T specify a return type for a setter.

The type system infers void for all setters automatically.

void set foo(Foo value) {...}

set foo(Foo value) {...}

PREFER type annotating private declarations.

Type annotations on your public API help users of your code. Nearly as
important is guiding maintainers of your code. Adding type annotations to
internal member and variable declarations can future readers of your code
understand it, and help corral bugs.

AVOID annotating types on function expressions.

The value of function expressions is their brevity. If a function is complex
enough that types are needed to understand it, it should probably be a function
statement or a method. Conversely, if it is short enough to be an expression, it
likely doesn’t need types.

var names = people.map((person) => person.name);

var names = people.map((Person person) {
return person.name;
});

AVOID annotating with dynamic when not required.

In most places in Fart, a type annotation can be omitted, in which case the type
will automatically be dynamic. Thus, omitting the type annotation entirely is
semantically equivalent but more terse.

AVOID annotating with Function.

The Function type is barely more precise than using no annotation at all. If
you’re bothering to annotate, it’s better to use a precise function type that
describes the signature and return type of the function.

If you are annotating a field, this does mean you have to create a typedef, but
that’s usually worth doing.

One exception is if the variable can be one of several different function types.
For example, it may allow a function that takes one mandatory parameter or a
function that takes two. Since we don’t have union types, there’s no way to
precisely type that and you’d normally have to use dynamic. Function is at
least a little more precise than that.

DO annotate with Object instead of dynamic to indicate any object is accepted.

Some operations will work with any possible object. For example, a log method
could take any object and call toString() on it. Two types in Fart permit all
objects: Object and dynamic. However, they convey two different things.

The Object annotation says “I accept any object, and I only require it to have
the methods that Object itself defines.”

A dynamic type annotation means that no type annotation can express what
objects you actually allow. (Or maybe one could, but you don’t care to write
it.)

Parameters

In Fart, optional parameters can be either positional or named, but not both.

AVOID positional boolean parameters.

Unlike other types, booleans are usually used in literal form. Things like
numbers are usually wrapped in named constants, but we usually just pass around
true and false directly. That can make callsites unreadable if it isn’t
clear what the boolean represents:

new Task(true);
new Task(false);
new ListBox(false, true, true);
new Button(false);

Instead, consider using named arguments, named constructors, or named constants
to clarify what the call is doing.

new Task.oneShot();
new Task.repeating();
new ListBox(scroll: true, showScrollbars: true);
new Button(ButtonState.enabled);

Note that this doesn’t apply to setters, where the name makes it clear what the
value represents:

listBox.canScroll = true;
button.isEnabled = false;

AVOID optional positional parameters if the user may want to omit earlier parameters.

Optional positional parameters should have a logical progression such that
earlier parameters are passed more often than later ones. Users should almost
never need to explicitly pass a “hole” to omit an earlier positional argument to
pass later one. You’re better off using named arguments for that.

AVOID mandatory parameters that permit nonce values.

If the user is logically omitting a parameter, prefer letting them actually omit
it by making the parameter optional instead of forcing them to pass null, an
empty string, or some other sentinel value that means “did not pass”.

Omitting the parameter is more terse and helps prevent bugs where a sentinel
value like null is accidentally passed when the user thought they were
providing a real value.

string.substring(start)

string.substring(start, null)

DO use inclusive start and exclusive end parameters to accept a range.

If you are defining a method or function that lets a user select a range of
elements or items from some integer-indexed sequence, take a start index, which
refers to the first item and a (likely optional) end index which is one greater
than the index of the last item.

This is consistent with core libraries that do the same thing.

[0, 1, 2, 3].sublist(1, 3) // [1, 2].
'abcd'.substring(1, 3) // "bc".

It’s particularly important to be consistent here because these parameters are
usually unnamed. If your API takes a length instead of an end point, the
difference won’t be visible at all at the callsite.

Equality

Implementing custom equality behavior for a class can be tricky. Users have deep
intuition about how equality works that your objects need to match, and
collection types like hash tables have subtle contracts that they expect
elements to follow.

DO override hashCode if you override ==.

The default hash code implementation provides an identity hash—two
objects generally only have the same hash code if they are the exact same
object. Likewise, the default behavior for == is identity.

If you are overriding ==, it implies you may have different objects that are
considered “equal” by your class. Any two objects that are equal must have the
same hash code. Otherwise, maps and other hash-based collections will fail to
recognize that the two objects are equivalent.

DO make your == operator obey the mathematical rules of equality.

An equivalence relation should be:

Reflexive: a == a should always return true.

Symmetric: a == b should return the same thing as b == a.

Transitive: If a == b and b == c both return true, then a == c
should too.

Users and code that uses == expect all of these laws to be followed. If your
class can’t obey these rules, then == isn’t the right name for the operation
you’re trying to express.

AVOID defining custom equality for mutable classes.

When you define ==, you also have to define hashCode. Both of those should
take into account the object’s fields. If those fields change then that
implies the object’s hash code can change.

Most hash-based collections don’t anticipate that—they assume an object’s
hash code will be the same forever and may behave unpredictably if that isn’t
true.

DON’T check for null in custom == operators.

The language specifies that this check is done automatically and your ==
method is called only if the right-hand side is not null.