- enumeration type is an inductive type whose all constructors have zero arguments
- always use the formatter
- don't use unicode, except perhaps for judoc
- avoid
Pair
- prefer enumeration types with meaningful constructor names over booleans,
e.g., instead of using
Bool
define:
type Transferability :=
| Transferable
| NonTransferable;
- all imports at the beginning of the file
- explicit names for imported identifiers (
import X open using {iden1; iden2}
)- exception:
import Stdlib.Prelude open
andimport Anoma open
are allowed
- exception:
- types: upper camel case (
Nat
,PublicKey
) - functions, constructors, etc: lower camel case (
tokenLogic
,transferLogic
)- exception: constructors of enumeration types: upper camel case
type BalanceFactor :=
| --- Left child is higher.
LeanLeft
| --- Equal heights of children.
LeanNone
| --- Right child is higher.
LeanRight;
type Ordering :=
| LessThan
| Equal
| GreaterThan;
- instances: lower camel case with
I
at the end: trait + type +I
, e.g.,instance eqNatI : Eq Nat := ..
- boolean check functions: start with
is
(isWhatever
) - record constructors:
mk
+ type name (mkResource
) - meaningful descriptive long names for arguments of public functions, e.g.,
element
,list
,initialValue
- exception: common abbreviations allowed:
fun
,acc
,elem
- exception: generic functions whose arguments have no specific meaning, e.g.,
- exception: common abbreviations allowed:
id {A} (x : A) : A := x
- short names like
x
are okay for local definitions
- put everything to the left of
:
in type signatures, with full meaningful names for arguments- temporary exception: when needed for termination, argument can be moved to the right of
:
- even when the argument type is moved to the right, give a descriptive argument name in the type:
- temporary exception: when needed for termination, argument can be moved to the right of
isMember {A} (testEq : A -> A -> Bool) (element : A) : (list : List A) -> Bool
- type variables uppercase (upper camel case, like with types)
- higher-order type variables also upper camel case (
F : Type -> Type
) - implicit type variables: use short version
{A}
in function signatures on the left - prefer
{A B}
over{A} {B}
in function signatures - meaningful type variable names for more "high-level" functions where this makes sense
Example:
split
{ResourceType}
{{Convertable ResourceType}}
{{HasOwner ResourceType}}
{{HasQuantity ResourceType}}
{{HasTransferability ResourceType}}
{{TransactionConvertable ResourceType ResourceType}}
(self : KeyPair)
(toSplit : ResourceType)
(quantitiesAndReceivers : List (Pair Nat PublicKey))
: Result StandardError Transaction :=
...
But we can use a short name A
in:
find {A} (predicate : A -> Bool) : (list : List A) -> Maybe A
- use
case
whenever possible; matching on multiple values handled with pairs - don't use pattern matching with side conditions when there is an
else
side condition -- convert to anif
in the body instead - don't pattern match on
Bool
-- useif
instead
- don't use "pipes" (i.e.,
|>
,<|
,>>
,<<
, etc.)- exception: there is a "natural" data processing pipeline in several steps, e.g.,
map (+ 1) >> filter (< 0) >> sum
- use iterators
for
,map
, etc., instead of the function application syntax withfold
, etc. - use named application when reasonable
- use ADT syntax
- every constructor on a separate line
- give meaningful names to constructor arguments
Example:
type BinaryTree (A : Type) :=
| leaf
| node@{
left : BinaryTree A;
element : A;
right : BinaryTree A
};
- the documentation string should be an English sentence
- mention the meaningful argument names in the function's judoc