└── README.md
/README.md:
--------------------------------------------------------------------------------
1 | #Functional Swift - A Style Guide
2 |
3 | ##Background
4 |
5 | Functional Swift is a proper subset of Swift and its features that encourages [immutability](http://en.wikipedia.org/wiki/Immutable_object), [recursion](http://en.wikipedia.org/wiki/Recursion#Recursion_in_computer_science), and [higher types](http://en.wikipedia.org/wiki/Kind_(type_theory)) instead of mutability, loops, and subtyping respectively. The end goal is to approach the cleanliness and readability of Declarative Programming proper while still maintaining the simplicity and semantics of Swift.
6 |
7 | *Note* This document is not a sample of best practices, nor will adhering to it necessarily produce the most efficient or practical code. It merely serves as an exploration of pure declarative programming in an imperative language.
8 |
9 | ##Restrictions
10 |
11 | For, do, and while loops are strictly forbidden. What you cannot express recursively, you should not express at all.
12 |
13 | **For Example**
14 |
15 | ```
16 | public func foldl(f: (B, A) -> B) -> B -> [A] -> B {
17 | return { z in { l in
18 | switch destruct(l) {
19 | case .Empty:
20 | return z
21 | case .Destructure(let x, let xs):
22 | return foldl(f)(f(z, x))(xs)
23 | }
24 | } }
25 | }
26 | ```
27 |
28 | Assignment is restricted to `let` bindings and [monadic extraction](http://book.realworldhaskell.org/read/monads.html) (prefix `!`, reappropriated from [strictness bangs](https://www.haskell.org/haskellwiki/Performance/Strictness)) in `do_` blocks.
29 |
30 | **For Example**
31 |
32 | ```
33 | public func finally(a : IO)(then : IO) -> IO {
34 | return mask({ (let restore : IO -> IO) -> IO in
35 | return do_ { () -> A in
36 | let r = !onException(restore(a))(what: then)
37 | let b = !then
38 | return r
39 | }
40 | })
41 | }
42 | ```
43 |
44 | All classes should derive a [kind class](https://github.com/typelift/Basis/blob/master/Basis/Kinds.swift) (`K0-K10`), then be declared `final` so as to prevent further subtyping.
45 |
46 | **For Example**
47 |
48 | ```
49 | public final class Either : K2 {
50 | ```
51 |
52 |
53 | All higher-kinded classes that can, should provide a destructuring function named `destruct()` that returns an enum so as to facilitate switch-case usage.
54 |
55 | **For Example**
56 |
57 | ```
58 | public enum EitherD {
59 | case Left(Box)
60 | case Right(Box)
61 | }
62 |
63 | //...
64 |
65 | public func destruct() -> EitherD {
66 | if lVal != nil {
67 | return .Left(Box(lVal!))
68 | }
69 | return .Right(Box(rVal!))
70 | }
71 | ```
72 |
73 | Leave the interface inside classes as minimal as possible. Prefer functions that take that class as their first parameter (CoreFoundation style) to encourage composition.
74 |
75 | **For Example**
76 |
77 | ```
78 | public class Chan : K1 {
79 | init(read : MVar>>, write: MVar>>) {}
80 | }
81 |
82 | public func newChan() -> IO> {}
83 | public func writeChan(c : Chan)(x : A) -> IO<()> {}
84 | public func readChan(c : Chan) -> IO {}
85 | public func dupChan(c : Chan) -> IO> {}
86 |
87 | ```
88 |
89 | Never return the same object from a function without performing some kind of evaluation (notable exception: `id`).
90 |
91 | ##Whitespace
92 |
93 | Swift code shall use tabs. The only exception is diagrams in comments, which must use spaces to guarantee correct formatting. Each opening brace introduces another tab-level of indentation. Each closing brace removes one tab-level of indentation. Braces appear on the same line as the statements they accompany.
94 |
95 | **For Example**
96 |
97 | ```
98 | /// Functors map the functions and objects in one set to a different set of functions and objects.
99 | /// This is represented by any structure parametrized by some set A, and a function `fmap` that
100 | /// takes objects in A to objects in B and wraps the result back in a functor. `fmap` respects the
101 | /// following commutative diagram:
102 | ///
103 | /// F(A)
104 | /// •
105 | /// / \
106 | /// / \
107 | /// / \
108 | /// fmap f / \ fmap (f • g)
109 | /// / \
110 | /// •-----------•
111 | /// F(B) F(C)
112 | /// fmap g
113 | ///
114 | /// Formally, a Functor is a mapping between Categories, but we have to restrict ourselves to the
115 | /// Category of Swift Types (S), so in practice a Functor is just an Endofunctor. Functors are
116 | /// often described as "Things that can be mapped over".
117 | public protocol Functor {
118 | /// Type of Source Objects
119 | typealias A
120 | /// Type of Target Objects
121 | typealias B
122 |
123 | /// Type of our Functor
124 | typealias FA = K1
125 |
126 | /// Type of a target Functor
127 | typealias FB = K1
128 |
129 | /// Map "inside" a Functor.
130 | ///
131 | /// F on our diagram.
132 | class func fmap(A -> B) -> FA -> FB
133 | func <%>(A -> B, FA) -> FB
134 |
135 | /// Constant Replace | Replaces all values in the target Functor with a singular constant value
136 | /// from the source Functor.
137 | ///
138 | /// Default definition:
139 | /// `curry(<%>) • const`
140 | func <%(A, FB) -> FA
141 | }
142 | ```
143 |
144 | Longer array and dictionary literals may appear on multiple lines. If statements should have a single space between the if and the case, and the case and the opening brace. Else statements appear on the same line as the closing brace of the if statement they accompany.
145 |
146 | **For Example**
147 |
148 | ```
149 | public func destruct(l : [T]) -> ArrayD {
150 | if l.count == 0 {
151 | return .Empty
152 | } else if l.count == 1 {
153 | return .Destructure(l[0], [])
154 | }
155 | let hd = l[0]
156 | let tl = Array(l[1..(k: A -> B -> B) -> B -> [A] -> B {
171 | return { z in { l in
172 | switch destruct(l) {
173 | case .Empty:
174 | return z
175 | case .Destructure(let x, let xs):
176 | return k(x)(foldr(k)(z)(xs))
177 | }
178 | } }
179 | }
180 | ```
181 |
182 | Operators should be accompanied by functions that are fully curried and perform the same operations. Operators are, in general, the infix form of functions. They are not to be defined on a whim. Operators should be easily reachable on a Mac's keyboard.
183 |
184 | **For Example**
185 |
186 | ```
187 | /// A type-restricted version of const. In cases of typing ambiguity, using this function forces
188 | /// its first argument to resolve to the type of the second argument.
189 | public func asTypeOf(x : A) -> A -> A {
190 | return const(x)
191 | }
192 |
193 | /// "As Type Of" | A type-restricted version of const. In cases of typing ambiguity, using this
194 | /// function forces its first argument to resolve to the type of the second argument.
195 | ///
196 | /// Composed because it is the face one makes when having to tell the typechecker how to do its job.
197 | public func >=<(x : A, y : A) -> A {
198 | return asTypeOf(x)(y)
199 | }
200 | ```
201 |
202 | ##Data Types
203 |
204 | Data types should be classes that derive a kind class, then should be final. Recursive datatypes may require `Box`s or `@autoclosure`s. A datatype's members are always private, and let-bound whenever possible. Common operators that act on these private variables should remain in the same file as the data type.
205 |
206 | **For Example**
207 |
208 | ```
209 | public final class Version : K0 {
210 | public let versionBranch : [Int]
211 | public let versionTags : [String]
212 |
213 | public init(_ versionBranch : [Int], _ versionTags : [String]) {
214 | self.versionBranch = versionBranch
215 | self.versionTags = versionTags
216 | }
217 | }
218 | ```
219 |
220 | ##Protocols
221 |
222 | Protocols are weakened typeclasses. Provide all functions that return higher-kinded types with the appropriate typealiases to properly restrict implementations. Default implementations are not possible, but whenever it is appropriate, provide a comment showing the default implementation, or provide a method that returns the default definition for that function. Infix operators should always call their normal form counterparts, not the other way around.
223 |
224 | **For Example**
225 |
226 | ```
227 | public protocol Functor {
228 | /// Type of Source Objects
229 | typealias A
230 | /// Type of Target Objects
231 | typealias B
232 |
233 | /// Type of our Functor
234 | typealias FA = K1
235 |
236 | /// Type of a target Functor
237 | typealias FB = K1
238 |
239 | /// Map "inside" a Functor.
240 | ///
241 | /// F on our diagram.
242 | class func fmap(A -> B) -> FA -> FB
243 | func <%>(A -> B, FA) -> FB
244 |
245 | /// Constant Replace | Replaces all values in the target Functor with a singular constant value
246 | /// from the source Functor.
247 | func <^(A, FB) -> FA
248 | }
249 |
250 | /// Eases writing a definition for Constant Replace. Hand it an fmap, and x in B, and a source.
251 | public func defaultReplace(fmap : (A -> B) -> FA -> FB)(x : B)(f : FA) -> FB {
252 | return (fmap • const)(x)(f)
253 | }
254 | ```
255 |
256 | ##Bodies
257 |
258 | Function bodies begin one line after and one tab-level from their surrounding function or closure brace. Function bodies may only contain let bindings, returns, switches, or simple if-else statements. Functions must return a value. For functions that perform side effects, that value shall be wrapped in an IO monad. For functions that perform only side effects, a result of type `IO<()>` shall always be returned.
259 |
260 | **For Example**
261 |
262 | ```
263 | /// Exits with error code 0 (success).
264 | public func exitSuccess() -> IO {
265 | return exitWith(.ExitSuccess)
266 | }
267 | ```
268 |
269 | ##Other
270 |
271 | Type annotations include a space between the parameter name and the colon, and the colon and the type. Function applications do not include a space between the parameter name and the colon, but do include it between the colon and the type.
272 |
273 | **For Example**
274 |
275 | ```
276 | public func until(p : A -> Bool)(f : A -> A)(x : A) -> A {
277 | if p(x) {
278 | return x
279 | }
280 | return until(p)(f: f)(x: f(x))
281 | }
282 | ```
283 |
284 | Generic typealiases are forbidden by the language, so typealiases not required to implement protocols are forbidden. Functions should never expose typealias'd types, and should instead expose the fully expanded type to the user.
285 |
286 | Non-total functions are allowed, but discouraged and should always be documented as such. Generally, prefer types to enforce invariants.
287 |
288 |
289 |
--------------------------------------------------------------------------------