tag:blogger.com,1999:blog-54282328823957583572024-02-07T16:57:26.167-05:00more indirectionA blog on Scala, Swift and FPMore Indirectionhttp://www.blogger.com/profile/09749479501125035513noreply@blogger.comBlogger21125tag:blogger.com,1999:blog-5428232882395758357.post-44026388482106294122015-12-24T08:17:00.000-05:002015-12-24T08:17:01.095-05:00Working on the Swift compiler with Jetbrains AppCode<div>
Just a few weeks ago, Apple <a href="https://developer.apple.com/swift/blog/" target="_blank">open-sourced</a> the Swift compiler and standard library. This is exciting news for the Swift community: not only will Swift development now be done in the open, but the availability of Swift's source makes it possible to port Swift to <a href="https://swift.org/download/" target="_blank">other platforms</a>.</div>
<div>
<br /></div>
<div>
The <a href="https://github.com/apple/swift" target="_blank">Swift codebase</a> is very large (approaching 400k lines of C++ code, including test cases) so an IDE to help you navigate through it would be ideal. I'm partial to the Jetbrains tools, but wasn't sure if I could use them to browse the Swift source tree. I had some difficulty getting CLion to use the CMake scripts in the Swift repo and was about to give up. Fortunately, I noticed in the bottom of Swift's <a href="https://github.com/apple/swift/blob/master/README.md" target="_blank">README</a> that there's a script to generate an Xcode project from the Swift repo.</div>
<div>
<br /></div>
<div>
So here's how I generated the Xcode project and was able to work on Swift using AppCode:</div>
<div>
<br /></div>
<div>
1. Create a new parent directory, cd into it and clone the <a href="https://github.com/apple/swift" target="_blank">Swift repo</a>:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">$ mkdir Swift</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">$ cd Swift</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">$ git clone https://github.com/apple/swift.git</span></div>
<div>
<br /></div>
<div>
2. Prep the Swift repo, per the instructions in the <a href="https://github.com/apple/swift/blob/master/README.md" target="_blank">README</a>. This will import other tools that Swift depends on (like cmark and some LLVM tools)</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">$ cd swift</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">$ <span style="color: #333333;">./utils/update-checkout --clone</span></span></div>
<div>
<br /></div>
<div>
3. Generate the Xcode project:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">$ utils/build-script -X --skip-build -- --reconfigure</span></div>
<div>
<br /></div>
<div>
4. Now open AppCode and click File | Open...</div>
<div>
<br /></div>
<div>
5. Find the outer Swift directory you created in step 1 (the one that holds the Swift repo and all its associated tools).</div>
<div>
<br /></div>
<div>
6. Open the build folder, then open Xcode-DebugAssert. You'll see a folder that looks like <span style="font-family: Courier New, Courier, monospace;">swift-macosx-x86_64</span> (the exact name may differ based on your system).</div>
<div>
<br /></div>
<div>
Just open this folder in AppCode, wait for indexing to complete, and you'll be able to browse and Edit the Swift source from AppCode!</div>
<div>
<br /></div>
<div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_4WRIzOoODGjMS5BTrTAfqCnHyw-XHkZcMWtcXJjhjSfzPF8v85cf_uVZM-a49RP6pPzygpynmawK0JfdacSsUBarWqTTrcwIsHduFXZvjqUF6EU4YK4hhBhEiS7T-0hZ_-C5K4p2wifd/s1600/AppCode.png" imageanchor="1"><img border="0" height="412" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_4WRIzOoODGjMS5BTrTAfqCnHyw-XHkZcMWtcXJjhjSfzPF8v85cf_uVZM-a49RP6pPzygpynmawK0JfdacSsUBarWqTTrcwIsHduFXZvjqUF6EU4YK4hhBhEiS7T-0hZ_-C5K4p2wifd/s640/AppCode.png" width="640" /></a></div>
More Indirectionhttp://www.blogger.com/profile/09749479501125035513noreply@blogger.com0tag:blogger.com,1999:blog-5428232882395758357.post-30124817782695186512015-07-26T21:31:00.001-04:002016-01-11T08:39:08.926-05:00GCD and Parallel Collections in SwiftOne of the benefits of functional programming is that it's straightforward to parallelize operations. Common FP idioms like map, filter and reduce can be adapted so they run on many cores at once, letting you get instant parallelization wherever you find a bottleneck.<br />
<br />
The benefits of these parallel combinators are huge. Wherever you find a bottleneck in your program, you can simply replace your call to map with a call to a parallel map and your code will be able to take advantage of all the cores on your system. On my eight-core system, for example, simply using a parallel map can theoretically yield an eight-fold speed boost. Of course, there are a few reasons you might not see that theoretical speed improvement: namely, the overhead of creating threads, splitting up the work, synchronizing data between the threads, etc. Nevertheless, if you profile your code and focus on hotspots, you can see tremendous improvements with simple changes.<br />
<br />
Swift doesn't yet come with parallel collections functions, but we can build them ourselves, using Grand Central Dispatch:<br />
<div style="color: #686868; font-family: Menlo; font-size: 11px;">
<div style="min-height: 13px;">
</div>
// requires Swift 2.0 or higher<br />
<div style="color: #0329d6;">
extension<span style="color: black; font-variant-ligatures: no-common-ligatures;"> </span><span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">Array</span><span style="color: black; font-variant-ligatures: no-common-ligatures;"> {</span></div>
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">public</span> <span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">func</span> pmap<t>(transform: (<span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">Element</span> -> <span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">T</span>)) -> [<span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">T</span>] {</t><br />
<div style="color: #0329d6;">
<span style="color: black; font-variant-ligatures: no-common-ligatures;"> </span>guard<span style="color: black; font-variant-ligatures: no-common-ligatures;"> !</span>self<span style="color: black; font-variant-ligatures: no-common-ligatures;">.</span>isEmpty<span style="color: black; font-variant-ligatures: no-common-ligatures;"> </span>else<span style="color: black; font-variant-ligatures: no-common-ligatures;"> {</span></div>
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">return</span> []<br />
}<br />
<div style="min-height: 13px;">
</div>
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">var</span> result: [(<span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">Int</span>, [<span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">T</span>])] = []<br />
<div style="min-height: 13px;">
</div>
<div style="color: #0329d6;">
<span style="color: black; font-variant-ligatures: no-common-ligatures;"> </span>let<span style="color: black; font-variant-ligatures: no-common-ligatures;"> group = </span>dispatch_group_create<span style="color: black; font-variant-ligatures: no-common-ligatures;">()</span></div>
<div style="color: #0329d6;">
<span style="color: black; font-variant-ligatures: no-common-ligatures;"> </span>let<span style="color: black; font-variant-ligatures: no-common-ligatures;"> lock = </span>dispatch_queue_create<span style="color: black; font-variant-ligatures: no-common-ligatures;">(</span><span style="color: #ff52a9; font-variant-ligatures: no-common-ligatures;">"pmap queue for result"</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">, </span>DISPATCH_QUEUE_SERIAL<span style="color: black; font-variant-ligatures: no-common-ligatures;">)</span></div>
<div style="min-height: 13px;">
</div>
<div style="color: #0329d6;">
<span style="color: black; font-variant-ligatures: no-common-ligatures;"> </span>let<span style="color: black; font-variant-ligatures: no-common-ligatures;"> step: </span><span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">Int</span><span style="color: black; font-variant-ligatures: no-common-ligatures;"> = </span>max<span style="color: black; font-variant-ligatures: no-common-ligatures;">(</span>1<span style="color: black; font-variant-ligatures: no-common-ligatures;">, </span>self<span style="color: black; font-variant-ligatures: no-common-ligatures;">.</span>count<span style="color: black; font-variant-ligatures: no-common-ligatures;"> / </span><span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">NSProcessInfo</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">.</span>processInfo<span style="color: black; font-variant-ligatures: no-common-ligatures;">().</span>activeProcessorCount<span style="color: black; font-variant-ligatures: no-common-ligatures;">) </span><span style="color: #686868; font-variant-ligatures: no-common-ligatures;">// step can never be 0</span></div>
<div style="min-height: 13px;">
</div>
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">for</span> <span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">var</span> stepIndex = <span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">0</span>; stepIndex * step < <span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">self</span>.<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">count</span>; stepIndex++ {<br />
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">let</span> capturedStepIndex = stepIndex<br />
<div style="min-height: 13px;">
<br /></div>
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">var</span> stepResult: [<span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">T</span>] = []<br />
<div style="color: #0329d6;">
<span style="color: black; font-variant-ligatures: no-common-ligatures;"> </span>dispatch_group_async<span style="color: black; font-variant-ligatures: no-common-ligatures;">(group, </span>dispatch_get_global_queue<span style="color: black; font-variant-ligatures: no-common-ligatures;">(</span>DISPATCH_QUEUE_PRIORITY_DEFAULT<span style="color: black; font-variant-ligatures: no-common-ligatures;">, </span>0<span style="color: black; font-variant-ligatures: no-common-ligatures;">)) {</span></div>
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">for</span> i <span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">in</span> (capturedStepIndex * step)..<((capturedStepIndex + <span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">1</span>) * step) {<br />
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">if</span> i < <span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">self</span>.<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">count</span> {<br />
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">let</span> mappedElement = transform(<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">self</span>[i])<br />
stepResult += [mappedElement]<br />
}<br />
}<br />
<div style="min-height: 13px;">
<br /></div>
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">dispatch_group_async</span>(group, lock) {<br />
result += [(capturedStepIndex, stepResult)]<br />
}<br />
}<br />
}<br />
<div style="min-height: 13px;">
</div>
<div style="color: #0329d6;">
<span style="color: black; font-variant-ligatures: no-common-ligatures;"> </span>dispatch_group_wait<span style="color: black; font-variant-ligatures: no-common-ligatures;">(group, </span>DISPATCH_TIME_FOREVER<span style="color: black; font-variant-ligatures: no-common-ligatures;">)</span></div>
<div style="min-height: 13px;">
</div>
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">return</span> result.<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">sort</span> { $0.<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">0</span> < $1.<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">0</span> }.<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">flatMap</span> { $0.<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">1</span> }<br />
}<br />
}</div>
<div style="font-family: Menlo; font-size: 11px;">
<br /></div>
<span style="font-family: Courier New, Courier, monospace;">pmap</span> takes the same arguments as <span style="font-family: Courier New, Courier, monospace;">map</span> but runs the function across all of your system's CPUs. Let's break the function down, step by step.<br />
<ol>
<li>In the case of an empty array, <span style="font-family: Courier New, Courier, monospace;">pmap</span> returns early, since the overhead of splitting up the work and synchronizing the results is non-trivial. We might take this even further by falling back to standard map for arrays with a very small element count.</li>
<li>Create a Grand Central Dispatch <a href="https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/#//apple_ref/doc/uid/TP40008079-CH2-SW19" target="_blank">group</a> that we can associate with the GCD blocks we'll run later on. Since all of these blocks will be in the same group, the invoking thread can wait for the group to be empty at the end of the function and know for certain that all of the background work has finished before returning to the caller.</li>
<li>Create a dedicated, sequential lock queue to control access to the result array. This is a <a href="https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/ThreadMigration/ThreadMigration.html#//apple_ref/doc/uid/TP40008091-CH105-SW3" target="_blank">common pattern in GCD</a>: simulating a mutex with a sequential queue. Since a sequential queue will never run two blocks simultaneously, we can be sure that whatever operations we perform in this queue will be isolated from one another.</li>
<li>Next, <span style="font-family: Courier New, Courier, monospace;">pmap</span> breaks the array up into "steps", based on the host machine's CPU count (since this is read at runtime from <span style="font-family: Courier New, Courier, monospace;">NSProcessInfo</span>, this function will automatically scale up to use all available cores). Each step is dispatched to one of GCD's global background queues. In the invoking thread, this for loop will run very, very quickly, since all it does is add closures to background queues.</li>
<li>The main for loop iterates through each "step," capturing the stepIndex in a local variable, capturedStepIndex. If we don't do this, the closures passed to dispatch_group_async will all refer to the same storage location - as the for loop increments, all of the workers will see stepIndex increase by one and will all operate on the same step. By capturing the variable, each worker has its own copy of stepIndex, which never changes as the for loop proceeds.</li>
<li>We calculate the start and end indices for this step. For each array element in that range, we call <span style="font-family: Courier New, Courier, monospace;">transform</span> on the element and add it to this worker's local <span style="font-family: Courier New, Courier, monospace;">stepResult</span> array. Because it's unlikely that the number of elements in the array will be exactly divisible by a given machine's processor count, we check that <span style="font-family: Courier New, Courier, monospace;">i</span> never goes beyond the end of the array, which could otherwise happen in the very last step.</li>
<li>After an entire step has been processed, we add this worker's results to the master <span style="font-family: Courier New, Courier, monospace;">result</span> array. Since the order in which workers will finish is nondeterministic, each element of the <span style="font-family: Courier New, Courier, monospace;">result</span> array is a tuple containing the <span style="font-family: Courier New, Courier, monospace;">stepIndex</span> and the transformed elements in that step's range. We use the lock queue to ensure that all changes to the <span style="font-family: Courier New, Courier, monospace;">result</span> array are synchronized.
<ul><ul>
<li>Note that we only have to enter this critical section once for each core - an alternative implementation of <span style="font-family: Courier New, Courier, monospace;">pmap</span> might create a single master result array of the same size as the input and set each element to its mapped result as it goes. But this would have to enter the critical section once for every array element, instead of just once for each CPU, generating more memory and processor contention and benefiting less from spatial locality. </li>
<li>We use <span style="font-family: Courier New, Courier, monospace;">dispatch_sync</span> instead of <span style="font-family: Courier New, Courier, monospace;">dispatch_async</span> because we want to be sure that the worker's changes have been applied to the <span style="font-family: Courier New, Courier, monospace;">masterResults</span> array before declaring this worker to be done. If we were to use <span style="font-family: Courier New, Courier, monospace;">dispatch_async</span>, the scheduler could very easily finish all of the step blocks but leave one or more of these critical section blocks unprocessed, leaving us with an incomplete result.</li>
</ul>
</ul>
</li>
<li>Back on the original thread, we call <span style="font-family: Courier New, Courier, monospace;">dispatch_group_wait</span>, which waits until all blocks in the group have completed. At this point, we know that all work has been done and all changes to the master results array have been made.</li>
<li>The final line sorts the master array by <span style="font-family: Courier New, Courier, monospace;">stepIndex</span> (since steps finish in a nondeterministic order) and then flattens the master array in that order.</li>
</ol>
<div>
<span style="font-family: inherit;">To see how this works, let's create a simple profile function:</span></div>
<div style="font-family: Menlo; font-size: 11px;">
<br /></div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">func</span> profile<a href="https://www.blogger.com/blogger.g?blogID=5428232882395758357">(desc: <span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">String</span>, block: () -> <span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">A</span>) -> <span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">Void</span> {</a><br />
<div style="color: #0329d6;">
<span style="color: black; font-variant-ligatures: no-common-ligatures;"> </span>let<span style="color: black; font-variant-ligatures: no-common-ligatures;"> start = </span><span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">NSDate</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">().</span>timeIntervalSince1970</div>
block()<br />
<div style="min-height: 13px;">
</div>
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">let</span> duration = <span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">NSDate</span>().<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">timeIntervalSince1970</span> - start<br />
<div style="color: #ff52a9;">
<span style="color: black; font-variant-ligatures: no-common-ligatures;"> </span><span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">print</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">(</span>"Profiler: completed <span style="color: black; font-variant-ligatures: no-common-ligatures;">\</span>(<span style="color: black; font-variant-ligatures: no-common-ligatures;">desc</span>) in <span style="color: black; font-variant-ligatures: no-common-ligatures;">\</span>(<span style="color: black; font-variant-ligatures: no-common-ligatures;">duration * </span><span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">1000</span>)ms"<span style="color: black; font-variant-ligatures: no-common-ligatures;">)</span></div>
<br />
}</div>
<div>
We'll test this out using a simple function called <span style="font-family: Courier New, Courier, monospace;">slowCalc</span>, which adds a small sleep delay before each calculation, to ensure that each map operation does enough work. In production code, you should <b>never</b> sleep in code submitted to a GCD queue - this is purely to simulate a slow calculation for demonstration purposes. Without this little delay, the overhead of parallelization would be too great to see a speedup:<br />
<br />
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">func</span> slowCalc(x: <span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">Int</span>) -> <span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">Int</span> {</div>
<div style="color: #0329d6; font-family: Menlo; font-size: 11px;">
<span style="color: black; font-variant-ligatures: no-common-ligatures;"> </span><span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">NSThread</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">.</span>sleepForTimeInterval<span style="color: black; font-variant-ligatures: no-common-ligatures;">(</span>0.1<span style="color: black; font-variant-ligatures: no-common-ligatures;">)</span></div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">return</span> x * <span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">2</span></div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
<div style="font-family: Menlo; font-size: 11px; min-height: 13px;">
<br /></div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">let</span> smallTestData: [<span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">Int</span>] = [<span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">Int</span>](<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">0</span>..<<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">10</span>)</div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">let</span> largeTestData = [<span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">Int</span>](<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">0</span>..<<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">300</span>)</div>
<div style="font-family: Menlo; font-size: 11px; min-height: 13px;">
<br /></div>
<div style="color: #0329d6; font-family: Menlo; font-size: 11px;">
profile<span style="color: black; font-variant-ligatures: no-common-ligatures;">(</span><span style="color: #ff52a9; font-variant-ligatures: no-common-ligatures;">"large dataset (sequential)"</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">) { </span>largeTestData<span style="color: black; font-variant-ligatures: no-common-ligatures;">.</span>map<span style="color: black; font-variant-ligatures: no-common-ligatures;"> { </span>slowCalc<span style="color: black; font-variant-ligatures: no-common-ligatures;">($0) } }</span></div>
<div style="color: #0329d6; font-family: Menlo; font-size: 11px;">
profile<span style="color: black; font-variant-ligatures: no-common-ligatures;">(</span><span style="color: #ff52a9; font-variant-ligatures: no-common-ligatures;">"large dataset (parallel)"</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">) { </span>largeTestData<span style="color: black; font-variant-ligatures: no-common-ligatures;">.</span>pmap<span style="color: black; font-variant-ligatures: no-common-ligatures;"> { </span>slowCalc<span style="color: black; font-variant-ligatures: no-common-ligatures;">($0) } }</span></div>
</div>
<div>
<span style="color: black; font-variant-ligatures: no-common-ligatures;"><br /></span></div>
<div>
<span style="font-family: inherit;">On my eight-core machine, this results in:</span></div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="font-family: Times; font-size: small;"><br /></span></div>
<div style="font-family: Menlo; font-size: 11px;">
<b>Profiler: completed large dataset (sequential) in 31239.7990226746ms</b><br />
<b>Profiler: completed large dataset (parallel) in 4005.04493713379ms</b><br />
<div>
<b><br /></b></div>
</div>
<div>
<span style="font-family: inherit;">an 7.8-fold increase, which is about what you'd expect.</span></div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="font-family: Times; font-size: small;"><br /></span></div>
It's important thing to remember that if each iteration doesn't do enough work, the overhead of splitting up work, setting up worker blocks and synchronizing data access will far outweigh the time savings of parallelization. The amount of overhead involved can be surprising. This code is identical to the above, except that it doesn't add the extra delay.<br />
<br />
<div style="color: #ff52a9; font-family: Menlo; font-size: 11px;">
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">profile</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">(</span>"large dataset (sequential, no delay)"<span style="color: black; font-variant-ligatures: no-common-ligatures;">) { </span><span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">largeTestData</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">.</span><span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">map</span><span style="color: black; font-variant-ligatures: no-common-ligatures;"> { $0 * </span><span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">2</span><span style="color: black; font-variant-ligatures: no-common-ligatures;"> } }</span></div>
<div style="color: #ff52a9; font-family: Menlo; font-size: 11px;">
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">profile</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">(</span>"large dataset (parallel, no delay)"<span style="color: black; font-variant-ligatures: no-common-ligatures;">) { </span><span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">largeTestData</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">.</span><span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">pmap</span><span style="color: black; font-variant-ligatures: no-common-ligatures;"> { $0 * </span><span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">2</span><span style="color: black; font-variant-ligatures: no-common-ligatures;"> } }</span></div>
<div>
<span style="color: black; font-variant-ligatures: no-common-ligatures;"><br /></span></div>
<div>
<span style="color: black; font-variant-ligatures: no-common-ligatures;">On my machine, it results in:</span></div>
<div>
<div style="font-family: Menlo; font-size: 11px;">
<div style="min-height: 13px;">
<b></b><br /></div>
<b>Profiler: completed large dataset (sequential, no delay) in 53.4629821777344ms</b><br />
<b>Profiler: completed large dataset (parallel, no delay) in 161.548852920532ms</b><br />
<div>
<b><br /></b></div>
</div>
</div>
<div>
The parallel version is <b>three times slower </b>than the sequential version! This is a really important consideration when using parallel collection functions:</div>
<div>
<ol>
<li>Make sure that each of your iterations does enough work to make parallelization worth it.</li>
<li>Parallel collections are not a panacea - you can't just sprinkle them throughout your code and assume you'll get a performance boost. You still need to profile for hotspots, and it's important to focus on bottlenecks found through profiling, rather than hunches about what parts of your code are slowest.</li>
<li>Modern CPUs are blindingly fast - basic operations like addition or multiplication are so fast that it's not worth parallelizing these, unless your array is very large.</li>
</ol>
<div>
You can use the same techniques to implement a parallel filter function:<br />
<br />
<div style="color: #686868; font-family: Menlo; font-size: 11px;">
// requires Swift 2.0 or higher</div>
<div style="color: #0329d6; font-family: Menlo; font-size: 11px;">
extension<span style="color: black; font-variant-ligatures: no-common-ligatures;"> </span><span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">Array</span><span style="color: black; font-variant-ligatures: no-common-ligatures;"> {</span></div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">public</span> <span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">func</span> pfilter(includeElement: <span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">Element</span> -> <span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">Bool</span>) -> [<span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">Element</span>] {</div>
<div style="color: #0329d6; font-family: Menlo; font-size: 11px;">
<span style="color: black; font-variant-ligatures: no-common-ligatures;"> </span>guard<span style="color: black; font-variant-ligatures: no-common-ligatures;"> !</span>self<span style="color: black; font-variant-ligatures: no-common-ligatures;">.</span>isEmpty<span style="color: black; font-variant-ligatures: no-common-ligatures;"> </span>else<span style="color: black; font-variant-ligatures: no-common-ligatures;"> {</span></div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">return</span> []</div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
<div style="font-family: Menlo; font-size: 11px; min-height: 13px;">
</div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">var</span> result: [(<span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">Int</span>, [<span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">Element</span>])] = []</div>
<div style="font-family: Menlo; font-size: 11px; min-height: 13px;">
</div>
<div style="color: #0329d6; font-family: Menlo; font-size: 11px;">
<span style="color: black; font-variant-ligatures: no-common-ligatures;"> </span>let<span style="color: black; font-variant-ligatures: no-common-ligatures;"> group = </span>dispatch_group_create<span style="color: black; font-variant-ligatures: no-common-ligatures;">()</span></div>
<div style="color: #0329d6; font-family: Menlo; font-size: 11px;">
<span style="color: black; font-variant-ligatures: no-common-ligatures;"> </span>let<span style="color: black; font-variant-ligatures: no-common-ligatures;"> lock = </span>dispatch_queue_create<span style="color: black; font-variant-ligatures: no-common-ligatures;">(</span><span style="color: #ff52a9; font-variant-ligatures: no-common-ligatures;">"pmap queue for result"</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">, </span>DISPATCH_QUEUE_SERIAL<span style="color: black; font-variant-ligatures: no-common-ligatures;">)</span></div>
<div style="font-family: Menlo; font-size: 11px; min-height: 13px;">
</div>
<div style="color: #0329d6; font-family: Menlo; font-size: 11px;">
<span style="color: black; font-variant-ligatures: no-common-ligatures;"> </span>let<span style="color: black; font-variant-ligatures: no-common-ligatures;"> step: </span><span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">Int</span><span style="color: black; font-variant-ligatures: no-common-ligatures;"> = </span>max<span style="color: black; font-variant-ligatures: no-common-ligatures;">(</span>1<span style="color: black; font-variant-ligatures: no-common-ligatures;">, </span>self<span style="color: black; font-variant-ligatures: no-common-ligatures;">.</span>count<span style="color: black; font-variant-ligatures: no-common-ligatures;"> / </span><span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">NSProcessInfo</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">.</span>processInfo<span style="color: black; font-variant-ligatures: no-common-ligatures;">().</span>activeProcessorCount<span style="color: black; font-variant-ligatures: no-common-ligatures;">) </span><span style="color: #686868; font-variant-ligatures: no-common-ligatures;">// step can never be 0</span></div>
<div style="font-family: Menlo; font-size: 11px; min-height: 13px;">
</div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">for</span> <span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">var</span> stepIndex = <span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">0</span>; stepIndex * step < <span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">self</span>.<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">count</span>; stepIndex++ {</div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">let</span> capturedStepIndex = stepIndex</div>
<div style="font-family: Menlo; font-size: 11px; min-height: 13px;">
</div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">var</span> stepResult: [<span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">Element</span>] = []</div>
<div style="color: #0329d6; font-family: Menlo; font-size: 11px;">
<span style="color: black; font-variant-ligatures: no-common-ligatures;"> </span>dispatch_group_async<span style="color: black; font-variant-ligatures: no-common-ligatures;">(group, </span>dispatch_get_global_queue<span style="color: black; font-variant-ligatures: no-common-ligatures;">(</span>DISPATCH_QUEUE_PRIORITY_DEFAULT<span style="color: black; font-variant-ligatures: no-common-ligatures;">, </span>0<span style="color: black; font-variant-ligatures: no-common-ligatures;">)) {</span></div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">for</span> i <span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">in</span> (capturedStepIndex * step)..<((capturedStepIndex + <span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">1</span>) * step) {</div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">if</span> i < <span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">self</span>.<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">count</span> && includeElement(<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">self</span>[i]) {</div>
<div style="font-family: Menlo; font-size: 11px;">
stepResult += [<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">self</span>[i]]</div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
<div style="font-family: Menlo; font-size: 11px; min-height: 13px;">
</div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">dispatch_group_async</span>(group, lock) {</div>
<div style="font-family: Menlo; font-size: 11px;">
result += [(capturedStepIndex, stepResult)]</div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
<div style="font-family: Menlo; font-size: 11px; min-height: 13px;">
</div>
<div style="color: #0329d6; font-family: Menlo; font-size: 11px;">
<span style="color: black; font-variant-ligatures: no-common-ligatures;"> </span>dispatch_group_wait<span style="color: black; font-variant-ligatures: no-common-ligatures;">(group, </span>DISPATCH_TIME_FOREVER<span style="color: black; font-variant-ligatures: no-common-ligatures;">)</span></div>
<div style="font-family: Menlo; font-size: 11px; min-height: 13px;">
</div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">return</span> result.<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">sort</span> { $0.<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">0</span> < $1.<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">0</span> }.<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">flatMap</span> { $0.<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">1</span> }</div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
</div>
</div>
<div>
<div style="font-family: Menlo; font-size: 11px;">
<div style="color: #686868;">
<br /></div>
</div>
This code is almost exactly identical to <span style="font-family: Courier New, Courier, monospace;">pmap</span> - only the logic in the inner <span style="font-family: Courier New, Courier, monospace;">for</span> loop is different.<br />
<br />
We can now start using these combinators together (again, we have to use a slowed-down predicate function in order to see the benefit from parallelization):<br />
<br />
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">func</span> slowTest(x: <span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">Int</span>) -> <span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">Bool</span> {</div>
<div style="color: #0329d6; font-family: Menlo; font-size: 11px;">
<span style="color: black; font-variant-ligatures: no-common-ligatures;"> </span><span style="color: #ab1f79; font-variant-ligatures: no-common-ligatures;">NSThread</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">.</span>sleepForTimeInterval<span style="color: black; font-variant-ligatures: no-common-ligatures;">(</span>0.1<span style="color: black; font-variant-ligatures: no-common-ligatures;">)</span></div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">return</span> x % <span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">2</span> == <span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">0</span></div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
<div style="font-family: Menlo; font-size: 11px; min-height: 13px;">
<br /></div>
<div style="color: #0329d6; font-family: Menlo; font-size: 11px;">
profile<span style="color: black; font-variant-ligatures: no-common-ligatures;">(</span><span style="color: #ff52a9; font-variant-ligatures: no-common-ligatures;">"large dataset (sequential)"</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">) { </span>largeTestData<span style="color: black; font-variant-ligatures: no-common-ligatures;">.</span>filter<span style="color: black; font-variant-ligatures: no-common-ligatures;"> { </span>slowTest<span style="color: black; font-variant-ligatures: no-common-ligatures;">($0) }.</span>map<span style="color: black; font-variant-ligatures: no-common-ligatures;"> { </span>slowCalc<span style="color: black; font-variant-ligatures: no-common-ligatures;">($0) } }</span></div>
<div style="color: #ff52a9; font-family: Menlo; font-size: 11px;">
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">profile</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">(</span>"large dataset (sequential filter, parallel map)"<span style="color: black; font-variant-ligatures: no-common-ligatures;">) { </span><span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">largeTestData</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">.</span><span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">filter</span><span style="color: black; font-variant-ligatures: no-common-ligatures;"> { </span><span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">slowTest</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">($0) }.</span><span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">pmap</span><span style="color: black; font-variant-ligatures: no-common-ligatures;"> { </span><span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">slowCalc</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">($0) } }</span></div>
<div style="color: #ff52a9; font-family: Menlo; font-size: 11px;">
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">profile</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">(</span>"large dataset (parallel filter, sequential map)"<span style="color: black; font-variant-ligatures: no-common-ligatures;">) { </span><span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">largeTestData</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">.</span><span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">pfilter</span><span style="color: black; font-variant-ligatures: no-common-ligatures;"> { </span><span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">slowTest</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">($0) }.</span><span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">map</span><span style="color: black; font-variant-ligatures: no-common-ligatures;"> { </span><span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">slowCalc</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">($0) } }</span></div>
<div style="color: #ff52a9; font-family: Menlo; font-size: 11px;">
<span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">profile</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">(</span>"large dataset (parallel filter, parallel map)"<span style="color: black; font-variant-ligatures: no-common-ligatures;">) { </span><span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">largeTestData</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">.</span><span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">pfilter</span><span style="color: black; font-variant-ligatures: no-common-ligatures;"> { </span><span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">slowTest</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">($0) }.</span><span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">pmap</span><span style="color: black; font-variant-ligatures: no-common-ligatures;"> { </span><span style="color: #0329d6; font-variant-ligatures: no-common-ligatures;">slowCalc</span><span style="color: black; font-variant-ligatures: no-common-ligatures;">($0) } }</span></div>
<div style="color: #0329d6; font-family: Menlo; font-size: 11px;">
<br /></div>
</div>
<div>
<span style="color: black; font-variant-ligatures: no-common-ligatures;">which results in:</span></div>
<div>
<span style="color: black; font-variant-ligatures: no-common-ligatures;"><br /></span></div>
<div>
<div style="font-family: Menlo; font-size: 11px;">
<b>Profiler: completed large dataset (sequential) in 1572.28803634644ms</b></div>
<div style="font-family: Menlo; font-size: 11px;">
<b>Profiler: completed large dataset (sequential filter, parallel map) in 1153.90300750732ms</b></div>
<div style="font-family: Menlo; font-size: 11px;">
<b>Profiler: completed large dataset (parallel filter, sequential map) in 642.061948776245ms</b></div>
<div style="font-family: Menlo; font-size: 11px;">
</div>
<div style="font-family: Menlo; font-size: 11px;">
<b>Profiler: completed large dataset (parallel filter, parallel map) in 231.456995010376ms</b></div>
</div>
<div>
<b><br /></b></div>
<div>
Using one parallel combinator gives a slight improvement; combining the two parallel operations gives us an almost sevenfold performance improvement over the basic sequential implementation.</div>
<div>
<br /></div>
<div>
Here are some other directions to pursue:</div>
<div>
<ol>
<li>Implement parallel versions of <span style="font-family: Courier New, Courier, monospace;">find</span>, <span style="font-family: Courier New, Courier, monospace;">any</span>/<span style="font-family: Courier New, Courier, monospace;">exists</span> and <span style="font-family: Courier New, Courier, monospace;">all</span>. These are tricky because their contracts stipulate that processing stops as soon as they have a result. So you'll have to find some way to stop your parallel workers as soon as the function has its answer.</li>
<li>Implement a parallel version of reduce. The benefit of doing this is that reduce is a "primitive" higher-order function - you can easily implement <span style="font-family: Courier New, Courier, monospace;">pmap</span> and <span style="font-family: Courier New, Courier, monospace;">pfilter</span> given an existing parallel reduce function.</li>
<li>Generalize these functions to work on all collections (not just arrays), using Swift 2's protocol extensions.</li>
</ol>
</div>
More Indirectionhttp://www.blogger.com/profile/09749479501125035513noreply@blogger.com3tag:blogger.com,1999:blog-5428232882395758357.post-90765059327609065502014-12-15T14:08:00.001-05:002014-12-27T09:42:15.862-05:00Simple Combinators for Manipulating CGPoint/CGSize/CGRect with SwiftOne of the most painful things about Objective-C was having to modify CGPoint, CGSize or CGRect values. The clunky struct interface made even simple modifications verbose and ugly, since struct expressions were read-only:<br />
<br />
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #587ea8; font-family: Menlo;">CGRect</span> imageBounds = <span style="color: #323e7d; font-family: Menlo;">self</span>.view.bounds;</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
imageBounds.size.height -= <span style="color: #323e7d; font-family: Menlo;">self</span>.footer.bounds.size.height;</div>
<br />
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">self</span>.imageView.bounds = imageBounds;</div>
<div>
<br /></div>
<div>
Even though we have auto-layout, I often find myself doing this kind of arithmetic with points, size or rects. In Objective-C, it required either generating dummy variables so you can modify members (as above), or really messy struct initialization syntax:</div>
<div>
<br /></div>
<div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">self</span>.imageView.bounds = (CGRect) { </div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
.origin = <span style="color: #323e7d; font-family: Menlo;">self</span>.view.bounds.origin,</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
.size = CGSizeMake(<span style="color: #323e7d; font-family: Menlo;">self</span>.view.bounds.size.width, <span style="color: #323e7d; font-family: Menlo;">self</span>.view.bounds.size.height - </div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;"> self</span>.footer.bounds.size.height) };</div>
</div>
<div>
<br /></div>
<div>
Fortunately, none of this boilerplate is necessary with Swift. Since Swift lets you extend even C structures with new methods, I wrote a handful of combinators that eliminate this kind of code. The above snippet can now be replaced with:</div>
<div>
<br /></div>
<div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">self</span>.imageView.bounds = <span style="color: #323e7d; font-family: Menlo;">self</span>.view.bounds.mapHeight { $<span style="color: #323e7d; font-family: Menlo;">0</span> - <span style="color: #323e7d; font-family: Menlo;">self</span>.footer.size.height }</div>
</div>
<div>
<br />
I can easily enlarge a scroll view's content size to hold its pages:<br />
<br />
<div style="font-size: 11px;">
<span style="font-family: 'Source Code Pro';"><span style="color: #323e7d; font-family: Menlo;"> self</span>.scrollView.contentSize = <span style="color: #323e7d; font-family: Menlo;">self</span>.scrollView.bounds.size.mapWidth { $0 * CGFloat(</span><span style="color: #323e7d; font-family: Menlo;">pages.count)</span><span style="font-family: Source Code Pro;"> }</span></div>
</div>
<div>
<span style="font-family: Source Code Pro;"><br /></span></div>
<div>
I can do calculations that previously would've required dozens of lines of code in just one or two:</div>
<div>
<br /></div>
<div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
let topHalfFrame = <span style="color: #323e7d; font-family: Menlo;">self</span>.view.bounds.mapHeight { $<span style="color: #323e7d; font-family: Menlo;">0</span> / <span style="color: #323e7d; font-family: Menlo;">2</span> }</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
let bottomHalfFrame = topHalfFrame.mapY { $<span style="color: #323e7d; font-family: Menlo;">0</span> + topHalfFrame.size.height }</div>
</div>
<div>
<br /></div>
<div>
These two lines will give me two frames that each take up half of the height of their parent view.</div>
<div>
<br /></div>
<div>
In cases where I simply need to set a value, I use the primitive "with..." functions:</div>
<div>
<br /></div>
<div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">self</span>.view.bounds.withX(<span style="color: #323e7d; font-family: Menlo;">0</span>).withY(<span style="color: #323e7d; font-family: Menlo;">0</span>).withSize(0).withHeight(0)</div>
</div>
<div>
<br /></div>
<div>
Note that these methods can all be chained to create complex expressions.</div>
<div>
<br /></div>
<div>
The code for these methods is trivial, yet they give you a huge boost in expressive power.<br />
<br />
<b>GitHub project</b>: <a href="https://github.com/moreindirection/SwiftGeometry">https://github.com/moreindirection/SwiftGeometry</a><br />
<h2>
Code</h2>
</div>
<div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">extension</span> CGPoint {</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">func</span> mapX(f: (<span style="color: #587ea8; font-family: Menlo;">CGFloat</span> -> <span style="color: #587ea8; font-family: Menlo;">CGFloat</span>)) -> <span style="color: #587ea8; font-family: Menlo;">CGPoint</span> {</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">return</span> <span style="color: #323e7d; font-family: Menlo;">self</span>.withX(f(<span style="color: #323e7d; font-family: Menlo;">self</span>.x))</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
}</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px; min-height: 14px;">
</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">func</span> mapY(f: (<span style="color: #587ea8; font-family: Menlo;">CGFloat</span> -> <span style="color: #587ea8; font-family: Menlo;">CGFloat</span>)) -> <span style="color: #587ea8; font-family: Menlo;">CGPoint</span> {</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">return</span> <span style="color: #323e7d; font-family: Menlo;">self</span>.withY(f(<span style="color: #323e7d; font-family: Menlo;">self</span>.y))</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
}</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px; min-height: 14px;">
</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">func</span> withX(x: <span style="color: #587ea8; font-family: Menlo;">CGFloat</span>) -> <span style="color: #587ea8; font-family: Menlo;">CGPoint</span> {</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">return</span> CGPoint(x: x, y: <span style="color: #323e7d; font-family: Menlo;">self</span>.y)</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
}</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px; min-height: 14px;">
</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">func</span> withY(y: <span style="color: #587ea8; font-family: Menlo;">CGFloat</span>) -> <span style="color: #587ea8; font-family: Menlo;">CGPoint</span> {</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">return</span> CGPoint(x: <span style="color: #323e7d; font-family: Menlo;">self</span>.x, y: y)</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
}</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
}</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px; min-height: 14px;">
<br /></div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">extension</span> CGSize {</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">func</span> mapWidth(f: (<span style="color: #587ea8; font-family: Menlo;">CGFloat</span> -> <span style="color: #587ea8; font-family: Menlo;">CGFloat</span>)) -> <span style="color: #587ea8; font-family: Menlo;">CGSize</span> {</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">return</span> <span style="color: #323e7d; font-family: Menlo;">self</span>.withWidth(f(<span style="color: #323e7d; font-family: Menlo;">self</span>.width))</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
}</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px; min-height: 14px;">
</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">func</span> mapHeight(f: (<span style="color: #587ea8; font-family: Menlo;">CGFloat</span> -> <span style="color: #587ea8; font-family: Menlo;">CGFloat</span>)) -> <span style="color: #587ea8; font-family: Menlo;">CGSize</span> {</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">return</span> <span style="color: #323e7d; font-family: Menlo;">self</span>.withHeight(f(<span style="color: #323e7d; font-family: Menlo;">self</span>.height))</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
}</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px; min-height: 14px;">
</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">func</span> withWidth(width: <span style="color: #587ea8; font-family: Menlo;">CGFloat</span>) -> <span style="color: #587ea8; font-family: Menlo;">CGSize</span> {</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">return</span> CGSize(width: width, height: <span style="color: #323e7d; font-family: Menlo;">self</span>.height)</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
}</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px; min-height: 14px;">
</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">func</span> withHeight(height: <span style="color: #587ea8; font-family: Menlo;">CGFloat</span>) -> <span style="color: #587ea8; font-family: Menlo;">CGSize</span> {</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">return</span> CGSize(width: <span style="color: #323e7d; font-family: Menlo;">self</span>.width, height: height)</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
}</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
}</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px; min-height: 14px;">
<br /></div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">extension</span> CGRect {</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">func</span> mapX(f: (<span style="color: #587ea8; font-family: Menlo;">CGFloat</span> -> <span style="color: #587ea8; font-family: Menlo;">CGFloat</span>)) -> <span style="color: #587ea8; font-family: Menlo;">CGRect</span> {</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">return</span> <span style="color: #323e7d; font-family: Menlo;">self</span>.withX(f(<span style="color: #323e7d; font-family: Menlo;">self</span>.origin.x))</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
}</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px; min-height: 14px;">
</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">func</span> mapY(f: (<span style="color: #587ea8; font-family: Menlo;">CGFloat</span> -> <span style="color: #587ea8; font-family: Menlo;">CGFloat</span>)) -> <span style="color: #587ea8; font-family: Menlo;">CGRect</span> {</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">return</span> <span style="color: #323e7d; font-family: Menlo;">self</span>.withY(f(<span style="color: #323e7d; font-family: Menlo;">self</span>.origin.y))</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
}</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px; min-height: 14px;">
</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">func</span> mapWidth(f: (<span style="color: #587ea8; font-family: Menlo;">CGFloat</span> -> <span style="color: #587ea8; font-family: Menlo;">CGFloat</span>)) -> <span style="color: #587ea8; font-family: Menlo;">CGRect</span> {</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">return</span> <span style="color: #323e7d; font-family: Menlo;">self</span>.withWidth(f(<span style="color: #323e7d; font-family: Menlo;">self</span>.size.width))</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
}</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px; min-height: 14px;">
</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">func</span> mapHeight(f: (<span style="color: #587ea8; font-family: Menlo;">CGFloat</span> -> <span style="color: #587ea8; font-family: Menlo;">CGFloat</span>)) -> <span style="color: #587ea8; font-family: Menlo;">CGRect</span> {</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">return</span> <span style="color: #323e7d; font-family: Menlo;">self</span>.withHeight(f(<span style="color: #323e7d; font-family: Menlo;">self</span>.size.height))</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
}</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px; min-height: 14px;">
</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">func</span> withX(x: <span style="color: #587ea8; font-family: Menlo;">CGFloat</span>) -> <span style="color: #587ea8; font-family: Menlo;">CGRect</span> {</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">return</span> CGRect(origin: <span style="color: #323e7d; font-family: Menlo;">self</span>.origin.withX(x), size: <span style="color: #323e7d; font-family: Menlo;">self</span>.size)</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
}</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px; min-height: 14px;">
</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">func</span> withY(y: <span style="color: #587ea8; font-family: Menlo;">CGFloat</span>) -> <span style="color: #587ea8; font-family: Menlo;">CGRect</span> {</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">return</span> CGRect(origin: <span style="color: #323e7d; font-family: Menlo;">self</span>.origin.withY(y), size: <span style="color: #323e7d; font-family: Menlo;">self</span>.size)</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
}</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px; min-height: 14px;">
</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">func</span> withWidth(width: <span style="color: #587ea8; font-family: Menlo;">CGFloat</span>) -> <span style="color: #587ea8; font-family: Menlo;">CGRect</span> {</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">return</span> CGRect(origin: <span style="color: #323e7d; font-family: Menlo;">self</span>.origin, size: <span style="color: #323e7d; font-family: Menlo;">self</span>.size.withWidth(width))</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
}</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px; min-height: 14px;">
</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">func</span> withHeight(height: <span style="color: #587ea8; font-family: Menlo;">CGFloat</span>) -> <span style="color: #587ea8; font-family: Menlo;">CGRect</span> {</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
<span style="color: #323e7d; font-family: Menlo;">return</span> CGRect(origin: <span style="color: #323e7d; font-family: Menlo;">self</span>.origin, size: <span style="color: #323e7d; font-family: Menlo;">self</span>.size.withHeight(height))</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
}</div>
<div style="font-family: 'Source Code Pro'; font-size: 11px;">
}</div>
</div>
More Indirectionhttp://www.blogger.com/profile/09749479501125035513noreply@blogger.com0tag:blogger.com,1999:blog-5428232882395758357.post-25332424416741015462014-08-26T21:39:00.000-04:002016-05-04T10:29:06.939-04:00NSNotificationCenter, Swift and blocksThe conventional way to register observers with <span style="font-family: "courier new" , "courier" , monospace;">NSNotificationCenter</span> is to use the <a href="https://developer.apple.com/library/ios/documentation/general/conceptual/CocoaEncyclopedia/Target-Action/Target-Action.html" target="_blank">target-action pattern</a>. While this gets the job done, it's inherently not type-safe.<br />
<br />
For example, the following Swift snippet will compile perfectly:<br />
<br />
<div style="font-family: Menlo; font-size: 11px;">
NSNotificationCenter.defaultCenter().addObserver(<span style="color: #35568a;">self</span>, selector: Selector(<span style="color: #e82300;">"itemAdded:"</span>),</div>
<div style="font-family: Menlo; font-size: 11px;">
name: MyNotificationItemAdded, object: <span style="color: #35568a;">nil</span>)</div>
<div>
<span style="font-family: "courier new" , "courier" , monospace;"><br /></span></div>
<div>
<span style="font-family: inherit;">even though at runtime it will fail unless </span><span style="font-family: "courier new" , "courier" , monospace;">self</span><span style="font-family: inherit;"> has a method named </span><span style="font-family: "courier new" , "courier" , monospace;">itemAdded </span><span style="font-family: inherit;">that takes exactly one parameter (leaving off that last colon in the selector will turn this line into a no-op). Plus, this method gives you no way to take advantages of Swift's closures, which would allow the observer to access local variables in the method that adds the observer and would eliminate the need to create a dedicated method to handle the event.</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
A better way to do this is to use blocks. And <span style="font-family: "courier new" , "courier" , monospace;">NSNotificationCenter</span><span style="font-family: inherit;"> does include a block-based API:</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
<div style="font-family: Menlo; font-size: 11px;">
NSNotificationCenter.defaultCenter().addObserverForName(MyNotificationItemAdded, object: <span style="color: #35568a;">nil</span>, queue: <span style="color: #35568a;">nil</span>) { note <span style="color: #35568a;">in</span></div>
<div style="color: #cf8724; font-family: Menlo; font-size: 11px;">
<span style="color: black;"> </span>// ...</div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
</div>
<div>
<br /></div>
<div>
This is much nicer, especially with Swift's trailing closure syntax. There are no method names to be looked up at runtime, we can refer to local variables in the method that registered the observer and we can perform small bits of logic in reaction to events without having to create and name dedicated methods.</div>
<div>
<br /></div>
<div>
The catch comes in resource management. It's very important that an object remove its event observers when it's deallocated, or else <span style="font-family: "courier new" , "courier" , monospace;">NSNotificationCenter</span> will try to invoke methods on invalid pointers.</div>
<div>
<br /></div>
<div>
The traditional target-action method has the one advantage that we can easily handle this requirement with a single call in <span style="font-family: "courier new" , "courier" , monospace;">deinit</span>:</div>
<div>
<div style="color: #35568a; font-family: Menlo; font-size: 11px;">
<span style="color: black;"><br /></span>
<span style="color: black;"> </span>deinit<span style="color: black;"> {</span></div>
<div style="font-family: Menlo; font-size: 11px;">
NSNotificationCenter.defaultCenter().removeObserver(<span style="color: #35568a;">self</span>)</div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
</div>
<div>
<br /></div>
<div>
With the block API, however, since there is no explicit <span style="font-family: "courier new" , "courier" , monospace;">target</span> object, each call to <span style="font-family: "courier new" , "courier" , monospace;">addObserverForName</span><span style="font-family: inherit;"> returns "an opaque object to act as observer." So your observer class would need to track all of these objects and then remove them all from the notification center in </span><span style="font-family: "courier new" , "courier" , monospace;">deinit</span><span style="font-family: inherit;">, which is a pain.</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
<span style="font-family: inherit;">In fact, the hassle of having to do bookkeeping on the observer objects almost cancels out the convenience of using </span>the<span style="font-family: inherit;"> block API. Frustrated </span>by this situation, I sat down and created a simple helper class, <span style="font-family: "courier new" , "courier" , monospace;">NotificationManager</span>:</div>
<div>
<div style="font-family: Menlo; font-size: 11px; min-height: 13px;">
<br /></div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #35568a;">class</span> NotificationManager {</div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #35568a;">private</span> <span style="color: #35568a;">var</span> observerTokens: [<span style="color: #c35900;">AnyObject</span>] = []</div>
<div style="font-family: Menlo; font-size: 11px; min-height: 13px;">
<br /></div>
<div style="color: #35568a; font-family: Menlo; font-size: 11px;">
<span style="color: black;"> </span>deinit<span style="color: black;"> {</span></div>
<div style="font-family: Menlo; font-size: 11px;">
deregisterAll()</div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
<div style="font-family: Menlo; font-size: 11px; min-height: 13px;">
<br /></div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #35568a;">func</span> deregisterAll() {</div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #35568a;">for</span> token <span style="color: #35568a;">in</span> observerTokens {</div>
<div style="font-family: Menlo; font-size: 11px;">
NSNotificationCenter.defaultCenter().removeObserver(token)</div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
<div style="font-family: Menlo; font-size: 11px; min-height: 13px;">
<br /></div>
<div style="font-family: Menlo; font-size: 11px;">
observerTokens = []</div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
<div style="font-family: Menlo; font-size: 11px; min-height: 13px;">
<br /></div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #35568a;">func</span> registerObserver(name: <span style="color: #c35900;">String</span>, block: (<span style="color: #c35900;">NSNotification</span> -> Void)) {</div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #35568a;">let</span> newToken = NSNotificationCenter.defaultCenter().addObserverForName(name, object: <span style="color: #35568a;">nil</span>, queue: <span style="color: #35568a;">nil, usingBlock: block)</span><br />
<br /></div>
<div style="font-family: Menlo; font-size: 11px;">
observerTokens.append(newToken)</div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
<div style="font-family: Menlo; font-size: 11px; min-height: 13px;">
</div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #35568a;">func</span> registerObserver(name: <span style="color: #c35900;">String</span>, forObject object: <span style="color: #c35900;">AnyObject</span>, block: (<span style="color: #c35900;">NSNotification</span> -> Void)) {</div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #35568a;">let</span> newToken = NSNotificationCenter.defaultCenter().addObserverForName(name, object: object, queue: <span style="color: #35568a;">nil, usingBlock: </span>block)</div>
<div style="font-family: Menlo; font-size: 11px;">
</div>
<div style="font-family: Menlo; font-size: 11px;">
observerTokens.append(newToken)</div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
<div style="font-family: Menlo; font-size: 11px;">
</div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
<div style="font-family: Menlo; font-size: 11px;">
<br /></div>
First, this simple class provides a Swift-specialized API around <span style="font-family: "courier new" , "courier" , monospace;">NSNotificationCenter</span><span style="font-family: inherit;">. It provides an additional convenience method without an </span><span style="font-family: "courier new" , "courier" , monospace;">object</span><span style="font-family: inherit;"> parameter (rarely used, in my experience) to make it easier to use trailing-closure syntax. But most importantly, it keeps track of the observer objects generated when observers are registered, and removes them when the object is deinit'd.</span><br />
<span style="font-family: inherit;"><br /></span>
A client of this class can simply keep a member variable of type <span style="font-family: "courier new" , "courier" , monospace;">NotificationManager</span><span style="font-family: inherit;"> and use it to register its observers. When the parent class is deallocated, the </span><span style="font-family: "courier new" , "courier" , monospace;">deinit</span><span style="font-family: inherit;"> method will automatically be called on its </span><span style="font-family: "courier new" , "courier" , monospace;">NotificationManager</span><span style="font-family: inherit;"> member variable, and its observers will be properly </span>disposed<span style="font-family: inherit;"> of:</span><br />
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #35568a;"><br /></span>
<span style="color: #35568a;">class</span> MyController: <span style="color: #c35900;">UIViewController</span> {</div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #35568a;">private</span> <span style="color: #35568a;">let</span> notificationManager = NotificationManager()</div>
<div style="font-family: Menlo; font-size: 11px; min-height: 13px;">
</div>
<div style="color: #35568a; font-family: Menlo; font-size: 11px;">
<span style="color: black;"> </span>override<span style="color: black;"> </span>init<span style="color: black;">() {</span></div>
<div style="font-family: Menlo; font-size: 11px;">
notificationManager.registerObserver(MyNotificationItemAdded) { note <span style="color: #35568a;">in</span></div>
<div style="font-family: Menlo; font-size: 11px;">
println(<span style="color: #e82300;">"item added!"</span>)</div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
<div style="font-family: Menlo; font-size: 11px; min-height: 13px;">
</div>
<div style="color: #35568a; font-family: Menlo; font-size: 11px;">
<span style="color: black;"> </span>super<span style="color: black;">.</span>init<span style="color: black;">()</span></div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
<div style="font-family: Menlo; font-size: 11px; min-height: 13px;">
</div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #35568a;">required</span> <span style="color: #35568a;">init</span>(coder: <span style="color: #c35900;">NSCoder</span>) {</div>
<div style="color: #e82300; font-family: Menlo; font-size: 11px;">
<span style="color: black;"> fatalError(</span>"decoding not implemented"<span style="color: black;">)</span></div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
<div style="font-family: Menlo; font-size: 11px;">
<br /></div>
<div>
<span style="font-family: "times" , "times new roman" , serif; font-size: small;">When the MyController instance is deallocated, its NotificationManager member variable will be automatically deallocated, triggering the call to deregisterAll that will remove the dead objects from NSNotificationCenter.</span></div>
<div style="font-size: 11px;">
<span style="font-size: small;"><br /></span></div>
In my apps, I add a notificationManager instance to my common <span style="font-family: "courier new" , "courier" , monospace;">UIViewController</span> base class so I don't have to explicitly declare the member variable in all of my controller subclasses.<br />
<br />
Another benefit of using my own wrapper around NSNotificationCenter is that I can add useful functionality, like group observers: an observer that's triggered when any one of a group of notifications are posted:<br />
<br />
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #35568a;">struct</span> NotificationGroup {</div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #35568a;">let</span> entries: [<span style="color: #c35900;">String</span>]</div>
<div style="font-family: Menlo; font-size: 11px; min-height: 13px;">
</div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #35568a;">init</span>(<span style="color: #35568a;">_</span> newEntries: <span style="color: #c35900;">String</span>...) {</div>
<div style="font-family: Menlo; font-size: 11px;">
entries = newEntries</div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
<br />
<div style="font-family: Menlo; font-size: 11px;">
}</div>
<div style="font-family: Menlo; font-size: 11px;">
<br /></div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #35568a;">extension</span> NotificationManager {</div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #35568a;">func</span> registerGroupObserver(group: <span style="color: #c35900;">NotificationGroup</span>, block: (<span style="color: #c35900;">NSNotification</span> -> ()?)) {</div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #35568a;">for</span> name <span style="color: #35568a;">in</span> group.entries {</div>
<div style="font-family: Menlo; font-size: 11px;">
registerObserver(name, block: block)</div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
<div style="font-family: Menlo; font-size: 11px;">
</div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
<div style="font-family: Menlo; font-size: 11px;">
<br /></div>
<div style="font-size: 11px;">
<span style="font-size: small;">This can be a great way to easily set up an event handler to run when, for example, an item is changed in any way at all:</span></div>
<div style="font-size: 11px;">
<span style="font-size: small;"><br /></span></div>
<div style="font-family: Menlo; font-size: 11px;">
<span style="color: #35568a;">let</span> MyNotificationItemsChanged = NotificationGroup(</div>
<div style="font-family: Menlo; font-size: 11px;">
MyNotificationItemAdded,</div>
<div style="font-family: Menlo; font-size: 11px;">
MyNotificationItemDeleted,</div>
<div style="font-family: Menlo; font-size: 11px;">
MyNotificationItemMoved,</div>
<div style="font-family: Menlo; font-size: 11px;">
MyNotificationItemEdited</div>
<div style="font-family: Menlo; font-size: 11px;">
</div>
<div style="font-family: Menlo; font-size: 11px;">
)</div>
<div style="font-family: Menlo; font-size: 11px; min-height: 13px;">
<br /></div>
<div style="font-family: Menlo; font-size: 11px;">
notificationManager.registerGroupObserver(MyNotificationItemsChanged) { note <span style="color: #35568a;">in</span></div>
<div style="color: #cf8724; font-family: Menlo; font-size: 11px;">
<span style="color: black;"> </span>// ...</div>
<div style="font-size: 11px;">
</div>
<div style="font-family: Menlo; font-size: 11px;">
}</div>
</div>
<div>
<br /></div>
More Indirectionhttp://www.blogger.com/profile/09749479501125035513noreply@blogger.com11tag:blogger.com,1999:blog-5428232882395758357.post-65758267741452374682014-06-26T23:19:00.002-04:002014-11-29T10:27:58.051-05:00Unit Testing in SwiftSince <a href="https://developer.apple.com/swift/" target="_blank">Swift</a> was released at the beginning of the month, I've been doing using it for most of my iOS development. It's been a pleasant experience: I've been able to discard huge amounts of boilerplate and take advantage of a few functional programming techniques that were previously unavailable on the iPhone and iPad.<br />
<br />
One area where Swift has made huge improvements over Objective-C is unit tests. Objective-C's verbosity made it difficult to create small, focused classes to perform specific tasks. Plus, the language's insistence on keeping only one class to a file and the cumbersome pairing of every implementation file with a header imposed a hefty penalty on programmers who tried to divide their work up into discrete, testable components.<br />
<br />
Unit testing in Swift is done with the same XCTest framework introduced back in Xcode 5 for Objective-C. But Swift's concision and its inclusion of modern language features like closures makes XCTest much more pleasant than it was to use under Objective-C. We'll walk through a very simple example of Swift unit testing below.<br />
<br />
To get started, create an empty iOS Application project in Xcode called Counter. Xcode will generate a CounterTests folder for you and an associated test target.<br />
<br />
First, let's create a simple class to be tested. Create the file "Counter.swift" and add the following code to it:<br />
<br />
<div style="font-family: Inconsolata; font-size: 13px;">
<span style="color: #de348c;">import</span> Foundation</div>
<div style="font-family: Inconsolata; font-size: 13px; min-height: 14px;">
<br /></div>
<div style="font-family: Inconsolata; font-size: 13px;">
<span style="color: #de348c;">public class</span> Counter {</div>
<div style="font-family: Inconsolata; font-size: 13px;">
<span style="color: #de348c;">public</span> <span style="color: #de348c;">var</span> count: <span style="color: #587ea8;">Int</span></div>
<div style="font-family: Inconsolata; font-size: 13px; min-height: 14px;">
</div>
<div style="font-family: Inconsolata; font-size: 13px;">
<span style="color: #de348c;">public</span> <span style="color: #de348c;">init</span>(count: <span style="color: #587ea8;">Int</span>) {</div>
<div style="font-family: Inconsolata; font-size: 13px;">
<span style="color: #de348c;">self</span>.<span style="color: #587ea8;">count</span> = count</div>
<div style="font-family: Inconsolata; font-size: 13px;">
}</div>
<div style="font-family: Inconsolata; font-size: 13px; min-height: 14px;">
</div>
<div style="color: #de348c; font-family: Inconsolata; font-size: 13px;">
<span style="color: black;"> </span>public convenience<span style="color: black;"> </span>init<span style="color: black;">() {</span></div>
<div style="font-family: Inconsolata; font-size: 13px;">
<span style="color: #de348c;">self</span>.<span style="color: #de348c;">init</span>(count: <span style="color: #272ad8;">0</span>)</div>
<div style="font-family: Inconsolata; font-size: 13px;">
}</div>
<div style="font-family: Inconsolata; font-size: 13px; min-height: 14px;">
</div>
<div style="font-family: Inconsolata; font-size: 13px;">
<span style="color: #de348c;">public </span><span style="color: #de348c;">func</span> increment() {</div>
<div style="font-family: Inconsolata; font-size: 13px;">
<span style="color: #de348c;">self</span>.<span style="color: #587ea8;">count</span>++</div>
<div style="font-family: Inconsolata; font-size: 13px;">
}</div>
<br />
<div style="font-family: Inconsolata; font-size: 13px;">
}</div>
<br />
This is a very simple class, but it will be enough to illustrate how to use XCTest to test your own Swift code.<br />
<br />
<b>UPDATE</b>: Note that as of Xcode 6.1, any symbols that you want to be visible in your test case should be declared public so they can be seen from the test target, which is distinct from your main application target. In the above example, the class above and any of its members that need to be accessed in the test case have been declared public. Thanks to Kaan Ersan for pointing this out in the comments.<br />
<br />
Create a file called "CounterTest.swift" in the CounterTests folder Xcode generated for you (this simple test will be your "Hello, world" for Swift testing):<br />
<br />
<div style="font-family: Inconsolata; font-size: 13px;">
<span style="color: #de348c;">import</span> XCTest</div>
<div style="font-family: Inconsolata; font-size: 13px;">
<span style="color: #de348c;">import</span> Counter</div>
<div style="font-family: Inconsolata; font-size: 13px; min-height: 14px;">
<br /></div>
<div style="font-family: Inconsolata; font-size: 13px;">
<span style="color: #de348c;">class</span> CounterTest: <span style="color: #587ea8;">XCTestCase</span> {</div>
<div style="font-family: Inconsolata; font-size: 13px;">
<span style="color: #de348c;">func</span> testSimpleAddition() {</div>
<div style="font-family: Inconsolata; font-size: 13px;">
<span style="color: #de348c;">let</span> counter = Counter()</div>
<div style="font-family: Inconsolata; font-size: 13px;">
<span style="color: #587ea8;">XCTAssertEqual</span>(<span style="color: #272ad8;">0</span>, counter.count)</div>
<div style="font-family: Inconsolata; font-size: 13px;">
}</div>
<br />
<div style="font-family: Inconsolata; font-size: 13px;">
}</div>
<br />
<b>NOTE</b>:<b> </b>In the current version of Swift (Beta 2), you have to import your main target into the test target to get your tests to compile and run. This is why we <span style="font-family: Courier New, Courier, monospace;">import Counter</span> at the top.<br />
<b><br /></b>
<b>NOTE</b>: I've seen a few Swift tutorials recommend that you use the built-in Swift function <span style="font-family: Courier New, Courier, monospace;">assert</span> in your test cases - do not do this! <span style="font-family: Courier New, Courier, monospace;">assert</span> will terminate your entire program if it fails. Using the XCTAssert functions provides a number of important benefits:<br />
<br />
<ul>
<li>If one test case fails, your other cases can continue running; <span style="font-family: Courier New, Courier, monospace;">assert</span><span style="font-family: Arial, Helvetica, sans-serif;"> </span><span style="font-family: inherit;">stops the entire program.</span></li>
<li>Because the XCTAssert functions are more explicit about what you're expecting, they can print helpful failure messages (e.g. "2 was not equal to 3") whereas assert can only report that its condition was false. There's a broad variety of assert functions, including XCTAssertLessThan, XCTAssertNil, etc.</li>
<li>The Swift language specification explicitly forbids string interpolation in the message passed to assert; the XCTAssert functions don't face this limitation.</li>
</ul>
<div>
To try your test code out, click "Test" on the "Product" menu. Your single test should pass.</div>
<div>
<br /></div>
<div>
We'll add two more test cases to create and exercise several instances of <span style="font-family: Courier New, Courier, monospace;">Counter</span> and to ensure that the counter wraps around when it overflows:</div>
<div>
<br /></div>
<div>
<div style="font-family: Inconsolata; font-size: 13px;">
<span style="color: #de348c;">import</span> XCTest</div>
<div style="color: #de348c; font-family: Inconsolata; font-size: 13px;">
import<span style="color: black;"> Test</span></div>
<div style="font-family: Inconsolata; font-size: 13px; min-height: 14px;">
<br /></div>
<div style="font-family: Inconsolata; font-size: 13px;">
<span style="color: #de348c;">class</span> CounterTest: <span style="color: #587ea8;">XCTestCase</span> {</div>
<div style="font-family: Inconsolata; font-size: 13px;">
<span style="color: #de348c;">func</span> testInvariants() {</div>
<div style="font-family: Inconsolata; font-size: 13px;">
<span style="color: #de348c;">let</span> counter = <span style="color: #587ea8;">Counter</span>()</div>
<div style="color: #797979; font-family: Inconsolata; font-size: 13px;">
<span style="color: black;"> </span><span style="color: #587ea8;">XCTAssertEqual</span><span style="color: black;">(</span><span style="color: #272ad8;">0</span><span style="color: black;">, counter.</span><span style="color: #587ea8;">count</span><span style="color: black;">, </span>"Counter not initialized to 0"<span style="color: black;">)</span></div>
<div style="font-family: Inconsolata; font-size: 13px; min-height: 14px;">
</div>
<div style="font-family: Inconsolata; font-size: 13px;">
counter.<span style="color: #587ea8;">increment</span>()</div>
<div style="color: #797979; font-family: Inconsolata; font-size: 13px;">
<span style="color: black;"> </span><span style="color: #587ea8;">XCTAssertEqual</span><span style="color: black;">(</span><span style="color: #272ad8;">1</span><span style="color: black;">, counter.</span><span style="color: #587ea8;">count</span><span style="color: black;">, </span>"Increment is broken"<span style="color: black;">)</span></div>
<div style="font-family: Inconsolata; font-size: 13px; min-height: 14px;">
<br /></div>
<div style="color: #797979; font-family: Inconsolata; font-size: 13px;">
<span style="color: black;"> </span><span style="color: #587ea8;">XCTAssertEqual</span><span style="color: black;">(</span><span style="color: #272ad8;">1</span><span style="color: black;">, counter.</span><span style="color: #587ea8;">count</span><span style="color: black;">, </span>"Count has unwanted side effects!"<span style="color: black;">)</span></div>
<div style="font-family: Inconsolata; font-size: 13px;">
}</div>
<div style="font-family: Inconsolata; font-size: 13px; min-height: 14px;">
</div>
<div style="font-family: Inconsolata; font-size: 13px;">
<span style="color: #de348c;">func</span> testMultipleIncrements() {</div>
<div style="font-family: Inconsolata; font-size: 13px;">
<span style="color: #de348c;">let</span> counts = [<span style="color: #272ad8;">1</span>, <span style="color: #272ad8;">2</span>, <span style="color: #272ad8;">3</span>, <span style="color: #272ad8;">4</span>, <span style="color: #272ad8;">5</span>, <span style="color: #272ad8;">6</span>]</div>
<div style="font-family: Inconsolata; font-size: 13px; min-height: 14px;">
</div>
<div style="font-family: Inconsolata; font-size: 13px;">
<span style="color: #de348c;">for</span> count <span style="color: #de348c;">in</span> counts {</div>
<div style="font-family: Inconsolata; font-size: 13px;">
<span style="color: #de348c;">let</span> counter = <span style="color: #587ea8;">Counter</span>()</div>
<div style="font-family: Inconsolata; font-size: 13px; min-height: 14px;">
</div>
<div style="font-family: Inconsolata; font-size: 13px;">
<span style="color: #de348c;">for</span> i <span style="color: #de348c;">in</span> <span style="color: #272ad8;">0</span>..count {</div>
<div style="font-family: Inconsolata; font-size: 13px;">
counter.<span style="color: #587ea8;">increment</span>()</div>
<div style="font-family: Inconsolata; font-size: 13px;">
}</div>
<div style="font-family: Inconsolata; font-size: 13px; min-height: 14px;">
</div>
<div style="color: #797979; font-family: Inconsolata; font-size: 13px;">
<span style="color: black;"> </span><span style="color: #587ea8;">XCTAssertEqual</span><span style="color: black;">(counter.</span><span style="color: #587ea8;">count</span><span style="color: black;">, count, </span>"Incremented value does not match expected"<span style="color: black;">)</span></div>
<div style="font-family: Inconsolata; font-size: 13px;">
}</div>
<div style="font-family: Inconsolata; font-size: 13px;">
}</div>
<div style="font-family: Inconsolata; font-size: 13px; min-height: 14px;">
</div>
<div style="font-family: Inconsolata; font-size: 13px;">
<span style="color: #de348c;">func</span> testWraparound() {</div>
<div style="font-family: Inconsolata; font-size: 13px;">
<span style="color: #de348c;">let</span> counter = <span style="color: #587ea8;">Counter</span>(count: <span style="color: #587ea8;">Int</span>.max)</div>
<div style="font-family: Inconsolata; font-size: 13px;">
counter.<span style="color: #587ea8;">increment</span>()</div>
<div style="font-family: Inconsolata; font-size: 13px; min-height: 14px;">
</div>
<div style="color: #587ea8; font-family: Inconsolata; font-size: 13px;">
<span style="color: black;"> </span>XCTAssertEqual<span style="color: black;">(counter.</span>count<span style="color: black;">, </span>Int<span style="color: black;">.min)</span></div>
<div style="font-family: Inconsolata; font-size: 13px;">
}</div>
<div style="font-family: Inconsolata; font-size: 13px;">
</div>
<div style="font-family: Inconsolata; font-size: 13px;">
}</div>
</div>
<div>
<br /></div>
<div>
These tests should pass as well.<br />
<br />
You can find out more about XCTest in the Apple guide "Testing with Xcode." I hope this was helpful - please feel free to comment if anything is unclear.</div>
<div>
<br /></div>
More Indirectionhttp://www.blogger.com/profile/09749479501125035513noreply@blogger.com4tag:blogger.com,1999:blog-5428232882395758357.post-2103153168626288792013-02-11T20:31:00.001-05:002013-03-07T07:37:34.413-05:00anorm-typed: Statically-Typed SQL Queries for Scala Play Applications<span style="font-family: Helvetica Neue, Arial, Helvetica, sans-serif;">
The Play framework's default persistence framework, <a href="http://www.playframework.com/documentation/2.0.1/ScalaAnorm" target="_blank">Anorm</a>, is a very thin wrapper around JDBC (the whole library is about 800 lines of code). Although I like the idea of a framework that treats a database as a database - instead of trying to <a href="http://en.wikipedia.org/wiki/Object-relational_impedance_mismatch" target="_blank">shoehorn databases into the OO paradigm</a> - Anorm has never really appealed to me. Since it's just a wrapper around SQL, you end up writing lots of raw SQL in your application. This is a problem, because the Scala compiler and typechecker have no opportunity to check your database interaction for errors. As flawed as ORM approaches can be, at least they can generate valid SQL for you.
Consider this Anorm call from the <a href="http://www.playframework.com/documentation/2.0.1/ScalaAnorm" target="_blank">Play! documentation</a>:
<code><pre>
SQL(
"""
select * from Country c
join CountryLanguage l on l.CountryCode = c.Code
where c.code = {countryCode};
"""
).on("countryCode" -> "FRA")
</pre></code>
Here are just some of the ways this code can go wrong at runtime:<br />
<ul>
<li>A typo in an SQL keyword</li>
<li>A typo in a column or table name</li>
<li>Reference to a column or table that doesn't exist</li>
<li>A typo in the "countryCode" key passed to the "on" function</li>
<li>Passing in a non-string value for "countryCode"</li>
<li>A mismatch between the parameters named in the query string and the keys passed to "on"</li>
</ul>
With Anorm's primary competitors (<a href="http://slick.typesafe.com" target="_blank">SLICK</a> and <a href="http://squeryl.org" target="_blank">Squeryl</a>), you create mappings between columns and class fields, then use a query DSL to translate Scala Collections-like code into SQL. These frameworks are still vulnerable to some of the above problems, but they have some advantages:
<ul>
<li>You map each column only once, so if you get the column's name or type wrong, there's only one place to correct it, and then the rest of your program will be free of that particular bug.</li>
<li>These frameworks generate SQL themselves from a simple Scala DSL, so most syntax errors are ruled out.</li>
</ul>
Yet, these frameworks also introduce a number of issues:
<ul>
<li>You need to manually maintain model mappings that can drift out of sync with the database</li>
<li>The DSL's these libraries provide are necessarily limited. Some queries that would be straightforward and fast with pure SQL are simply inexpressible in these DSL's.</li>
<li>Both mappings are database-agnostic. This has obvious advantages, but if you need to take advantage of a database-specific data type, function or syntactical convenience, you're out of luck.</li>
</ul>
About a month ago, Play developer Guillaume Bort <a href="https://groups.google.com/forum/#!msg/play-framework/Si3zz0qSkZY/fy7sgca0IvUJ" target="_blank">announced</a> a proof-of-concept implementation of a statically-checked version of Anorm, Play's persistence framework (<a href="https://github.com/guillaumebort/anormtyped-demo" target="_blank">source on Github)</a>. The framework was inspired by Joni Freeman's <a href="https://github.com/jonifreeman/sqltyped" target="_blank">sqltyped</a> framework.
The main API of anorm-typed is the <code>TypedSQL</code> macro. When you compile a Scala file that contains <code>TypedSQL</code> calls, these calls are expanded into type-safe code that accepts parameters for any placeholders in the SQL and returns a tuple based on the column types selected in the query. Here's a short example:
<code>
<pre>
 // assume
// CREATE TABLE users(
// id integer,
// best_friend_id integer,
// name varchar(256)
// );
val q = TypedSQL("select * from users")
q().list() // returns List[(Int, Int, String)]
val q2 = TypedSQL("select name from users where id = ?")
q2(5).single() // returns String
val q3 = TypedSQL("update users set name = ? where id = 5")
q3("Tyrone Slothrop").execute()
</pre>
</code>
The anorm-typed module will catch every one of the errors I listed above - before your application can even compile. Note that everything here is type-checked, and that the code simply will not compile if we make a mistake matching Scala types to SQL types, if the SQL has a syntax error, if we use a nonexistent column, or if we provide the wrong number of arguments to the query. Awesome.
Of course, there are some drawbacks to this approach:<br />
<ul>
<li>The TypedSQL macro needs to connect to your database <i>during compilation</i>. This can cause a number of issues:
<ul>
<li>CI servers or other automated builds will need to be able to access a database to finish compilation</li>
<li>IDE's have no idea what to do with the TypedSQL macro - IntelliJ highlights every call as an error, even though the code compiles fine.</li>
</ul>
</li>
</ul>
Still, this is pretty close to my holy grail for database interaction. I'm planning to set aside some time to work on an alternative implementation that would suit my needs a little better: instead of a macro, I'm planning to build an SBT plugin for Play apps that would, as with the conf/routes compiler, compile a list of SQL queries into an autogenerated file.More Indirectionhttp://www.blogger.com/profile/09749479501125035513noreply@blogger.com0tag:blogger.com,1999:blog-5428232882395758357.post-16622035734875465732011-11-15T08:58:00.000-05:002011-11-15T08:58:05.897-05:00<blockquote>I should also say, there's really nothing wrong in just coding the <br />
imperative algorithm in Scala. It's not a sacrilege to use imperative code, <br />
in particular if it's inside a function. Sometimes it's the clearest way to <br />
express things. Many other times it is not. </blockquote><br />
- <a href="http://groups.google.com/group/scala-user/msg/614c82c425f2a563?hl=de">Martin Oderksy</a>More Indirectionhttp://www.blogger.com/profile/09749479501125035513noreply@blogger.com0tag:blogger.com,1999:blog-5428232882395758357.post-62697689291864814642011-08-20T18:45:00.003-04:002011-10-12T23:54:09.255-04:00The Implicit Environment Pattern<p>In general, I'm pretty skeptical of the purported benefits of test-driven development. Nevertheless, there are cases where some unit tests can be extremely helpful. Recently, I wanted to write a simple command-line utility to analyze some sales data for me. The problem was simple and well-contained enough that I thought it would make sense to write a few unit tests. </p><p>One of the perennial problems with writing good unit tests is cleanly separating out the objects being tested from their dependencies and collaborators. Most discussions of this topic limit this to separating out references to global variables or singleton objects. They typically do this with dependency injection. Conventional dependency injection is a pretty good solution to this problem, but it's often an extralingual solution, relying on XML files that are kept separate from your source code and which fall outside the purview of the type system. Even if they avoid XML files (Guice comes to mind), they're fairly heavy systems, in that you have to bring in jar's, set up your configuration, and get familiar with the API. </p><p>And aside from simply avoiding hardcoded class references, there were some other kinds of dependencies that I wanted to isolate: </p><ul><li><strong>Environment information</strong>, such as where to find configuration files, server addresses and ports, or flags</li>
<li><strong>Side effects, including I/O</strong> For example, <code>println</code> assumes the presence of a console and offers no way for you to compare what was printed to what you expected to print. Similarly, reading from the console assumes that someone is present to type in commands and provides no way for you to provide mock input. </ul><p>I eventually hit on a solution I'm calling the <strong>Implicit Environment Pattern</strong>. It makes heavy use of Scala-specific features, like traits and implicits. Unlike most dependency injection solutions, this is simply a code pattern and requires no external jar's, dependencies or configuration languages.</p><p>The key principles of this pattern are: <ul><li>Separate configuration, hardcoded class references and I/O operations into abstract traits<br />
<li>Define concrete implementations of these traits for different environments, e.g. production, testing, QA, development<br />
<li>Pass these implementations into their dependent classes via implicit parameters to the constructor<br />
<li>Dependencies are transitive - the implicit values passed into one class will be transparently passed into any other instances they create, as long as those classes declare their dependencies via implicit parameters<br />
</ul></p><p>To start with, I'll create a number of traits to declare very fine-grained dependencies: </p><pre class="prettyprint">trait WritesOutput {
def println(x: Any): Unit
}
trait ReadsSalesDirectory {
def salesDirectory: File
}
trait ReadsHistoricalRecords {
def historicalRecordsFile: File
}
trait NeedsConsoleInput {
def readLine(completionOptions: Iterable[String] = Nil): String
}
</pre><p>In this example, I've made the choice to treat the sales directory as a configuration parameter, so I can simply pass in a different directory in different environments. Depending on how much I/O is going on, I could also create a trait with a <code>readFromFiles</code> method that abstracts the I/O completely; in that case, my test implementations could just return an in-memory list from <code>readFiles</code> without touching the filesystem at all. Which of these paths you choose depends on how complicated the I/O is and on whether it's easier to provide test files or just create in-memory data structures. In this case, I have easy access to sample files, I'm pretty confident that basic line-reading code will work and I'm only reading data and not changing anything, so I've made this choice in the interest of pragmatism. </p><p>Now let's write a class that makes use of these traits via implicit parameters: </p><pre class="prettyprint">class SalesReportParser(val startingDay: LocalDate)(
implicit salesDir: ReadsSalesDirectory, writer: WritesOutput) {
...
def parseAll(): List[SalesRecord] = {
val result = withSource(Source.fromFile(salesDir.salesDirectory)) { source =>
...
}
writer.println("Parsed %d files.".format(result.count))
result
}
}</pre><p>Clients of the class will pass in its dependencies transparently via implicits. So in my main method, I simply write: </p><pre class="prettyprint">object SalesReport {
class ProdEnv extends WritesToStdout
with ReadsSalesDirectory
with ReadsBooksAndRecords
with AcceptsInput {
def println(x: Any) = Predef.println(x)
val salesDirecotry = new File(...)
val historicalRecordsFile = new File(...)
def readLine(completionOptions: Iterable[String]) = {
...
}
}
def main(args: Array[String]) {
implicit val env: ProdEnv = new ProdEnv
val parser = new SalesReportParser(new LocalDate)
parser.parseAll()
...
}
}
</pre><p>(For convenience, one class implements all of the necessary traits. In tests, I might want to create separate implementations for individual dependencies.) </p><p>The implicit value <code>env</code> in <code>main</code> is transparently passed into <code>SalesReportParser</code>'s constructor. Importantly, if <code>SalesReportParser</code> instantiates any classes that have their own dependencies declared as implicits, they'll be automatically passed in from the <code>SalesReportParser</code> instance. </p><p>If I fail to provide any of the parser's dependencies, the bug will be caught at compile time, since implicit resolution will fail when <code>SalesReportParser</code>'s constructor is called. This is what I want: it's a beautiful thing when you're able to structure your code such that certain bugs are "inexpressible," i.e. the compiler will reject them outright before they can even become bugs.</p><p>So now I've isolated the console output dependency and sales directory path from my sales report parsing logic. Further, each class specifies exactly what dependencies it needs - the SalesReportParser can't read the historical records without explicitly adding <code>ReadsHistoricalRecords</code> as an implicit parameter, so readers of this code can immediately determine how a given class depends on and affects the world around it. In effect, the SalesReportParser class acts like a function in the functional programming sense - since I provide the class with the objects that will accept its input and emit its output, it need not affect the real world unless I provide it dependencies that do so. </p><p>But the other benefit is that my unit tests become extremely straightforward: </p><pre class="prettyprint">class TestTheParser extends TestCase {
class TestEnv extends ReadsSalesDirectory with WritesToStdout {
var writtenData: List[String] = Nil
def salesDirectory = new File("test/testSalesData")
def println(x: Any) = {
writtenData ::= x
}
}
val testDate = ...
def testMe = {
implicit val env = new TestEnv
val parser = new SalesReportParser(testDate)
parser.parseAll()
env.writtenData should_== List("Parsed 10 files.")
}
}
</pre><p>The Implicit Environment pattern also allows us to create a single <code>TestEnv</code> class to be used throughout all test cases and override its behavior only when necessary. For example, our <code>TestEnv</code> class will by default accumulate output into the writtenData array, but we may want to override this in certain cases: <pre class="prettyprint">class MyTest extends TestCase {
def testMe = {
implicit val testEnvCatchesPrinting = new TestEnv {
override def println(x) = fail("should not print to stdout")
}
val parser = new SalesReportParser
parser.parseAll() // any output here will cause the test to fail
}
}
</pre><p>Another advantage of this architecture - with strict separation of I/O into explicitly specified dependencies - is that it's less likely that test code will accidentally perform operations on production data. By isolating as much I/O as is practical into dependency traits, the only way the the two could intermingle would be by intentionally passing in a production implementation into test code, or vice-versa. </p>More Indirectionhttp://www.blogger.com/profile/09749479501125035513noreply@blogger.com4tag:blogger.com,1999:blog-5428232882395758357.post-90678883065574224632011-07-09T15:38:00.001-04:002011-07-09T15:51:33.159-04:00Prefer recursion to var's and while loops<p>A while back, when I was first picking up Scala, I needed to prompt the user for a line of text and keep prompting until I got a non-blank line.</p><p>Apparently, I threw the following together:</p><pre class="prettyprint">def readCompleteLine(prompt: String) = {
val reader = new jline.ConsoleReader()
reader.setDefaultPrompt(prompt)
var line: String = null
do {
line = reader.readLine()
} while( line != null && line.length == 0 )
line
}
</pre><p>I came across this function yesterday and involuntarily made a gross-out sound. var's, mutability, a while loop (a do-while loop, no less!) and even the dread <code>null</code>. There had to be a better way.</p><p>Using higher-order functions and recursion, I rewrote this to be less imperative, more readable and more general:</p><pre class="prettyprint">def readUntil[X](prompt: String, transform: String => X, pred: X => Boolean): X = {
val reader = new jline.ConsoleReader() { setDefaultPrompt(prompt)}
@tailrec def ruHelper: X = Option(reader.readLine()).map(transform(_)) match {
case Some(x) if pred(x) => x
case _ => ruHelper
}
ruHelper
}
/* simple, string-only version */
def readUntil(prompt: String, pred: String => Boolean): String = {
readUntil(prompt, x => x, pred)
}
</pre><p>Using this as a primitive, it's easy to write a method that repeatedly prompts the user for a string until it's non-empty.</p><pre class="prettyprint">def readUntilNonBlankLine(prompt: String) = readUntil(prompt, _.length > 0)
</pre><p>But it's also easy to create a method that only allows input from a list of accepted strings:</p><pre class="prettyprint">def readOneOf(prompt: String, acceptableValues: List[String]): String =
readUntil(prompt, acceptableValues.contains(_))
</pre><p>Or even a function that reads until the value is a properly-formed floating-point number:</p><pre class="prettyprint">import util.control.Exception._
def tryToReadDouble(s: String) =
catching(classOf[NumberFormatException]) opt (s.toDouble)
def readUntilValidDouble(prompt: String) = ReadingUtils.readUntil(prompt,
tryToReadDouble(_), (s: Option[Double]) => s.isDefined).get
</pre><p>Higher-order functions and genericity can really pay off - the base <code>readUntil</code><br />
is now a nicely abstracted primitive that I can test in isolation and then plug in elsewhere in my program, without having to write and rewrite error-prone imperative code.</p>More Indirectionhttp://www.blogger.com/profile/09749479501125035513noreply@blogger.com0tag:blogger.com,1999:blog-5428232882395758357.post-77117216570405830852011-06-08T15:25:00.009-04:002011-07-14T09:10:09.380-04:00Migrating to sbt 0.10: sbt-idea-plugin<p>In pre-0.10 versions of sbt, you could use sbt-idea-plugin to generate IDEA projects from your SBT files. That plugin doesn't work in 0.10, but here's how to make it happen:<br />
<p>Create a new file <code>~/.sbt/plugins/project/Build.scala</code> and put the following code in it: </p><pre class="prettyprint">import sbt._
object MyPlugins extends Build {
lazy val root = Project("root", file("."))
dependsOn (uri("git://github.com/ijuma/sbt-idea.git#sbt-0.10"))
}</pre><p>Now restart SBT and type <code>gen-idea</code> (this command replaces the old <code>idea</code>). SBT 0.10 includes any code in this directory in all projects, so any other sbt-0.10 project will immediately get this functionality.<br />
</p><p>The initial project created by <code>idea-gen</code> will have a separate IDEA module for the <code>project</code> folder. I haven't been able to get IDEA to compile anything while this module is there, so I remove the module and then mark the project as excluded in the main module.<br />
</p><p>Note that if you later make a change to your .sbt file (e.g. adding a new dependency), you need to bring the IDEA project up-to-date. To do this, open the SBT Console in IDEA, run "update" and then run "gen-idea". Your IDEA project will be brought up to date and IDEA will prompt you to reload the project. Choose 'OK' and you're good to go.<br />
</p><p>(thanks to <a href="http://groups.google.com/group/simple-build-tool/msg/3f6616672b0246db">Graham Tackley</a> for figuring out the proper way to do this) </p><p><b>Update</b>: Updated the path to <code>~/.sbt/plugins/project/Build.scala</code>, as it's not necessary to configure the IDEA plugin on a per-project basis.More Indirectionhttp://www.blogger.com/profile/09749479501125035513noreply@blogger.com4tag:blogger.com,1999:blog-5428232882395758357.post-24291174145137962672011-06-08T15:01:00.004-04:002011-06-09T12:39:46.648-04:00Migrating to sbt 0.10: Lift<p>
The latest release of sbt (version 0.10) is utterly incompatible with build scripts from older versions of SBT. Here's how to migrate a Lift project to SBT 0.10 (these instructions work with 2.4-M1; I believe you can use earlier versions of lift, as long as you downgrade <code>scalaVersion</code> to 2.8.1):<br />
</p>
<ol>
<li>Rename <code>project/</code> to <code>project-old</code>/</li>
<li>Create a new <code>build.sbt</code> file in the root of your project<br />
<br />
<pre class="prettyprint">name := "projname"
scalaVersion := "2.9.0"
seq(WebPlugin.webSettings: _*)
libraryDependencies ++= Seq(
"net.liftweb" %% "lift-webkit" % "2.4-M1" % "compile->default",
"net.liftweb" %% "lift-mapper" % "2.4-M1" % "compile->default",
"net.liftweb" %% "lift-wizard" % "2.4-M1" % "compile->default")
libraryDependencies ++= Seq(
"junit" % "junit" % "4.5" % "test->default",
"org.mortbay.jetty" % "jetty" % "6.1.22" % "jetty",
"javax.servlet" % "servlet-api" % "2.5" % "provided->default",
"com.h2database" % "h2" % "1.2.138",
"ch.qos.logback" % "logback-classic" % "0.9.26" % "compile->default"
)</pre></li>
<li><code>mkdir -p project/plugins</code></li>
<li>Create a new file <code>project/plugins/build.sbt</code> and put the following in it:<br />
<pre class="prettyprint">resolvers += "Web plugin repo" at "http://siasia.github.com/maven2"
libraryDependencies <+= sbtVersion("com.github.siasia" %% "xsbt-web-plugin" % _)</pre>
</ol>
<p>
Now you're good to go.
</p>
<p>
Note that the Jetty dependency has changed. If you execute jetty-run and no output appears, make sure you changed the scope for the Jetty dependency from "test" (the correct value in older SBT's) to "jetty".<br />
</p>
<p>
Also note that the new SBT parser appears to be newline-sensitive; make sure you keep blank lines between each setting, as in the example above.
</p>More Indirectionhttp://www.blogger.com/profile/09749479501125035513noreply@blogger.com5tag:blogger.com,1999:blog-5428232882395758357.post-56152358825126721382011-02-23T17:14:00.003-05:002011-06-09T12:25:14.128-04:00I want to go to thereFrom the excellent <a href="http://lamp.epfl.ch/~imaier/pub/DeprecatingObserversTR2010.pdf">Deprecating the Observer Pattern</a> by Odersky et al:<br />
<br />
<pre class="prettyprint">// step 1:
val path = new Path((self next mouseDown).position)
// step 2:
self loopUntil mouseUp {
val m = self next mouseMove
path.lineTo(m.position)
draw(path)
}
// step 3:
path.close()
draw(path)
</pre><br />
This is a complete implementation of a sketching application using scala-react.More Indirectionhttp://www.blogger.com/profile/09749479501125035513noreply@blogger.com0tag:blogger.com,1999:blog-5428232882395758357.post-69038825482593752562011-01-21T15:12:00.000-05:002011-05-28T19:57:55.683-04:00Thoughts on node.js and HaskellI've been hearing chatter about <a href="http://nodejs.org/">node.js</a> in the last few months. A few weeks ago, a colleague gave me a quick demo and I decided to take a closer look. I was impressed with how much scale it could handle without batting an eye, and I was especially impressed with the claim (wish I could find where I saw it) that you could replace a cluster of five or six thread-based web servers with a single node.js process and see comparable performance.<br />
<br />
Many web servers (e.g. Tomcat, Apache) dedicate a thread to every open connection. When a web request comes in, a thread is either created fresh or, if possible, dequeued from a thread pool. The thread performs some computation and I/O, and the thread scheduler puts it to sleep when it blocks for I/O or when it exceeds its quantum. This works well enough, but as more and more threads are created to handle simultaneous connections, the paradigm starts to break down: each new thread adds some overhead, and a large enough number of simultaneous connections will grind the server to a halt.<br />
<br />
node.js is a JavaScript environment built from the ground up to use asynchronous I/O. Like other JavaScript implementations it has a single main thread. It achieves concurrency through an API that requires you to provide a callback (a closure) specifying what should happen when any I/O operation completes. Instead of using threads or blocking, node.js uses low-level event primitives to register for I/O notifications. The overhead of context switching and thread tracking is replaced by a simple queue of callbacks. As the events come in, node.js invokes the corresponding callback. The callback then carries on its work until it either begins another I/O request or completes its task, at which point node handles the next event or sleeps while waiting for a new one. Here's a simple web-based "Hello, world!" in node.js from the snap-benchmarks repository:<br />
<br />
<script src="https://gist.github.com/728322.js"> </script><br />
<br />
As you can see, all I/O operations accept a callback as their final argument. Instead of a series of calls that happen to block, we explicitly handle each case where an I/O request completes, giving us code consisting of nested callbacks.<br />
<br />
In addition to reducing the strain placed on a system by a large number of threads, this approach also removes the need for thread synchronization - everything is happening in rapid sequence, rather than in parallel, so race conditions and deadlock are not a concern. The disadvantages are that any runaway CPU computation will hose your server (there's only one thread, after all) and that you must write your code in an extremely unnatural style to get it to work at all. The slogan of this blog used to be a famous Olin Shivers quote: "I object to doing things computers can do" - and explicitly handling I/O responses certainly falls into the category of Things a Computer Should Do.<br />
<br />
Anyway, this got me thinking about how Haskell could take advantage of the asynchronous I/O paradigm. I was delighted to find that others were thinking the same thing. As it happens, the new I/O manager in GHC 7 uses async I/O behind the scenes - so your existing code with blocking calls automatically takes advantage of the better scalability of asynchronous I/O. The Snap framework has published benchmarks that show it handling about 150% more requests per second than node.js:<br />
<br />
<a href="http://snapframework.com/blog/2010/11/17/snap-0.3-benchmarks" target="_blank"><img src="http://snapframework.com/media/img/pong-bench-20101117.png" /></a><br />
<br />
But what's even nicer is that there's no need to contort your code to get this performance. Snap and the underlying GHC I/O manager completely abstract away the asynchronous nature of what I'm doing. I don't need to set callbacks because the I/O manager is handling that under the covers. I pretend I'm using blocking I/O and the I/O manager takes care of everything else. I get some defense against long-running computations (because more than one thread is processing the incoming events) and, most importantly, I can write code in what I consider a saner style.<br />
<br />
The catch is that most I/O libraries are not designed for asynchronous I/O. So, network and other I/O calls made at the Haskell level will work appropriately, but external libraries (say, MySQL) are often written with blocking I/O at the C level, which can negate some of the scalability gains from asynchronous I/O.More Indirectionhttp://www.blogger.com/profile/09749479501125035513noreply@blogger.com7tag:blogger.com,1999:blog-5428232882395758357.post-45100938898615884712010-12-09T19:41:00.001-05:002011-05-28T19:57:47.457-04:00parallel-io<p>
A few days ago, <a href="http://hackage.haskell.org/package/parallel-io">parallel-io</a> was uploaded to Hackage. Previously, when I wanted to perform a number of I/O actions in parallel, I used some homegrown code that used <code>forkIO</code> and <code>MVar</code>'s to run a bunch of I/O actions in parallel and collect their result.<p>
The new package makes all that unnecessary. Its two most important functions functions are:
<pre><code> parallel_ :: [IO a] -> IO () -- ignores results
parallel :: [IO a] -> IO [a] -- collects results into a list</code></pre>
I have some code that fetches a number of exchange rates in relation to the US dollar. I want to do this work in parallel, so previously I used my own combinators. Now it's as simple as:
<pre><code> getExchangeRateToUSD :: String -> IO Double
getExchangeRateToUSD = ...
parallel $ map getExchangeRateToUSD ["CAD", "EUR", "JPY", "GBP"]</code></pre>
The map call returns a list of IO actions. parallel-io dutifully forks off a thread for each of these actions and returns a value of type <code>IO [Double]</code>.
Nice and simple.More Indirectionhttp://www.blogger.com/profile/09749479501125035513noreply@blogger.com0tag:blogger.com,1999:blog-5428232882395758357.post-14491169825222302632010-09-12T22:29:00.000-04:002011-01-22T10:49:41.042-05:00Getting started with LeksahI've been wanting to try Leksah for some time, but found that it doesn't give you many cues about how to get started when you first run it. Today, I sat down and got a simple example running.<br />
<br />
Here's how to get a simple Haskell program running in Leksah:<br />
<br />
<ol><li>Open Leksah. If this is the first time you're running it, just hit OK when asked for search paths and wait for a bit (possibly quite a bit) while it indexes your installed packages.</li>
<li>Click "Workspace | New Workspace" and specify a location for the Leksah workspace, which is a single file that will track references to the Leksah projects you'll create.</li>
<li>Click Module | New Module. This creates a new Cabal module under your workspace. The New Module dialog is looking for a folder which will hold the Cabal files. Create a new directory, navigate to it, and press Open.</li>
<li>Press the Save button at the bottom of the Package tab.</li>
<li>Write your code within the module you created. Note that Leksah will complete function names as you type. It will also repeatedly compile your package and show the output in the Log window to alert you to errors.</li>
<li>Use the package menu to run your code or to install it via cabal. Your output will appear in the Log pane.</li>
</ol><br />
Hope that's helpful!More Indirectionhttp://www.blogger.com/profile/09749479501125035513noreply@blogger.com1tag:blogger.com,1999:blog-5428232882395758357.post-16534457528934861162010-08-24T23:36:00.001-04:002011-01-22T10:49:41.043-05:00Haskell Syntax Highlighting for BlogsCan anyone recommend a good way to get Haskell syntax highlighting for blog posts? I've been using Gist, which works very well, except that Gists don't show up in RSS feeds.More Indirectionhttp://www.blogger.com/profile/09749479501125035513noreply@blogger.com11tag:blogger.com,1999:blog-5428232882395758357.post-83805310820461792722010-08-23T07:20:00.001-04:002011-01-22T10:49:41.043-05:00Planet HaskellI give permission to include this blog and its contents on the Planet Haskell aggregator.More Indirectionhttp://www.blogger.com/profile/09749479501125035513noreply@blogger.com0tag:blogger.com,1999:blog-5428232882395758357.post-19377343138206195602010-08-21T21:10:00.000-04:002010-08-22T09:21:17.718-04:00Word Wrapping in HaskellPrompted by a question on haskell-cafe, here's a relatively concise word-wrapping implementation I wrote:<br />
<br />
<b>Update</b>: Thanks to Yitz in the comments for pointing out that "any isSpace line" should be "any isSpace beforeMax"<br />
<br />
<script src="http://gist.github.com/524460.js">
</script>More Indirectionhttp://www.blogger.com/profile/09749479501125035513noreply@blogger.com3tag:blogger.com,1999:blog-5428232882395758357.post-62702833480336909222010-07-25T12:52:00.000-04:002011-01-22T10:49:41.044-05:00cabal unpackIf you want to browse the source code of a package on Hackage, "cabal unpack" is a useful command.<br />
<br />
<pre>packages % ls
packages % cabal unpack web-routes-quasi
Unpacking to web-routes-quasi-0.5.0/
packages % ls
web-routes-quasi-0.5.0
packages %
</pre><br />
Just pass it the name of a package and you'll get an unzipped tarball in your current directory. Very useful.More Indirectionhttp://www.blogger.com/profile/09749479501125035513noreply@blogger.com1tag:blogger.com,1999:blog-5428232882395758357.post-32067391771930071782010-05-27T21:21:00.000-04:002011-01-22T10:49:41.044-05:00readFile and lazy I/ORecently, I came across a problem in a Haskell script I run frequently. Every so often, I drop a report file into a designated folder. Then I run my script, which peforms an operation like the following.<br />
<br />
<code>getReportFiles >>= map readFile</code><br />
<br />
This worked fine for months - until this morning, when the program crashed with an error indicating that too many files had been opened.<br />
<br />
The problem is that readFile uses lazy I/O. Generally, when we write code like getLine >>= putStrLn, we expect these calls to happen in order - indeed, that's one of the primary purposes of the IO monad. But readFile uses hGetContents internally, which is an exception to the strict I/O found elsewhere in Haskell. So readFile opens the file and then returns a thunk instead of actually reading the file. Only when the thunk is evaluated is the I/O performed and the file read into memory. And only when the thunk has been fully evaluated will the open file be closed.<br />
<br />
So in my snippet, I was reading in hundreds of files as thunks. and until the full contents of the thunks were evaluated, the files all remained open. This was no problem until the number of reports I had to process reached a certain point and exposed the bug.<br />
<br />
<strike>The solution in my case was to use Data.ByteString:<br />
<br />
<code>import qualified Data.ByteString.Char8 as BS<br />
<br />
eagerReadFile :: FilePath -> IO String<br />
eagerReadFile file = BS.unpack <$> BS.readFile file</code><br />
<br />
ByteString's readFile method is eager, so you'll get back the complete file contents.</strike><br />
<br />
<b>Update</b>: In the comments, Chris points out System.IO.Strict, which has a strict readFile function that simply replaces Prelude.readFile.<br />
<br />
Lazy I/O can be very useful: instead of reading in the complete contents of a large file, you can read it lazily using a function in the hGetContents family and then process it without having to read the entire contents into memory at once. But lazy I/O can surprise you if you're not expecting it.<br />
<br />
(thanks to #haskell for pointing out the eager Data.ByteString.Char8.readFile)More Indirectionhttp://www.blogger.com/profile/09749479501125035513noreply@blogger.com3tag:blogger.com,1999:blog-5428232882395758357.post-88836246652819428402010-04-23T18:54:00.000-04:002011-01-22T10:49:41.044-05:00The Brilliance of Maybe and the Value of Static Type CheckingI am sometimes surprised by the strokes of genius hidden inside Haskell's type system. For example: the Maybe type.<br />
<br />
Maybe a is a parameterized type that can hold either the value Nothing, or a value Just a . Seems simple enough - it's an optional value.<br />
<br />
But it goes deeper than that. Look at an object of type String in Java. This object's type is actually larger than String - it can either represent a string of characters or the special value null. Languages like Java, Ruby, and C# all make the mistake of allowing any type at all to hold null. This greatly diminishes the value of static typing, since any call of the form string.length() will sail happily past the compiler and then throw the dread NullPointerException at runtime if passed a null. The compiler will never see it coming, and your program could work for some time before you discover the problem.<br />
<br />
But in Haskell, we must be explicit. By default, an object of type Integer can only hold an integral value - there is no null, and no possibility of a runtime null pointer exception. The Haskell compiler will not even produce a program if you try to pass Nothing into a non-Maybe type. A datatype cannot be instantiated with null values unless you explicitly allow it to hold them.<br />
<br />
This eliminates an enormous class of errors. Anyone who has maintained a large Java application instinctively shudders at the prospect of an NPE - the value could have become null at any point in the program and resolving it requires extensive tracing.<br />
<br />
Further, in order to avoid the possibility of an NPE, you must litter your code with distracting and unnecessary conditionals like:<br />
<br />
<code>if( foo == null ) throw new IllegalArgumentException( ..<br />
if( foo.bar() == null ) throw new IllegalArgumentException( ... );</code><br />
<br />
before doing any real work. But what's worse is that you're turning a programming error into a runtime exception. The fact that your program compiles does not guarantee that you will have no NPE's and running it does not guarantee that you will hit every branch that might generate one.<br />
<br />
In Haskell, it is simply impossible to write a program that is susceptible to NullPointerExceptions at runtime unless you explicitly make the type in question optional by wrapping with Maybe.More Indirectionhttp://www.blogger.com/profile/09749479501125035513noreply@blogger.com10