Skip to content

ATK Code Style Guidelines

Michael McCrea edited this page Sep 14, 2019 · 18 revisions

An overview of rules adopted for the ATK library in SC3

Guidelines Overview

  • The SuperCollider Style Guidelines are followed.
  • The ATK generally prefers Receiver-verbose.
  • Except for flow control, for which Functional-verbose style is preferred.
    • Notable exceptions are do and collect, for which Receiver-verbose is also preferred.
  • Explicit expression is preferred over terse "Syntax Shortcut" code.
  • Deviations from the guidelines should be minimized, though local context may dictate that another style is decidedly more legible. For example one-line flow control.

Style Hierarchy

Styles can be broken down into two differentiating characteristics:

1) Notation

  • Receiver Notation (Generally preferred)
3.series(4, 10)
  • Functional Notation (Preferred for flow control)
series(3, 4, 10)

2) Verbosity

  • Verbose (Generally preferred)
if(k.isNumber, {		// includes parenthesis, compact, no space after 'if'
	"This is true!".postln
}, {				// comma-separated cases
	"This is false!".postln
});
  • Sparse ("modern", not preferred)
if (k.isNumber) {		// spaces separate statements
	"This is true!".postln 
} {
	"This is false!".postln
};

Syntax Guidelines

Whitespace, operators, parentheses

  • Tabs are used, not spaces. Tab length is set to 4 spaces.
  • Spaces should surround operators, including comparison operators.
    • if(a != 45, { a = a + 6 })
    • Exceptions include fractions and common quantities:
      • 1/2, pi/2, (3/2).sqrt, 3.sqrt/2, etc.
  • Spaces always follow commas.
    • [1, 3, 9]
  • Spaces are used to pad curly brackets, but not parentheses.
    • fork({ 2.wait; "ok go!".postln })
  • Use K&R style for multi-line functions:
if(v.isKindOf(Array), {
	k = 4 + 4;
	wrAttArr.(k, v)
}, { 
	k = 8 + 4;
	wrAtt.(k, v) 
});
  • A newline is place at the end of every file.

arg, var keywords

  • Use |pipes| instead of the arg keyword.
  • var keyword is always under first function line.
    • An empty line following the var declarations can increase readability.

multi-test comparisons

  • && and || are preferred over keywords and: and or:.
  • Multiple tests should be enclosed in their own parentheses.
if((test > 4) && (test2.isNil), { 
	"yes".postln
}, { 
	"no".postln
});
  • Exception: when efficiency is a concern, the comparison keywords should be used, with the second test enclosed in curly braces.
if((test1 > 4) and: { test2.isNil }, { 
	"yes".postln
}, { 
	"no".postln
});

Instance methods in the local context

Within an instance method, use the keyword this to explicitly refer to instance variables.

MyClass {
    var instVar;

    instanceMethod { |arg|
        this.instVar = arg 
    }
}

Coordinate and transform arguments

  • Use theta and phi for azimuth and elevation angles, and radius for radial arguments.
HoaEncodeDirection.ar(in, theta, phi, radius, order)
  • For a transform with a single angular argument, use angle.
HoaRotate.ar(in, angle, order)
  • For a transform with multiple angular arguments, use names indicating roles.
HoaYPR.ar(in, yaw, pitch, roll, order)
HoaNFCtrl.ar(in, encRadius, decRadius, order)

.new()

  • Object.new() is preferred to Object(). Though in areas of dense object instantiation, Object() may be preferred (e.g. UI elements).

Element Access

  • array[i] is preferred to array.at(i).

Comment spacing

  • Align nearby comments and offset from the code.
^(beamShape == nil).if({
	instance.initBasic			// (\basic, \amp)
}, {
	instance.initBeam(beamShape, nil)	// (beamShape, \amp)
})

Avoid extraneous compound operations

  • Avoid chaining too many methods together or performing calculations within the argument list of method calls. Use well-named temporary variables to carry your intermediate values along so it's clear what produces your returned values.
// Too crowded!
set { ^(this.class.asString.keep(3).toUpper ++ this.order.asString).asSymbol }

// longer but clearer
set {
	var classStr = this.class.asString.keep(3).toUpper;
	var orderStr = this.order.asString;

	^(classStr ++ orderStr).asSymbol
}

Flow control

Functional-verbose style is preferred for flow control. Flow control intervenes in the sequential execution of code—where execution might be forked, skipped, looped, etc. Some examples include if, switch, case, while, fork, defer, for, forBy, block.

Notable exceptions include do and collect, for which Receiver-verbose style is preferred.

if

// if - true only
if(k.isNumber, {
	"okok".postln;
	k = k * 35
});

// if - true and false cases
if(k.isNumber, {
	"oh yes".postln;
	k = k - 35
}, {
	"oh no".postln;
	k = nil
});

// very short
if(k.isNumber, { "okok".postln });

// very short, with else
x = if(k.isNumber, { k }, { nil });

// receiver notation (exceptional)
// enclose test in parentheses
x = (k.isNumber).if({ k }, { nil });

switch

result = switch(a,
	\one, { 1 },	// one-line function stays on same line [UND]
	\two, {	2 },
	\three, {	// multi-line function
		"picked three - functional style".postln;
		2
	},
	{ "default" }	// default case on it's own line
);

// switch - very short
x = a.switch(13, { 1 }, 24, { -1 });

case

result = case(
	{ a == \one }, { 1 },		// one-line function stays on same line
	{ a == \two }, {
		"picked two".postln;
		2
	},
	{ a == \three }, { 3 },
	{ "default" }
);

while

(
var i = 0;
while({ i < 12 }, {  // start function on same line (coincides with defer/fork above)
	i.postln;
	i = i + 1;
});
"done".postln;
)

do, collect

// multi-line
(
12.do({ |elem, i|
	var myVar;

	myVar = elem.squared;
	elem.postln;
	myVar.postln;
})
)

// short, single line
squares = 12.collect({ |elem| elem.squared });

fork, defer

// multi-line, no second arg specified
fork({
	callMyAsyncProcess();
	"giving the process some breathing room".postln;
	1.5.wait;
	okKeepMoving();
});

// multi-line, no second arg specified
// may be clearer in some cases
defer(
	{			// ... to start function on following line
		a = 45 / 23;
		"okok".postln;
		myButton.state = 2;
	}
);

// multi-line second arg specified
fork({				// start function on same line
	a = 45 / 23;
	"okok".postln;
	35
},
AppClock			// second arg on own line
);

// multi-line second arg specified
// may be clearer in some cases...
fork(
	{			// ... to start function on following line
		a = 45 / 23;
		"okok".postln;
		
	},
	AppClock
);

// a short one-liner
defer({ mySlider.value_(0.7) }, 2);

// exception: receiver notation ok on one line
{ mySlider.value_(0.7) }.defer(2);
Clone this wiki locally