tag:blogger.com,1999:blog-32736710872417265932023-06-20T15:18:04.640+02:00Tim Scheffler's blogTim Schefflerhttp://www.blogger.com/profile/00287780663826424312noreply@blogger.comBlogger5125tag:blogger.com,1999:blog-3273671087241726593.post-7214879847185706892010-03-05T15:32:00.001+01:002010-03-05T15:33:42.841+01:00A Cocoa application (almost) completely implemented in Haskell<div id="a-cocoa-application-almost-completely-implemented-in-haskell"
><!-- Abstract --><br />
<p
>This is the third part of a small series of articles<sup
><a href="#fn1" class="footnoteRef" id="fnref1"
>1</a
></sup
> on how to use Haskell for the development of Cocoa applications. In this post I describe a simple way how to implement a Cocoa controller in Haskell. This way one can define the application’s model layer completely in Haskell without any reference to the underlying Cocoa framework (or Objective-C classes).</p><div id="recap-OBJC-instances"
><h2
>Recap: <code
>OBJC</code
> Instances</h2><p
>Since the <a href="http://tscheff.blogspot.com/2010/02/small-haskell-objective-c-interface.html"
>last blog entry</a
> I created a few new instances of <code
>OBJC</code
> and changed some of the old ones. Here is an overview of the current implementation:</p><table
><tr class="header"
><th align="left"
>Data Structure</th
><th align="left"
>Haskell Representation</th
><th align="left"
>Objective-C Representation</th
><th align="left"
>Remarks</th
></tr>
<tr class="odd"
><td align="left"
>Opaque value 1</td
><td align="left"
><code
>StableId</code
></td
><td align="left"
><code
>NSObject</code
></td
><td align="left"
>Value created in Objective-C</td
></tr>
<tr class="even"
><td align="left"
>Opaque value 2</td
><td align="left"
><code
>StableValue</code
></td
><td align="left"
><code
>HSValue</code
></td
><td align="left"
>Value created in Haskell</td
></tr>
<tr class="odd"
><td align="left"
>“No Value”</td
><td align="left"
><code
>()</code
></td
><td align="left"
><code
>void</code
></td
><td align="left"
></td
></tr>
<tr class="even"
><td align="left"
>Scalar number</td
><td align="left"
><code
>Int</code
>, <code
>Double</code
>,…</td
><td align="left"
>C types <code
>int</code
>, <code
>double</code
>, …</td
><td align="left"
>Conversion by FFI</td
></tr>
<tr class="odd"
><td align="left"
>1st class number</td
><td align="left"
><code
>Int</code
>, <code
>Double</code
></td
><td align="left"
><code
>NSNumber</code
></td
><td align="left"
></td
></tr>
<tr class="even"
><td align="left"
>Boolean</td
><td align="left"
><code
>Bool</code
></td
><td align="left"
><code
>NSNumber</code
></td
><td align="left"
></td
></tr>
<tr class="odd"
><td align="left"
>String</td
><td align="left"
><span style="text-decoration: line-through;"
><code
>String</code
>,</span
> <code
>Text</code
>, <code
>ByteString</code
></td
><td align="left"
><code
>NSString</code
></td
><td align="left"
></td
></tr>
<tr class="even"
><td align="left"
>Container</td
><td align="left"
>List, tuple</td
><td align="left"
><code
>NSArray</code
></td
><td align="left"
></td
></tr>
<tr class="odd"
><td align="left"
>Dictionary</td
><td align="left"
><code
>Map k a</code
></td
><td align="left"
><code
>NSDictionary</code
></td
><td align="left"
></td
></tr>
</table><p
>Opaque values are treated differently based on the runtime in which the values have been created: If an opaque <code
>NSObject</code
> value was created in the Objective-C runtime it will be available in Haskell as a value of type <code
>StableId</code
>. Analogically, an arbitrary Haskell value can be wrapped as a <code
>StableValue</code
> and exported to Objective-C. In Objective-C this Haskell value is then available as an object of the <code
>HSValue</code
> class. This is just a proxy for the <a href="http://www.haskell.org/haskellwiki/GHC/Using_the_FFI"
>FFI</a
> <code
>StablePtr</code
>: the implementation of the <code
>HSValue</code
> class deals with all the creating and freeing of the <code
>StablePtr</code
> such that the user does not need to bother with this.</p><p
>This special treatment of opaque values is necessary exactly because they are <em
>opaque</em
>, meaning that neither the Haskell nor the Objective-C runtime can build native representations of values that were initially created in the foreign runtime. Therefore they have to be wrapped in the target runtime. For all the other data structures the <code
>OBJC</code
> typeclass has enough information to construct new native representations from the original values in the target runtime.</p><p
>Function or method return types of <code
>void</code
> are now translated to the <code
>()</code
> Haskell type. This has not been tested that much, but right now it works and it is used for the target actions of Cocoa’s <code
>NSControl</code
> view elements.</p><p
>I removed the instance declaration for the <code
>String</code
> Haskell type. This is just because I was getting all kinds of problems as <code
>String</code
> is just a type synonym for <code
>[Char]</code
> and this means that the instance definition for <code
>String</code
> overlaps with the instance definition for lists <code
>[a]</code
>. This problem is well known in Haskell and one can deal with it via GHC switches, like <code
>OverlappingInstances</code
>. But in the process of extending the <code
>OBJC</code
> typeclass I ran into more and more issues because of this. So I decided to drop the instance definition for <code
>String</code
> again. After all, converting <code
>String</code
>s from and to <code
>Text</code
> values is quite easy.</p><p
>A very similar problem is the Haskell representation of dictionaries. The most fundamental way would be to just translate <code
>NSDictionary</code
>s to Haskell key-value lists <code
>[(k, a)]</code
>, but this once again would collide with the instance for lists. So I decided to use <code
>Map</code
> as the Haskell representation for an <code
>NSDictionary</code
>.</p></div><div id="next-up-functions"
><h2
>Next up: functions</h2><p
>The way to export Haskell functions to Objective-C as presented in the last posts was quite tedious:</p><ol style="list-style-type: decimal;"
><li
><p
>Wrap a Haskell function for accepting <code
>Id</code
>s by usage of <code
>fromId</code
> and <code
>toId</code
>.</p></li>
<li
><p
>Export each function individually to C via the FFI.</p></li>
<li
><p
>Provide a dummy C-implementation of each function in the Objective-C project.</p></li>
</ol><p
>But as functions are first-class values in Haskell there must be a better way to export them to Objective-C!</p><p
>To make the exporting of Haskell functions easy we will define them as instances of the <code
>OBJC</code
> typeclass. As Haskell functions have no direct counterpart in Objective-C they will be similar to <code
>StableValue</code
>s. Accordingly a Haskell function of one argument will be represented in Objective-C by an object of the class <code
>HSFunc1</code
>, which is a subclass of <code
>HSValue</code
>.<sup
><a href="#fn2" class="footnoteRef" id="fnref2"
>2</a
></sup
></p><p
>The Haskell part is given as:</p><pre class="sourceCode haskell"
><code
><span class="Keyword"
>instance</span
><span class="Normal NormalText"
> (OBJC a, OBJC b) => OBJC (a -> IOOBJC b) </span
><span class="Keyword"
>where</span
>
<span class="Normal NormalText"
> toId f = newHSValue </span
><span class="String"
>"HSFunc1"</span
><span class="Normal NormalText"
> f'</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>where</span
><span class="Normal NormalText"
> </span
>
<span class="Normal NormalText"
> f' :: Id -> IOOBJC Id</span
>
<span class="Normal NormalText"
> f' x' = toId =<< f =<< fromId x'</span
>
<span class="Normal NormalText"
> fromId = </span
><span class="Function"
>undefined</span
>
</code
></pre><p
>Instances of <code
>OBJC</code
> are defined for a function that has a “wrappable” argument <code
>OBJC a</code
> and a wrappable result type <code
>OBJC b</code
>. These functions shall be executed in the <code
>IOOBJC</code
> monad.</p><p
>Firstly we wrap the complete function yielding a new function <code
>f'</code
> that has the type <code
>Id -> IOOBJC Id</code
>. Then we export <code
>f'</code
> to Objective-C via <code
>newHSValue</code
> which is defined as:</p><pre class="sourceCode haskell"
><code
><span class="Function FunctionDefinition"
>newHSValue ::</span
><span class="Normal NormalText"
> </span
><span class="DataType TypeConstructor"
>String</span
><span class="Normal NormalText"
> -> a -> IOOBJC Id</span
>
<span class="Normal NormalText"
>newHSValue className val = checkNullPtr (</span
><span class="String"
>"Could not create "</span
><span class="Normal NormalText"
> ++ className ++ </span
><span class="String"
>" object"</span
><span class="Normal NormalText"
>) $</span
>
<span class="Normal NormalText"
> BS.useAsCString (BS8.pack className) $ \cstr -></span
>
<span class="Normal NormalText"
> c_newHSValue cstr =<< newStablePtr val</span
>
</code
></pre><p
>the corresponding C-part is:</p><pre class="sourceCode objectivec"
><code
><span class="Normal NormalText"
>id newHSValue</span
><span class="Normal Symbol"
>(</span
><span class="DataType DataType"
>const</span
><span class="Normal NormalText"
> </span
><span class="DataType DataType"
>char</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
>name</span
><span class="Normal Symbol"
>,</span
><span class="Normal NormalText"
> HsStablePtr value</span
><span class="Normal Symbol"
>)</span
>
<span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> Class aClass </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>Class</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
>objc_getClass</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>name</span
><span class="Normal Symbol"
>);</span
>
<span class="Normal NormalText"
> id funcObj </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[[</span
><span class="Normal NormalText"
>aClass alloc</span
><span class="Normal Symbol"
>]</span
><span class="Normal NormalText"
> initWithHaskellValue</span
><span class="Normal Symbol"
>:</span
><span class="Normal NormalText"
>value</span
><span class="Normal Symbol"
>];</span
>
<span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>funcObj autorelease</span
><span class="Normal Symbol"
>];</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>return</span
><span class="Normal NormalText"
> funcObj</span
><span class="Normal Symbol"
>;</span
>
<span class="Normal Symbol"
>}</span
>
</code
></pre><p
>As we see the <code
>toId</code
> function creates an (autoreleased) <code
>HSFunc1</code
> object that handles the new <code
>StablePtr</code
> which was created by <code
>newHSValue</code
>.</p><p
><code
>HSFunc1</code
> now provides a simple way for Objective-C to call the original Haskell function. This is done by the method <code
>callWithArg:</code
>:</p><pre class="sourceCode objectivec"
><code
><span class="Normal Symbol"
>-(</span
><span class="Normal NormalText"
>id</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
>callWithArg</span
><span class="Normal Symbol"
>:(</span
><span class="Normal NormalText"
>id</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
>arg1</span
><span class="Normal Symbol"
>;</span
>
<span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>return</span
><span class="Normal NormalText"
> callFunc1</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>hsValue</span
><span class="Normal Symbol"
>,</span
><span class="Normal NormalText"
> arg1</span
><span class="Normal Symbol"
>);</span
>
<span class="Normal Symbol"
>}</span
>
</code
></pre><p
>and the Haskell implementation of <code
>callFunc1</code
>:</p><pre class="sourceCode haskell"
><code
><span class="Function FunctionDefinition"
>callFunc1 ::</span
><span class="Normal NormalText"
> (StablePtr (Id -> IOOBJC Id)) -> Id -> </span
><span class="DataType TypeConstructor"
>IO</span
><span class="Normal NormalText"
> Id</span
>
<span class="Normal NormalText"
>callFunc1 fPtr arg = catchOBJC $ </span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>do</span
><span class="Normal NormalText"
> f <- liftIO $ deRefStablePtr fPtr</span
>
<span class="Normal NormalText"
> f arg</span
>
</code
></pre><p
>With these definitions we can retrieve Haskell functions from Objective-C very easily and call them with Objective-C arguments getting back Objective-C values as results, for example:</p><pre class="sourceCode objectivec"
><code
><span class="Normal NormalText"
>NSArray </span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
>results </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>getMethod</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>controller</span
><span class="Normal Symbol"
>,</span
><span class="Normal NormalText"
> </span
><span class="String"
>@"lengthOfStrings"</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
> callWithArg</span
><span class="Normal Symbol"
>:</span
><span class="Normal NormalText"
>inputArray</span
><span class="Normal Symbol"
>];</span
>
</code
></pre><p
>Here <code
>controller</code
> is a Haskell controller (wrapped in an <code
>HSValue</code
>) and the function <code
>getMethod</code
> returns the Haskell function <code
>lengthOfStrings</code
> wrapped as an <code
>HSFunc1</code
> value which now can be called with <code
>callWithArg:</code
>.</p><p
>I have not yet implemented the <code
>fromId</code
> part of the function instance of <code
>OBJC</code
>. This is just because there was no need for this backward conversion, but the implementation should be quite straightforward.</p></div><div id="towards-a-haskell-controller"
><h2
>Towards a Haskell Controller</h2><p
>With the new instances of <code
>OBJC</code
> for wrapping arbitrary Haskell values and functions it is now possible to build a real Haskell controller.</p><p
>The connection of the Haskell controller to the view part of a Cocoa application will be managed by a Objective-C controller proxy layer. This controller proxy will interact with the Haskell controller via a small C interface:</p><ul
><li
><p
><code
>HSValue *initController(NSDictionary *ivars)</code
>: This creates the Haskell controller value and returns it as an <code
>HSValue</code
> to Objective-C. The dictionary <code
>ivars</code
> contains the data members of the calling Objective-C controller proxy by name. It can be used by the Haskell controller to access other Objective-C objects, like e.g. the view elements (see below!).</p></li>
<li
><p
><code
>NSArray *getMethodNames(HSValue *controller)</code
>: Returns the names of all exposed “methods” of the Haskell controller.</p></li>
<li
><p
><code
>id getMethod(HSValue *controller, NSString *methodName)</code
>: Returns the controller method for a given name. This will be an object of the class <code
>HSFunc1</code
>, <code
>HSFunc2</code
>, …</p></li>
</ul><p
>These are the only functions the Haskell controller has to provide in order to communicate with the Objective-C part of the application.</p><p
>With this interface a typical way to implement a Cocoa controller in Haskell would be as follows:</p><ol style="list-style-type: decimal;"
><li
><p
>Define a proxy Objective-C controller and bind all necessary view elements (and other needed Objective-C objects) to it by using <code
>IBOutlet</code
>s in the “Interface Builder”.</p></li>
<li
><p
>Initialize the Haskell controller in the <code
>awakeFromNib</code
> method of the Objective-C controller proxy. The bound <code
>IBOutlet</code
>s are provided to the Haskell controller by the <code
>ivars</code
> dictionary.<sup
><a href="#fn3" class="footnoteRef" id="fnref3"
>3</a
></sup
></p></li>
<li
><p
>Bind Haskell functions to the view elements.</p></li>
</ol><p
>The last step now looks something like this:</p><pre class="sourceCode haskell"
><code
><span class="String"
>"numberSlider"</span
><span class="Normal NormalText"
> </span
><span class="Others InfixOperator"
>`connect`</span
><span class="Normal NormalText"
> presentNumbers</span
>
</code
></pre><p
>This connects the <code
>target</code
> and <code
>action</code
> of the <code
>numberSlider</code
> view element to a Haskell function <code
>presentNumbers</code
>. It is of type <code
>Action</code
><sup
><a href="#fn4" class="footnoteRef" id="fnref4"
>4</a
></sup
> and given as:</p><pre class="sourceCode haskell"
><code
><span class="Function FunctionDefinition"
>presentNumbers ::</span
><span class="Normal NormalText"
> Action</span
>
<span class="Normal NormalText"
>presentNumbers sender =</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>do</span
><span class="Normal NormalText"
> val <- sender # objectValue </span
><span class="Comment"
>-- luckily "objectValue" delivers a NSNumber!</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>let</span
><span class="Normal NormalText"
> x = </span
><span class="Function"
>sqrt</span
><span class="Normal NormalText"
> </span
><span class="Function FunctionDefinition"
>val ::</span
><span class="Normal NormalText"
> </span
><span class="DataType TypeConstructor"
>Double</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>let</span
><span class="Normal NormalText"
> y = (</span
><span class="Function"
>round</span
><span class="Normal NormalText"
> val)^</span
><span class="DecVal Decimal"
>2</span
><span class="Normal NormalText"
> :: </span
><span class="DataType TypeConstructor"
>Int</span
>
<span class="Normal NormalText"
> outlet </span
><span class="String"
>"number_doubleValue"</span
><span class="Normal NormalText"
> >>= setObjectValue x</span
>
<span class="Normal NormalText"
> outlet </span
><span class="String"
>"number_integerValue"</span
><span class="Normal NormalText"
> >>= setObjectValue y</span
>
</code
></pre><p
>This corresponds to the original Objective-C version from the <a href="http://tscheff.blogspot.com/2010/02/small-haskell-objective-c-interface.html"
>last blog entry</a
>:</p><pre class="sourceCode objectivec"
><code
><span class="Normal Symbol"
>-</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>IBAction</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
>newNumberFromSlider</span
><span class="Normal Symbol"
>:(</span
><span class="Normal NormalText"
>id</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
>sender</span
><span class="Normal Symbol"
>;</span
>
<span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> NSNumber </span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
>value </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>NSNumber numberWithDouble</span
><span class="Normal Symbol"
>:[(</span
><span class="Normal NormalText"
>NSSlider</span
><span class="Normal Symbol"
>*)</span
><span class="Normal NormalText"
>sender doubleValue</span
><span class="Normal Symbol"
>]];</span
>
<span class="Normal NormalText"
> </span
><span class="Comment"
>// double</span
>
<span class="Normal NormalText"
> NSNumber </span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
>doubleRes </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> doubleTest</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>value</span
><span class="Normal Symbol"
>);</span
>
<span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>number_doubleValue setDoubleValue</span
><span class="Normal Symbol"
>:[</span
><span class="Normal NormalText"
>doubleRes doubleValue</span
><span class="Normal Symbol"
>]];</span
>
<span class="Normal NormalText"
> </span
><span class="Comment"
>// integer</span
>
<span class="Normal NormalText"
> NSNumber </span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
>intRes </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> integerTest</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>value</span
><span class="Normal Symbol"
>);</span
>
<span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>number_integerValue setIntValue</span
><span class="Normal Symbol"
>:[</span
><span class="Normal NormalText"
>intRes intValue</span
><span class="Normal Symbol"
>]];</span
>
<span class="Normal Symbol"
>}</span
>
</code
></pre><p
>By defining the complete action-target of the <code
>numberSlider</code
> in Haskell we do not have to export the functions <code
>doubleTest</code
> and <code
>integerTest</code
> anymore. All the Haskell calculation can be done directly in the Haskell function!</p><p
>The controller itself is just a plain Haskell data type, given for example as:</p><pre class="sourceCode haskell"
><code
><span class="Keyword"
>data</span
><span class="Normal NormalText"
> Controller = Controller { </span
>
<span class="Normal NormalText"
> </span
><span class="Function FunctionDefinition"
>ctrOutlets ::</span
><span class="Normal NormalText"
> OutletTable,</span
>
<span class="Normal NormalText"
> </span
><span class="Function FunctionDefinition"
>ctrMethods ::</span
><span class="Normal NormalText"
> MethodTable,</span
>
<span class="Normal NormalText"
> </span
><span class="Function FunctionDefinition"
>ctrModel ::</span
><span class="Normal NormalText"
> Model</span
>
<span class="Normal NormalText"
> }</span
>
</code
></pre></div><div id="connecting-a-model"
><h2
>Connecting a Model</h2><p
>In the <a href="http://github.com/tscheff/HSOBJC_Test"
>example application</a
> I have created a very simple model:</p><pre class="sourceCode haskell"
><code
><span class="Keyword"
>data</span
><span class="Normal NormalText"
> Model = Model {</span
>
<span class="Normal NormalText"
> </span
><span class="Function FunctionDefinition"
>mdSimpleName ::</span
><span class="Normal NormalText"
> IORef </span
><span class="Normal ModuleName"
>T.Text</span
><span class="Normal NormalText"
>,</span
>
<span class="Normal NormalText"
> </span
><span class="Function FunctionDefinition"
>mdStringsLength ::</span
><span class="Normal NormalText"
> IORef Answer </span
><span class="Comment"
>-- type Answer = [(Int, T.Text)]</span
>
<span class="Normal NormalText"
> }</span
>
</code
></pre><p
>I use <code
>IORef</code
>s for storing mutable data of the application. This data has to be stored somewhere as in the test application the user can e.g. enter some text and then later retrieve this text again by pressing a button. So we are dealing with two separate events that are triggered by the user and the application has to store this data between the two user-driven events.</p><p
>Dealing with <code
>IORef</code
>s seems to be not the classic Haskell solution, but somewhere we have to store this information. We could pass a new data structure back to the controller and avoid dealing with mutable data in the model, but then the controller has to keep this information. Now the controller could pass this new information back to the Objective-C controller proxy and leave it to this object to store a new <code
>HSValue</code
> value. This way we would avoid dealing with mutable data in the Haskell part at all, but this is only some kind of passing the buck. I think it’s best to handle mutable data in the model directly, because after all the model should manage the data (and state!) of the application. Another nice side effect of this is that this way the controller is a non-mutable data structure.</p><p
>The model itself is completely separated from the view and only interacts with the controller. This means that the model only operates on Haskell values: there is no reference to Objective-C objects in the model implementation. All the conversion is done by the controller or implicitly by the <code
>OBJC</code
> typeclass. This is a quite nice abstraction and means that we could use the very same model and plug it for example to a GTK+ GUI.</p></div><div id="conclusion"
><h2
>Conclusion</h2><p
>By mixing Haskell and Objective-C in a Cocoa application we have a very pure implementation of the MVC pattern: a stateful view, a stateless controller layer, and a stateful model. Because of being forced to make clear cuts between the individual parts the underlying pattern is visible much more clearly. For example the model only handles Haskell values like <code
>Text</code
> and does not know about <code
>NSString</code
>s.</p><p
>The test application can be found on <a href="http://github.com/tscheff/HSOBJC_Test"
>github</a
>.</p><!-- References --><br />
<br />
</div></div><div class="footnotes"
><hr
/><ol
><li id="fn1"
><p
>Previous articles: <a href="http://tscheff.blogspot.com/2010/02/currync-converter-using-haskell-with.html"
>“Curry’n’C Converter — Using Haskell with Objective-C in the Classic Cocoa Tutorial”</a
> and <a href="http://tscheff.blogspot.com/2010/02/small-haskell-objective-c-interface.html"
>“A small Haskell / Objective-C Interface”</a
>. <a href="#fnref1" class="footnoteBackLink" title="Jump back to footnote 1">↩</a></p></li>
<li id="fn2"
><p
>In this post I will only discuss functions of one argument. In the source however, you can find also the definition of <code
>HSFunc2</code
> which is for two-argument functions. <a href="#fnref2" class="footnoteBackLink" title="Jump back to footnote 2">↩</a></p></li>
<li id="fn3"
><p
>An implementation of a method <code
>- (NSDictionary*)ivarDictionary</code
> to get the <code
>ivars</code
> of the controller is provided in the example project <a href="http://github.com/tscheff/HSOBJC_Test"
>HSOBJC_Test</a
> <a href="#fnref3" class="footnoteBackLink" title="Jump back to footnote 3">↩</a></p></li>
<li id="fn4"
><p
><code
>Action</code
> is defined as <code
>type Action = StableId -> IOOBJC ()</code
>: For a given sender (<code
>StableId</code
>) it performs some <code
>IOOBJC</code
> actions and returns nothing, because the Objective-C actions return <code
>void</code
>. <a href="#fnref4" class="footnoteBackLink" title="Jump back to footnote 4">↩</a></p></li>
</ol></div>Tim Schefflerhttp://www.blogger.com/profile/00287780663826424312noreply@blogger.com0tag:blogger.com,1999:blog-3273671087241726593.post-77307644059357772962010-02-18T15:39:00.001+01:002010-02-18T15:54:49.703+01:00A small Haskell / Objective-C Interface<div id="a-small-haskell--objective-c-interface"
><!-- Abstract--><br />
<p
>In this post I will present a small Haskell typeclass <code
>OBJC</code
> for interfacing with Objective-C. Instead of implementing an interface via proxy types, <code
>OBJC</code
> will provide two functions <code
>toId</code
> and <code
>fromId</code
>. These can be used to transfer native Objective-C values to corresponding native Haskell value. It means for example converting a <code
>NSString</code
> to a Haskell <code
>String</code
> value or a <code
>NSArray</code
> to a Haskell list. The conversion via <code
>OBJC</code
> also works for more complex data structures, like e.g. <code
>[(Int, String)]</code
>.</p><p
>The use case for this typeclass would be to define a Haskell <em
>model</em
> implementation in a Cocoa Model-View-Controller application. This way the communication between the Haskell model and the Objective-C controller can be made easier and the programmer is able to define the model directly in terms of Haskell types.</p><p
>For testing I also provide a simple application that shows how to use the <code
>OBJC</code
> typeclass in a Cocoa application.</p><div id="introduction"
><h2
>Introduction</h2><p
>In the last <a href="http://tscheff.blogspot.com/2010/02/currync-converter-using-haskell-with.html"
>post</a
> I described a technique to build a simple Cocoa application that interacts with Haskell. In this Cocoa application (Apple’s famous <em
>Currency Converter</em
> tutorial) all that was passed were plain scalar values, namely the <code
>double</code
> values for exchange rate, dollar amount, and the result. Passing C scalar values is made easy, because they are handled by Haskell’s <a href="http://www.haskell.org/haskellwiki/GHC/Using_the_FFI"
>Foreign Function Interface</a
> out of the box. The FFI also helps us to pass stable opaque references of Haskell values back to C by providing the <code
>StablePtr</code
> type.</p><p
>In this post we will extend this simple interface to more data types. We will get a corresponding type to <code
>StablePtr</code
> for storing Objective-C objects in Haskell values in a <em
>stable</em
> way. Additionally we will create instances of <code
>OBJC</code
> for basic data types, like numbers, strings, and arrays. The template for this typeclass was the <code
>JSON</code
> typeclass that is described in chapter 6 of <a href="http://www.realworldhaskell.org"
>Real World Haskell</a
>.</p><p
>This approach focusses on providing help for defining Haskell models for Cocoa applications. The rest of the application will still be implemented in Objective-C. For a complete implementation of a Cocoa application in Haskell <a href="http://code.google.com/p/hoc/"
>HOC</a
> would be a good choice.</p><p
>For the development of the <code
>OBJC</code
> typeclass we will switch back to reference counting on the Objective-C side. Dealing with two garbage collectors in two runtimes is hard. One of these runtimes might be heavily threaded because of <a href="http://www.devworld.apple.com/mac/library/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html"
>Grand Central Dispatch</a
>; the other one uses lazy evaluation as default. Not exactly the kind of environment you want to play with if you are just starting. It’s not easy to be sure: is this object still reachable? When will it be evaluated and will it still be there? The ideal recipe for some headaches! So therefore it’s best to eliminate at least one cause for potential problems and switch back to reference-counting (retain/release) on the Objective-C side. Maybe I am too cautious, maybe it all works fine using the Objective-C GC, but for a start we will just take the simple approach.</p></div><div id="the-OBJC-typeclass"
><h2
>The <code
>OBJC</code
> typeclass</h2><p
>The basic Objective-C class is <code
>NSObject</code
> and a pointer to a <code
>NSObject</code
> instance is of type <code
>id</code
>. We would like to pass <code
>NSObject</code
>s to Haskell functions as arguments or to return them as a result of a function. On the Haskell side it would be nice if basic types, like e.g. arrays or strings, could be automatically converted to the corresponding Objective-C types and vice versa.</p><p
>For tasks like this it is best to define a typeclass in Haskell:</p><pre class="sourceCode haskell"
><code
><span class="Keyword"
>class</span
><span class="Normal NormalText"
> OBJC a </span
><span class="Keyword"
>where</span
>
<span class="Normal NormalText"
> </span
><span class="Function FunctionDefinition"
>fromId ::</span
><span class="Normal NormalText"
> Id -> IOOBJC a</span
>
<span class="Normal NormalText"
> </span
><span class="Function FunctionDefinition"
>toId ::</span
><span class="Normal NormalText"
> a -> IOOBJC Id</span
>
</code
></pre><p
>This means that an instance <code
>a</code
> of the typeclass <code
>OBJC</code
> will provide a function <code
>fromId</code
> that converts Objective-C <code
>id</code
>s to <code
>a</code
> and a function <code
>toId</code
> that does the backward conversion, providing an Objective-C object for an <code
>a</code
> value.</p><p
>The Objective-C pointers are represented in Haskell by the type <code
>Id</code
>:</p><pre class="sourceCode haskell"
><code
><span class="Keyword"
>data</span
><span class="Normal NormalText"
> ObjcObject</span
>
<span class="Keyword"
>type</span
><span class="Normal NormalText"
> Id = Ptr ObjcObject </span
><span class="Comment"
>-- typed pointer for all NSObjects </span
>
</code
></pre><p
>This is a typed pointer and can never be dereferenced.<sup
><a href="#fn1" class="footnoteRef" id="fnref1"
>1</a
></sup
> The type <code
>ObjcObject</code
> is an empty data declaration: there is no way in Haskell to construct a value of the type <code
>ObjcObject</code
>, which is ok as Objective-C objects are constructed by the Objective-C runtime. In order to use empty data declarations we have to switch on the GHC extension <code
>EmptyDataDecls</code
>.</p><p
>Now a closer look at the result types of the typeclass functions: <code
>toId</code
> will provide a new <code
>Id</code
> for a given <code
>a</code
> value. This new <code
>Id</code
> will be an Objective-C value and means, that we will have to call the Objective-C runtime and maybe construct a new object. This clearly implies, that we have to do some IO and therefore this function will run in the <code
>IO</code
> monad, meaning that calling this functions will have side effects, namely the construction of a new Objective-C object. Just what we wanted.</p><p
>The other function <code
>fromId</code
> will also call the Objective-C runtime and as Objective-C objects are not immutable per se calling the function <code
>fromId</code
> might give different results even if we provide the same <code
>id</code
> as an argument (for example, think of <code
>NSMutableArray</code
>). So this function has to be run in the <code
>IO</code
> monad too.</p><p
>But the conversion from an <code
>Id</code
> value to an <code
>a</code
> value might result in an error. This is because we are doing a conversion from a weakly typed language to a strongly typed one.<sup
><a href="#fn2" class="footnoteRef" id="fnref2"
>2</a
></sup
> We have to deal with this. Therefore the function <code
>fromId</code
> runs in the <code
>IOOBJC</code
> monad, which is the <code
>IO</code
> monad plus error handling. It is defined as:</p><pre class="sourceCode haskell"
><code
><span class="Keyword"
>type</span
><span class="Normal NormalText"
> OBJCError = </span
><span class="DataType TypeConstructor"
>String</span
>
<span class="Keyword"
>type</span
><span class="Normal NormalText"
> IOOBJC = ErrorT OBJCError </span
><span class="DataType TypeConstructor"
>IO</span
>
</code
></pre><p
>Using the <code
>ErrorT</code
> monad transformer. Inside this monad we can throw an error by the <code
>throwError</code
> action in case something goes wrong.<sup
><a href="#fn3" class="footnoteRef" id="fnref3"
>3</a
></sup
> Although it might strictly be not necessary<sup
><a href="#fn4" class="footnoteRef" id="fnref4"
>4</a
></sup
>, we will use the <code
>IOOBJC</code
> monad for the <code
>toId</code
> function also, because this way the resulting code involving both <code
>fromId</code
> and <code
>toId</code
> will look nicer.</p></div><div id="opaque-values---the-basic-instance-StableId"
><h2
>Opaque Values — The basic instance <code
>StableId</code
></h2><p
>The first instance of the <code
>OBJC</code
> typeclass will be <code
>StableId</code
>. This is the counterpart of the <code
>StablePtr</code
> from the FFI. Where the <code
>StablePtr</code
> provides a way to store an opaque reference to a Haskell value in Objective-C and making sure that the referenced value will not be garbage collected, the <code
>StableId</code
> will provide a way to store an opaque Objective-C object in Haskell. It will make sure, that this object will not be dealloced by Objective-C as long as Haskell holds a reference to this object.</p><p
>In order to deal with foreign-managed objects we have to use the <code
>ForeignPtr</code
> data type as the <a href="http://haskell.org/ghc/docs/latest/html/libraries/base-4.2.0.0/Foreign-ForeignPtr.html#t%3AForeignPtr"
>documentation</a
> explains:</p><blockquote
><p
>The type <code
>ForeignPtr</code
> represents references to objects that are maintained in a foreign language, i.e., that are not part of the data structures usually managed by the Haskell storage manager. The essential difference between <code
>ForeignPtr</code
>s and vanilla memory references of type <code
>Ptr a</code
> is that the former may be associated with finalizers.</p></blockquote><p
>Therefore we define:</p><pre class="sourceCode haskell"
><code
><span class="Normal NormalText"
>newtype StableId = StableId {</span
>
<span class="Normal NormalText"
> </span
><span class="Function FunctionDefinition"
>foreignPtr ::</span
><span class="Normal NormalText"
> ForeignPtr ObjcObject</span
>
<span class="Normal NormalText"
> }</span
>
</code
></pre><p
>This type shall be an instance of the <code
>OBJC</code
> typeclass:</p><pre class="sourceCode haskell"
><code
><span class="Keyword"
>instance</span
><span class="Normal NormalText"
> OBJC StableId </span
><span class="Keyword"
>where</span
>
<span class="Normal NormalText"
> toId x = liftIO $ </span
>
<span class="Normal NormalText"
> withForeignPtr (foreignPtr x) $ </span
>
<span class="Normal NormalText"
> \ptr -> c_retainId ptr >>= c_autoreleaseId</span
>
<span class="Normal NormalText"
> fromId ptr = liftIO $ </span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>do</span
><span class="Normal NormalText"
> x <- c_retainId ptr >>= newForeignPtr c_FunPtr_releaseId </span
>
<span class="Normal NormalText"
> </span
><span class="Function"
>return</span
><span class="Normal NormalText"
> $ StableId x</span
>
</code
></pre><p
>In <code
>fromId</code
> we create a new <code
>ForeignPtr</code
> from a given <code
>Id</code
>. This is done by <code
>x <- c_retainId ptr >>= newForeignPtr c_FunPtr_releaseId</code
>: first the <code
>Id</code
> value is retained by <code
>c_retainId</code
> and then a new <code
>ForeignPtr</code
> is created with an associated finalizer <code
>c_FunPtr_releaseId</code
>. This means, first Haskell increases the retain count of the transferred object such that the Objective-C runtime wont dealloc the object as long as Haskell holds a reference to it via the <code
>ForeignPtr</code
>. This object is then released —once the Haskell runtime has no longer a reference to it— using the mechanism of the <code
>ForeignPtr</code
> and the provided finalizer <code
>c_FunPtr_releaseId</code
>. These functions are defined in Objective-C as:</p><pre class="sourceCode objectivec"
><code
><span class="DataType DataType"
>void</span
><span class="Normal NormalText"
> releaseId</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>id object</span
><span class="Normal Symbol"
>)</span
>
<span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> </span
><span class="Comment"
>// release can trigger dealloc, which might need an autorelease pool</span
>
<span class="Normal NormalText"
> NSAutoreleasePool </span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
>pool </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[[</span
><span class="Normal NormalText"
>NSAutoreleasePool alloc</span
><span class="Normal Symbol"
>]</span
><span class="Normal NormalText"
> init</span
><span class="Normal Symbol"
>];</span
>
<span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>object release</span
><span class="Normal Symbol"
>];</span
>
<span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>pool release</span
><span class="Normal Symbol"
>];</span
>
<span class="Normal Symbol"
>}</span
>
<span class="Normal NormalText"
>id retainId</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>id object</span
><span class="Normal Symbol"
>)</span
>
<span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>return</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>object retain</span
><span class="Normal Symbol"
>];</span
>
<span class="Normal Symbol"
>}</span
>
<span class="Normal NormalText"
>id autoreleaseId</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>id object</span
><span class="Normal Symbol"
>)</span
>
<span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>return</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>object autorelease</span
><span class="Normal Symbol"
>];</span
>
<span class="Normal Symbol"
>}</span
>
</code
></pre><p
>The <code
>toId</code
> function is similar: we just retrieve the stored pointer to the Objective-C object and return it to the Objective-C runtime. Before that, we make sure that the returned object will not be dealloced by sending a <code
>retain</code
> and <code
>autorelease</code
> message to it via the Objective-C runtime.</p><p
>The <code
>StableId</code
> instance of <code
>OBJC</code
> can deal with every sub-class of <code
>NSObject</code
>. It provides an opaque storage for objects of these Objective-C classes just like the <code
>StablePtr</code
> on the C side.</p></div><div id="numbers"
><h2
>Numbers</h2><p
>For dealing with C scalar number types like <code
>double</code
> or <code
>int</code
> the <a href="http://www.haskell.org/haskellwiki/GHC/Using_the_FFI"
>FFI</a
> provides the necessary mechanisms. But the C number types are not first-class values in Objective-C. So, for example, you cannot store them in a <code
>NSArray</code
>.<sup
><a href="#fn5" class="footnoteRef" id="fnref5"
>5</a
></sup
> The first-class wrapper around C numbers is <code
>NSNumber</code
>. In the following we will construct instances for <code
>Int</code
> and <code
>Double</code
> of <code
>OBJC</code
> that can convert to and from <code
>NSNumber</code
> values.</p><p
>The definition of the numeric instances for <code
>OBJC</code
> is quite straightforward: first we define a helper function that checks if a given <code
>Id</code
> represents a <code
>NSNumber</code
> object and if so calls a function <code
>f :: (OBJC a) => (Id -> IO a)</code
> to do the real conversion of this <code
>NSNumber</code
> to a numeric value in Haskell:</p><pre class="sourceCode haskell"
><code
><span class="Function FunctionDefinition"
>checkIfNSNumber ::</span
><span class="Normal NormalText"
> (OBJC a) => (Id -> </span
><span class="DataType TypeConstructor"
>IO</span
><span class="Normal NormalText"
> a) -> Id -> IOOBJC a</span
>
<span class="Normal NormalText"
>checkIfNSNumber f ptr = </span
><span class="Keyword"
>do</span
><span class="Normal NormalText"
> isNSNumber <- liftIO $ c_isNSNumber ptr</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>case</span
><span class="Normal NormalText"
> (</span
><span class="Function"
>fromIntegral</span
><span class="Normal NormalText"
> isNSNumber) </span
><span class="Keyword"
>of</span
>
<span class="Normal NormalText"
> </span
><span class="DecVal Decimal"
>0</span
><span class="Normal NormalText"
> -> throwError </span
><span class="String"
>"not a NSNumber value"</span
>
<span class="Normal NormalText"
> </span
><span class="Function"
>otherwise</span
><span class="Normal NormalText"
> -> liftIO $ f ptr</span
>
</code
></pre><p
>The C helper function is given as:</p><pre class="sourceCode objectivec"
><code
><span class="DataType DataType"
>int</span
><span class="Normal NormalText"
> isNSNumber</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>id object</span
><span class="Normal Symbol"
>)</span
>
<span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>if</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>([</span
><span class="Normal NormalText"
>object isKindOfClass</span
><span class="Normal Symbol"
>:[</span
><span class="Normal NormalText"
>NSNumber class</span
><span class="Normal Symbol"
>]])</span
><span class="Normal NormalText"
> </span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>return</span
><span class="Normal NormalText"
> </span
><span class="DecVal Decimal"
>1</span
><span class="Normal Symbol"
>;</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>else</span
><span class="Normal NormalText"
> </span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>return</span
><span class="Normal NormalText"
> </span
><span class="DecVal Decimal"
>0</span
><span class="Normal Symbol"
>;</span
>
<span class="Normal Symbol"
>}</span
>
</code
></pre><p
>We are passing back <code
>int</code
> values instead of <code
>BOOL</code
>, because the FFI does not provide a wrapper for boolean values.</p><p
>With this we can now define the <code
>OBJC</code
> instances for <code
>Double</code
> and <code
>Int</code
>:</p><pre class="sourceCode haskell"
><code
><span class="Keyword"
>instance</span
><span class="Normal NormalText"
> OBJC </span
><span class="DataType TypeConstructor"
>Double</span
><span class="Normal NormalText"
> </span
><span class="Keyword"
>where</span
>
<span class="Normal NormalText"
> toId = checkNullPtr </span
><span class="String"
>"Could not create NSNumber"</span
><span class="Normal NormalText"
> . </span
>
<span class="Normal NormalText"
> c_numberWithDouble . </span
><span class="Function"
>realToFrac</span
>
<span class="Normal NormalText"
> fromId = checkIfNSNumber $ liftM </span
><span class="Function"
>realToFrac</span
><span class="Normal NormalText"
> . c_doubleValue</span
>
<span class="Keyword"
>instance</span
><span class="Normal NormalText"
> OBJC </span
><span class="DataType TypeConstructor"
>Int</span
><span class="Normal NormalText"
> </span
><span class="Keyword"
>where</span
>
<span class="Normal NormalText"
> toId = checkNullPtr </span
><span class="String"
>"Could not create NSNumber"</span
><span class="Normal NormalText"
> . </span
>
<span class="Normal NormalText"
> c_numberWithLong . </span
><span class="Function"
>fromIntegral</span
>
<span class="Normal NormalText"
> fromId = checkIfNSNumber $ liftM </span
><span class="Function"
>fromIntegral</span
><span class="Normal NormalText"
> . c_longValue</span
>
</code
></pre><p
>The function <code
>checkNullPtr</code
> is a simple function to lift an <code
>IO</code
> action to the <code
>IOOBJC</code
> monad. It will throw an error if a <code
>nullPtr</code
> is passed. This function is given as:</p><pre class="sourceCode haskell"
><code
><span class="Function FunctionDefinition"
>checkNullPtr ::</span
><span class="Normal NormalText"
> </span
><span class="DataType TypeConstructor"
>String</span
><span class="Normal NormalText"
> -> </span
><span class="DataType TypeConstructor"
>IO</span
><span class="Normal NormalText"
> Id -> IOOBJC Id</span
>
<span class="Normal NormalText"
>checkNullPtr msg act = </span
><span class="Keyword"
>do</span
><span class="Normal NormalText"
> ptrId <- liftIO act</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>if</span
><span class="Normal NormalText"
> ptrId == nullPtr</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>then</span
><span class="Normal NormalText"
> throwError msg</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>else</span
><span class="Normal NormalText"
> </span
><span class="Function"
>return</span
><span class="Normal NormalText"
> ptrId</span
>
</code
></pre><p
>Additionally we have the C conversion functions, here are the functions for <code
>Double</code
>:</p><pre class="sourceCode objectivec"
><code
><span class="DataType DataType"
>double</span
><span class="Normal NormalText"
> doubleValue</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>NSNumber </span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
>aNumber</span
><span class="Normal Symbol"
>)</span
>
<span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>return</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>aNumber doubleValue</span
><span class="Normal Symbol"
>];</span
>
<span class="Normal Symbol"
>}</span
>
<span class="Normal NormalText"
>NSNumber </span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
>numberWithDouble</span
><span class="Normal Symbol"
>(</span
><span class="DataType DataType"
>double</span
><span class="Normal NormalText"
> aDouble</span
><span class="Normal Symbol"
>)</span
>
<span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>return</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>NSNumber numberWithDouble</span
><span class="Normal Symbol"
>:</span
><span class="Normal NormalText"
>aDouble</span
><span class="Normal Symbol"
>];</span
>
<span class="Normal Symbol"
>}</span
>
</code
></pre><p
>This way basically all numeric values can be made into instances of <code
>OBJC</code
>.<sup
><a href="#fn6" class="footnoteRef" id="fnref6"
>6</a
></sup
></p></div><div id="strings"
><h2
>Strings</h2><p
>In Haskell we have the luxury of being provided with unicode strings. Same goes for Objective-C’s <code
>NSString</code
> class. The only problem is that we have to interface via C’s old ASCII strings. But this can be dealt with by using the nice <code
>Data.Text</code
><sup
><a href="#fn7" class="footnoteRef" id="fnref7"
>7</a
></sup
> package in combination with <code
>Data.ByteString</code
>.</p><p
>So in order to define an <code
>OBJC</code
> instance for <code
>String</code
><sup
><a href="#fn8" class="footnoteRef" id="fnref8"
>8</a
></sup
> we proceed by “wishful thinking” and assume, that we already have an instance for <code
>Data.Text</code
>:</p><pre class="sourceCode haskell"
><code
><span class="Keyword"
>instance</span
><span class="Normal NormalText"
> OBJC </span
><span class="DataType TypeConstructor"
>String</span
><span class="Normal NormalText"
> </span
><span class="Keyword"
>where</span
>
<span class="Normal NormalText"
> </span
><span class="Comment"
>-- via Text</span
>
<span class="Normal NormalText"
> toId = toId . T.pack</span
>
<span class="Normal NormalText"
> fromId x = </span
><span class="Function"
>return</span
><span class="Normal NormalText"
> . T.unpack =<< fromId x</span
>
</code
></pre><p
><code
>T.pack</code
> and <code
>T.unpack</code
> are functions from <code
>Data.Text</code
> (imported qualified as <code
>T</code
>) that construct <code
>T.Text</code
> values from <code
>String</code
>s and vice versa.</p><p
>In <code
>toId = toId . T.pack</code
> the function on the left-hand side has the type <code
>String -> IOOBJC Id</code
> and <code
>toId</code
> on the right-hand side has the type <code
>T.Text -> IOOBJC Id</code
>. These a two different functions and the Haskell compiler knows what function to take just by inferring the used types.</p><p
>We now define the instance for <code
>T.Text</code
> once again by wishful thinking:</p><pre class="sourceCode haskell"
><code
><span class="Keyword"
>instance</span
><span class="Normal NormalText"
> OBJC </span
><span class="Normal ModuleName"
>T.Text</span
><span class="Normal NormalText"
> </span
><span class="Keyword"
>where</span
>
<span class="Normal NormalText"
> </span
><span class="Comment"
>-- via ByteString</span
>
<span class="Normal NormalText"
> toId = toId . encodeUtf8</span
>
<span class="Normal NormalText"
> fromId x = </span
><span class="Function"
>return</span
><span class="Normal NormalText"
> . decodeUtf8 =<< fromId x</span
>
</code
></pre><p
>The functions <code
>encodeUtf8</code
> and <code
>decodeUtf8</code
> are from <code
>Data.Text.Encoding</code
> and provide the translation from and to <code
>ByteString</code
>s.</p><p
>Finally there comes the point where we really have to deal with the conversion to Objective-C types. For this the <code
>ByteString</code
> module provides the necessary functions:</p><pre class="sourceCode haskell"
><code
><span class="Keyword"
>instance</span
><span class="Normal NormalText"
> OBJC </span
><span class="Normal ModuleName"
>BS.ByteString</span
><span class="Normal NormalText"
> </span
><span class="Keyword"
>where</span
>
<span class="Normal NormalText"
> toId x = checkNullPtr </span
><span class="String"
>"Could not create NSString"</span
><span class="Normal NormalText"
> $ </span
>
<span class="Normal NormalText"
> BS.useAsCString x c_utf8ToNSString</span
>
<span class="Normal NormalText"
> fromId x = </span
><span class="Keyword"
>do</span
><span class="Normal NormalText"
> ptr <- liftIO $ c_nsStringToUtf8 x</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>if</span
><span class="Normal NormalText"
> ptr == nullPtr</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>then</span
><span class="Normal NormalText"
> throwError </span
><span class="String"
>"not a NSString value"</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>else</span
><span class="Normal NormalText"
> liftIO $ BS.packCString ptr</span
>
</code
></pre><p
>For this we also need the corresponding C helper functions:</p><pre class="sourceCode objectivec"
><code
><span class="DataType DataType"
>const</span
><span class="Normal NormalText"
> </span
><span class="DataType DataType"
>char</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
>nsStringToUtf8</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>NSString </span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
>str</span
><span class="Normal Symbol"
>)</span
>
<span class="Comment"
>// returns a CString of UTF8 chars for the NSString str.</span
>
<span class="Comment"
>// if str is not a NSString, it will pass back NULL</span
>
<span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> </span
><span class="Comment"
>// it is quite time consuming to test at runtime if `str` really is a NSString.</span
>
<span class="Normal NormalText"
> </span
><span class="Comment"
>// so let's be optimistic and deal with an exception by passing back the null pointer.</span
>
<span class="Normal NormalText"
> </span
><span class="DataType DataType"
>const</span
><span class="Normal NormalText"
> </span
><span class="DataType DataType"
>char</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
>p</span
><span class="Normal Symbol"
>;</span
>
<span class="Normal NormalText"
> @try </span
><span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> </span
><span class="Comment"
>// according to the documentation of NSString's UTF8String the resulting</span
>
<span class="Normal NormalText"
> </span
><span class="Comment"
>// char array will be freed by the autorelease pool.</span
>
<span class="Normal NormalText"
> p </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>str UTF8String</span
><span class="Normal Symbol"
>];</span
>
<span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>}</span
>
<span class="Normal NormalText"
> @catch </span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>NSException </span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
> e</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> p </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> NULL</span
><span class="Normal Symbol"
>;</span
>
<span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>}</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>return</span
><span class="Normal NormalText"
> p</span
><span class="Normal Symbol"
>;</span
>
<span class="Normal Symbol"
>}</span
>
<span class="Normal NormalText"
>NSString </span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
>utf8ToNSString</span
><span class="Normal Symbol"
>(</span
><span class="DataType DataType"
>const</span
><span class="Normal NormalText"
> </span
><span class="DataType DataType"
>char</span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
> cstr</span
><span class="Normal Symbol"
>)</span
>
<span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> NSString </span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
>res</span
><span class="Normal Symbol"
>;</span
>
<span class="Normal NormalText"
> @try </span
><span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> res </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>NSString stringWithUTF8String</span
><span class="Normal Symbol"
>:</span
><span class="Normal NormalText"
>cstr</span
><span class="Normal Symbol"
>];</span
>
<span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>}</span
>
<span class="Normal NormalText"
> @catch </span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>NSException </span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
> e</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> res </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> NULL</span
><span class="Normal Symbol"
>;</span
>
<span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>}</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>return</span
><span class="Normal NormalText"
> res</span
><span class="Normal Symbol"
>;</span
>
<span class="Normal Symbol"
>}</span
>
</code
></pre><p
>This way we can now construct three different types from a <code
>NSString</code
>: <code
>ByteString</code
>, <code
>Text</code
>, and <code
>String</code
>.</p></div><div id="arrays-lists"
><h2
><span style="text-decoration: line-through;"
>Arrays</span
> Lists</h2><p
>A more interesting data type is the array or list. (I know, arrays are not lists, but for the sake of this article I will use them both as representation of some abstract container type. For all that matters now, we can convert <code
>NSArrays</code
> to Haskell’s lists.)</p><p
>An array contains other values, maybe even other arrays. So we cannot say in advance how deep the conversion has to be. But in principle we have to trigger the conversion of the complete nested structure contained in the array.</p><p
>So the conversion of a container consists of two steps:</p><ol style="list-style-type: decimal;"
><li
><p
>Convert the original container value.</p></li>
<li
><p
>(Recursively) Convert all contained values.</p></li>
</ol><p
>This can be done in Haskell quite nicely:</p><pre class="sourceCode haskell"
><code
><span class="Keyword"
>instance</span
><span class="Normal NormalText"
> (OBJC a) => OBJC [a] </span
><span class="Keyword"
>where</span
>
<span class="Normal NormalText"
> toId xs = </span
><span class="Function"
>mapM</span
><span class="Normal NormalText"
> toId xs >>= toNSArray'</span
>
<span class="Normal NormalText"
> fromId x = fromNSArray' x >>= </span
><span class="Function"
>mapM</span
><span class="Normal NormalText"
> fromId </span
>
</code
></pre><p
>In <code
>fromId</code
> the function <code
>fromNSArray'</code
> has the type <code
>Id -> IOOBJC [Id]</code
>. It converts the original container <code
>NSArray</code
> to the Haskell list <code
>[Id]</code
> that contains the untreated Objective-C values of type <code
>Id</code
>. These values are then converted to Haskell values by <code
>mapM fromId</code
>. In the case of <code
>toId</code
> we have to execute the steps the other way around: first we have to convert all the values of a list <code
>(OBJC a) => [a]</code
> to <code
>[Id]</code
> which is done inside the <code
>IOOBJC</code
> monad by <code
>mapM toId</code
> and then convert the Haskell list to an <code
>NSArray</code
> by the function <code
>toNSArray' :: [Id] -> IOBJC Id</code
>.</p><p
>These helper function <code
>toNSArray'</code
> and <code
>fromNSArray'</code
> are defined as local functions and we get the final implementation:</p><pre class="sourceCode haskell"
><code
><span class="Keyword"
>instance</span
><span class="Normal NormalText"
> (OBJC a) => OBJC [a] </span
><span class="Keyword"
>where</span
>
<span class="Normal NormalText"
> toId xs = </span
><span class="Function"
>mapM</span
><span class="Normal NormalText"
> toId xs >>= toNSArray'</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>where</span
>
<span class="Normal NormalText"
> </span
><span class="Function FunctionDefinition"
>toNSArray' ::</span
><span class="Normal NormalText"
> [Id] -> IOOBJC Id</span
>
<span class="Normal NormalText"
> toNSArray' x = checkNullPtr </span
><span class="String"
>"Could not create NSArray"</span
><span class="Normal NormalText"
> $ </span
>
<span class="Normal NormalText"
> withArrayLen x $ \len ptr -> </span
>
<span class="Normal NormalText"
> c_arrayWithCArray ptr (</span
><span class="Function"
>fromIntegral</span
><span class="Normal NormalText"
> len)</span
>
<span class="Normal NormalText"
> fromId x = fromNSArray' x >>= </span
><span class="Function"
>mapM</span
><span class="Normal NormalText"
> fromId </span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>where</span
>
<span class="Normal NormalText"
> </span
><span class="Function FunctionDefinition"
>fromNSArray' ::</span
><span class="Normal NormalText"
> Id -> IOOBJC [Id]</span
>
<span class="Normal NormalText"
> fromNSArray' x = </span
><span class="Keyword"
>do</span
><span class="Normal NormalText"
> ptr <- liftIO $ c_getObjects x</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>if</span
><span class="Normal NormalText"
> ptr == nullPtr</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>then</span
><span class="Normal NormalText"
> throwError </span
><span class="String"
>"not a NSArray"</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>else</span
><span class="Normal NormalText"
> liftIO $ </span
><span class="Keyword"
>do</span
><span class="Normal NormalText"
> </span
>
<span class="Normal NormalText"
> len <- c_len x</span
>
<span class="Normal NormalText"
> res <- peekArray (</span
><span class="Function"
>fromIntegral</span
><span class="Normal NormalText"
> len) ptr</span
>
<span class="Normal NormalText"
> free ptr</span
>
<span class="Normal NormalText"
> </span
><span class="Function"
>return</span
><span class="Normal NormalText"
> res</span
>
</code
></pre><p
>The restriction <code
>(OBJC a) => OBJC [a]</code
> says, that we can convert only lists that contain values of another <code
>OBJC</code
> instance. But this really is not that much of a restriction as we have the <code
>OBJC</code
> instance <code
>StableId</code
> that can be used for every <code
>NSObject</code
> value.</p><p
>For the conversion of lists to C arrays we use the <code
>Foreign.Marshal.Array</code
> functions <code
>withArrayLen</code
> and <code
>peekArray</code
>. Finally here are the missing C helper functions:</p><pre class="sourceCode objectivec"
><code
><span class="Normal NormalText"
>NSArray </span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
>arrayWithCArray</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>id </span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
>objects</span
><span class="Normal Symbol"
>,</span
><span class="Normal NormalText"
> NSUInteger count</span
><span class="Normal Symbol"
>)</span
>
<span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>return</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>NSArray arrayWithObjects</span
><span class="Normal Symbol"
>:</span
><span class="Normal NormalText"
>objects count</span
><span class="Normal Symbol"
>:</span
><span class="Normal NormalText"
>count</span
><span class="Normal Symbol"
>];</span
>
<span class="Normal Symbol"
>}</span
>
<span class="Normal NormalText"
>NSUInteger lengthOfArray</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>NSArray </span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
>anArray</span
><span class="Normal Symbol"
>)</span
>
<span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> NSUInteger len</span
><span class="Normal Symbol"
>;</span
>
<span class="Normal NormalText"
> @try </span
><span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> len </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>anArray count</span
><span class="Normal Symbol"
>];</span
>
<span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>}</span
>
<span class="Normal NormalText"
> @catch </span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>NSException </span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
> e</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> len </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> </span
><span class="DecVal Decimal"
>0</span
><span class="Normal Symbol"
>;</span
>
<span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>}</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>return</span
><span class="Normal NormalText"
> len</span
><span class="Normal Symbol"
>;</span
>
<span class="Normal Symbol"
>}</span
>
<span class="Normal NormalText"
>id </span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
>getObjects</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>NSArray </span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
>anArray</span
><span class="Normal Symbol"
>)</span
>
<span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> id </span
><span class="Normal Symbol"
>(*</span
><span class="Normal NormalText"
>objects</span
><span class="Normal Symbol"
>);</span
>
<span class="Normal NormalText"
> @try </span
><span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> NSRange range </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> NSMakeRange</span
><span class="Normal Symbol"
>(</span
><span class="DecVal Decimal"
>0</span
><span class="Normal Symbol"
>,</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>anArray count</span
><span class="Normal Symbol"
>]);</span
>
<span class="Normal NormalText"
> objects </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> malloc</span
><span class="Normal Symbol"
>(</span
><span class="Keyword"
>sizeof</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>id</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
> range</span
><span class="Normal Symbol"
>.</span
><span class="Normal NormalText"
>length</span
><span class="Normal Symbol"
>);</span
>
<span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>anArray getObjects</span
><span class="Normal Symbol"
>:</span
><span class="Normal NormalText"
>objects range</span
><span class="Normal Symbol"
>:</span
><span class="Normal NormalText"
>range</span
><span class="Normal Symbol"
>];</span
>
<span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>}</span
>
<span class="Normal NormalText"
> @catch </span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>NSException </span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
> e</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> objects </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> NULL</span
><span class="Normal Symbol"
>;</span
>
<span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>}</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>return</span
><span class="Normal NormalText"
> objects</span
><span class="Normal Symbol"
>;</span
>
<span class="Normal Symbol"
>}</span
>
</code
></pre><p
>The nice thing now is that we can trigger the depth of the conversion just by the type of the resulting <code
>OBJC</code
> instance! E.g. if we have a large <code
>NSArray</code
> of <code
>NSArray</code
>s of <code
>NSString</code
>s we might not be interested in the individual strings on the Haskell side (we might just want to pass them back to Objective-C anyway). In this case we would use a conversion like <code
>fromId :: IOOBJC [StableId]</code
> and take, say, only the first element. But if we on the other hand really need all those strings nested in the structure we might use a conversion like <code
>fromId :: IOOBJC [[String]]</code
>. All just by specifying the result type.</p></div><div id="tuples"
><h2
>Tuples</h2><p
>We now have the conversions for the basic data structures. The conversions for other data structures like e.g. <code
>NSDictionary</code
> can easily be defined along these lines. But as a last example we will look at the conversion for a Haskell tuple type, because this is kind of interesting.</p><p
>Objective-C does not know tuples, in some sense <code
>NSArray</code
>s are tuples also. In Haskell one difference between tuples and lists is that tuples can contain values of different type. So if we want to define an instance of <code
>OBJC</code
> for a two-tuple we have to allow for that. This is expressed by the definition:</p><pre class="sourceCode haskell"
><code
><span class="Keyword"
>instance</span
><span class="Normal NormalText"
> (OBJC a, OBJC b) => OBJC (a, b) </span
><span class="Keyword"
>where</span
>
<span class="Normal NormalText"
> </span
><span class="Function"
>undefined</span
>
</code
></pre><p
>Here the type variables <code
>a</code
> and <code
>b</code
> might refer to different instances of <code
>OBJC</code
>. This means that types like <code
>(String, Double)</code
> would be an instance of <code
>OBJC</code
>, but also <code
>(String, String)</code
>.</p><p
>In order to make the definition of the instance for the two-tuple easy we would like to reuse old code. It would be nice if we could use the Haskell list to <code
>NSArray</code
> conversion as we finally want to convert the tuple to a <code
>NSArray</code
> anyway. But in Haskell a list can only contain values of the same type. So to make that work we will use <code
>StableId</code
> as a middleman and get:</p><pre class="sourceCode haskell"
><code
><span class="Keyword"
>instance</span
><span class="Normal NormalText"
> (OBJC a, OBJC b) => OBJC (a, b) </span
><span class="Keyword"
>where</span
>
<span class="Normal NormalText"
> </span
><span class="Comment"
>-- via list and StableId</span
>
<span class="Normal NormalText"
> toId (a, b) = </span
><span class="Keyword"
>do</span
><span class="Normal NormalText"
> </span
><span class="Comment"
>-- wrap arguments into opaque StableId, so that we can use them in a list</span
>
<span class="Normal NormalText"
> aStId <- fromId =<< toId </span
><span class="Function FunctionDefinition"
>a ::</span
><span class="Normal NormalText"
> IOOBJC StableId</span
>
<span class="Normal NormalText"
> bStId <- fromId =<< toId </span
><span class="Function FunctionDefinition"
>b ::</span
><span class="Normal NormalText"
> IOOBJC StableId</span
>
<span class="Normal NormalText"
> toId [aStId, bStId]</span
>
<span class="Normal NormalText"
> fromId x = </span
><span class="Keyword"
>do</span
><span class="Normal NormalText"
> ys <- fromId </span
><span class="Function FunctionDefinition"
>x ::</span
><span class="Normal NormalText"
> IOOBJC [StableId]</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>case</span
><span class="Normal NormalText"
> ys </span
><span class="Keyword"
>of</span
>
<span class="Normal NormalText"
> (aStId:bStId:[]) -> </span
><span class="Keyword"
>do</span
><span class="Normal NormalText"
> a <- fromId =<< toId aStId</span
>
<span class="Normal NormalText"
> b <- fromId =<< toId bStId</span
>
<span class="Normal NormalText"
> </span
><span class="Function"
>return</span
><span class="Normal NormalText"
> (a, b)</span
>
<span class="Normal NormalText"
> </span
><span class="Function"
>otherwise</span
><span class="Normal NormalText"
> -> throwError </span
><span class="String"
>"Wrong number of arguments for (,)"</span
>
</code
></pre><p
>Implementations for longer tuples can be done the same way.</p></div><div id="an-example-cocoa-application"
><h2
>An example Cocoa application</h2><p
>For testing the <code
>OBJC</code
> typeclass I created a short Cocoa application. Its source is available as <a href="http://github.com/tscheff/HSOBJC_Test"
>download</a
>.<sup
><a href="#fn9" class="footnoteRef" id="fnref9"
>9</a
></sup
> The mechanism how to use Haskell in an Objective-C application is described in my last blog <a href="http://tscheff.blogspot.com/2010/02/currync-converter-using-haskell-with.html"
>post</a
>.</p><p
>In this application mainly pure Haskell functions are tested. These are wrapped with the helper functions</p><pre class="sourceCode haskell"
><code
><span class="Function FunctionDefinition"
>toCocoa ::</span
><span class="Normal NormalText"
> (OBJC a, OBJC b) => (a -> b) -> Id -> </span
><span class="DataType TypeConstructor"
>IO</span
><span class="Normal NormalText"
> Id</span
>
<span class="Normal NormalText"
>toCocoa f anId = catchOBJC $ toId . f =<< fromId anId</span
>
<span class="Function FunctionDefinition"
>catchOBJC ::</span
><span class="Normal NormalText"
> IOOBJC Id -> </span
><span class="DataType TypeConstructor"
>IO</span
><span class="Normal NormalText"
> Id</span
>
<span class="Normal NormalText"
>catchOBJC act = </span
><span class="Keyword"
>do</span
><span class="Normal NormalText"
> eth <- runErrorT act</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>case</span
><span class="Normal NormalText"
> eth </span
><span class="Keyword"
>of</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword DataConstructor"
>Left</span
><span class="Normal NormalText"
> err -> </span
><span class="Keyword"
>do</span
><span class="Normal NormalText"
> nsLog $ </span
><span class="String"
>"(Haskell) OBJC error: "</span
><span class="Normal NormalText"
> ++ err</span
>
<span class="Normal NormalText"
> </span
><span class="Function"
>return</span
><span class="Normal NormalText"
> nullPtr</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword DataConstructor"
>Right</span
><span class="Normal NormalText"
> y -> </span
><span class="Function"
>return</span
><span class="Normal NormalText"
> y</span
>
</code
></pre><p
>With <code
>toCocoa</code
> the example “Array Test” is just defined as:</p><pre class="sourceCode haskell"
><code
><span class="Function FunctionDefinition"
>lengthOfStrings ::</span
><span class="Normal NormalText"
> [</span
><span class="DataType TypeConstructor"
>String</span
><span class="Normal NormalText"
>] -> [(</span
><span class="DataType TypeConstructor"
>Int</span
><span class="Normal NormalText"
>, </span
><span class="DataType TypeConstructor"
>String</span
><span class="Normal NormalText"
>)]</span
>
<span class="Normal NormalText"
>lengthOfStrings = </span
><span class="Function"
>map</span
><span class="Normal NormalText"
> $ \x -> (</span
><span class="Function"
>length</span
><span class="Normal NormalText"
> x, x)</span
>
<span class="Normal NormalText"
>foreign export ccall </span
><span class="Function FunctionDefinition"
>c_countAllStrings ::</span
><span class="Normal NormalText"
> Id -> </span
><span class="DataType TypeConstructor"
>IO</span
><span class="Normal NormalText"
> Id</span
>
<span class="Normal NormalText"
>c_countAllStrings = toCocoa lengthOfStrings</span
>
</code
></pre><p
>The <code
>toCocoa</code
> function automatically applies the necessary conversions for wrapping every function of type <code
>(OBJC a, OBJC b) => a -> b</code
> and makes it available to Objective-C!</p><p
>Included are also two tests in which the execution of the <code
>Id -> IO Id</code
> function is paused by passing back a <code
>StablePtr</code
> to Objective-C. I did this, because I do not feel that comfortable with <em
>lazy evaluation</em
>. As a Haskell beginner, I find it generally quite difficult to tell which parts of an expression will be evaluated immediately and which parts will become thunks. This especially worries me, because in passing Objective-C values to Haskell we must make sure that we have handled these values on the Haskell side <em
>before</em
> they are released by the Objective-C runtime. So I wanted to test a typical scenario where one would store an <code
>OBJC</code
> instance value inside a Haskell data structure and pass a <code
>StablePtr</code
> to this value back to Objective-C.</p></div><div id="summary"
><h2
>Summary</h2><p
>We now have quite a nice way to convert Haskell values to Objective-C or the other way around:</p><table
><tr class="header"
><th align="left"
>Data Structure</th
><th align="left"
>Haskell Representation</th
><th align="left"
>Objective-C Representation</th
><th align="left"
>Remarks</th
></tr>
<tr class="odd"
><td align="left"
>Opaque value</td
><td align="left"
><code
>StableId</code
> by <code
>OBJC</code
></td
><td align="left"
><code
>StablePtr</code
> from the FFI</td
><td align="left"
></td
></tr>
<tr class="even"
><td align="left"
>Scalar number</td
><td align="left"
><code
>Int</code
>, <code
>Double</code
>, …</td
><td align="left"
>C types <code
>int</code
>, <code
>double</code
>, …</td
><td align="left"
>Conversion by FFI</td
></tr>
<tr class="odd"
><td align="left"
>1st class number</td
><td align="left"
><code
>Int</code
>, <code
>Double</code
>, …</td
><td align="left"
><code
>NSNumber</code
></td
><td align="left"
>Conversion by <code
>OBJC</code
></td
></tr>
<tr class="even"
><td align="left"
>String</td
><td align="left"
><code
>String</code
>, <code
>Data.Text</code
></td
><td align="left"
><code
>NSString</code
></td
><td align="left"
>Conversion by <code
>OBJC</code
></td
></tr>
<tr class="odd"
><td align="left"
>Container</td
><td align="left"
>List, tuple</td
><td align="left"
><code
>NSArray</code
></td
><td align="left"
>Conversion by <code
>OBJC</code
></td
></tr>
</table><p
>It should be quite easy to expand this list for more data types, like e.g. <code
>NSDictionary</code
>.</p><p
>But one does not necessary has to define own wrappers for every <code
>NSObject</code
> type, as one can just use the opaque type <code
>StableId</code
> and use Objective-C methods to deal with this object. In the provided source is an example (“String Test”) that shows how to send a simple message to an Objective-C object.</p></div><div id="conclusion"
><h2
>Conclusion</h2><p
>The combination of typeclasses with a strong type system offers a nice way to do quasi-automatic transformation of Objective-C values to Haskell values and vice versa. For example, pure Haskell functions of type <code
>(OBJC a, OBJC b) => (a -> b)</code
> can be made available for Objective-C just by wrapping them with <code
>toCocoa</code
>. Other functions types can be exposed easily as well.</p><p
>Working on the typeclass was quite straightforward, even for a Haskell beginner like me. But I am convinced that the provided code is not written in the way that a Haskell expert would have written it.</p><p
>Although I really enjoyed working on this typeclass, I feel a bit uneasy concerning lazy evaluation, as mentioned before in the chapter “An example Cocoa application”. Right now, all my test cases show no problems with lazy evaluation and object retaining/releasing. But this surely does not mean that all of this is safe in every possible situation. Reasoning about this non-strict behavior is for me so far the biggest problem in programming Haskell.</p><p
>One last thing: Looking at the type of the tuple instance for <code
>OBJC</code
>, <code
>(OBJC a, OBJC b) => OBJC (a, b)</code
>, and the types of functions as arguments to <code
>toCocoa</code
>, <code
>(OBJC a, OBJC b) => (a -> b)</code
>, one notices that they look very similar.<sup
><a href="#fn10" class="footnoteRef" id="fnref10"
>10</a
></sup
> Maybe there is a sensible way to define an <code
>OBJC</code
> instance for these functions. On the Objective-C side we might have to define a new class, that will hold the original Haskell function, and that can invoke the stored function thereby automatically converting the argument to a Haskell value and the result back to an Objective-C value. Providing such an instance could help us to get rid of all those “dummy” C function definitions for the Haskell model.</p><br />
<br />
</div></div><div class="footnotes"
><hr
/><ol
><li id="fn1"
><p
>For details about typed pointers please consult chapter 17 of <a href="http://www.realworldhaskell.org"
>Real World Haskell</a
>. <a href="#fnref1" class="footnoteBackLink" title="Jump back to footnote 1">↩</a></p></li>
<li id="fn2"
><p
>Think of converting a <code
>NSArray</code
> to a type <code
>[String]</code
>: it might be possible at compile time to deduce the array type, but in general it is impossible in Objective-C to tell the type of the contained elements in an array at compile time. It is even possible, that an Objective-C array contains objects of different types. So converting such an array to a Haskell list might result in a runtime error. This class of runtime errors are handled by the <code
>IOBJC</code
> monad. <a href="#fnref2" class="footnoteBackLink" title="Jump back to footnote 2">↩</a></p></li>
<li id="fn3"
><p
>The <code
>IOOBJC</code
> just uses strings for the error. In a future version it might be better to use a dedicated instance of the <code
>Error</code
> typeclass as explained in chapter 19 of <a href="http://www.realworldhaskell.org"
>Real World Haskell</a
>. <a href="#fnref3" class="footnoteBackLink" title="Jump back to footnote 3">↩</a></p></li>
<li id="fn4"
><p
>The kind of errors, that <code
>IOOBJC</code
> is used for, will not be thrown by <code
>toId</code
>, because in this case we are converting from a strongly typed language to a language with a weaker type system. So we are loosing information about the types. <code
>IOBJC</code
> is there to deal with errors, where we don’t have enough information about the types of the values at compile time. <a href="#fnref4" class="footnoteBackLink" title="Jump back to footnote 4">↩</a></p></li>
<li id="fn5"
><p
>In Haskell numeric types are not only first-class citizens, but you can define your own numeric types and do quite interesting stuff as explained in chapter 13 of <a href="http://www.realworldhaskell.org"
>Real World Haskell</a
>. <a href="#fnref5" class="footnoteBackLink" title="Jump back to footnote 5">↩</a></p></li>
<li id="fn6"
><p
>Maybe it would be nice to provide an instance for complete numeric typeclasses, like <code
>Fractional a</code
>. <a href="#fnref6" class="footnoteBackLink" title="Jump back to footnote 6">↩</a></p></li>
<li id="fn7"
><p
><code
>Data.Text</code
> is not yet part of the <a href="http://hackage.haskell.org/platform/"
>Haskell Platform</a
>, but can easily be installed from <a href="http://hackage.haskell.org/package/text-0.7.0.1"
>hackage</a
>. <a href="#fnref7" class="footnoteBackLink" title="Jump back to footnote 7">↩</a></p></li>
<li id="fn8"
><p
>In order to define an instance for <code
>String</code
>, which is itself a type synonym for <code
>[Char]</code
>, we have to switch on the <code
>TypeSynonymInstances</code
> extension of GHC. <a href="#fnref8" class="footnoteBackLink" title="Jump back to footnote 8">↩</a></p></li>
<li id="fn9"
><p
>All source is licensed under the <a href="http://www.apache.org/licenses/LICENSE-2.0"
>Apache License</a
>, Version 2.0. <a href="#fnref9" class="footnoteBackLink" title="Jump back to footnote 9">↩</a></p></li>
<li id="fn10"
><p
><em
>(Warning: Sloppy mathematics!)</em
> In a sense the mapping of every argument to its result for a <em
>pure</em
> function can be expressed in tuples. Therefore this similarity of tuples and pure functions is not that much of a surprise. <a href="#fnref10" class="footnoteBackLink" title="Jump back to footnote 10">↩</a></p></li>
</ol></div>Tim Schefflerhttp://www.blogger.com/profile/00287780663826424312noreply@blogger.com5tag:blogger.com,1999:blog-3273671087241726593.post-38493416615927481512010-02-03T15:39:00.003+01:002010-02-05T13:07:34.812+01:00Curry’n’C Converter — Using Haskell with Objective-C in the Classic Cocoa Tutorial<div id="currync-converter---using-haskell-with-objective-c-in-the-classic-cocoa-tutorial"
><!-- ## Abstract --><br />
<p
>Using the classic <a href="http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/ObjCTutorial/01Introduction/01Introduction.html"
>“Currency Converter”</a
> Objective-C tutorial application as an example I show how to implement the model layer of a Cocoa application in Haskell:</p><ul
><li
><p
>I explain how to modify the standard Xcode project for building a mixed Haskell/Objective-C application.</p></li>
<li
><p
>I create an Objective-C wrapper class for the Haskell model part that hides the Haskell implementation from the Objective-C controller layer. Objects of this wrapper class are pure Objective-C objects and can utilize the Objective-C garbage collector for triggering the freeing of used Haskell data.</p></li>
<li
><p
>The result is a fully native OS X application.</p></li>
</ul><div id="introduction"
><h2
>Introduction</h2><p
>The native way to write an application for Mac OS X is by using Cocoa, which is an Objective-C framework. Therefore programming in Objective-C offers the full potential of Cocoa and helps to create user-friendly applications for the Mac. But as much as Objective-C is preferable for implementing the interaction with the user or the communication with the OS X system, in modeling specific problems other programming languages might be better suited.</p><p
>Haskell is functional programming language that is particularly good in dealing with abstract data. It has a very concise syntax and its type system offers great help to the developer for precisely describing difficult problems. And as Haskell is a compiled language you do not have to trade in speed for expressiveness. This makes Haskell an excellent implementation language for the model layer of an application.</p></div><div id="preparation"
><h2
>Preparation</h2><p
>This posting assumes that you have a basic knowledge of Objective-C and Cocoa. Ideally, you already have worked your way through the original <a href="http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/ObjCTutorial/01Introduction/01Introduction.html"
>“Currency Converter”</a
> tutorial before.</p><p
>There are many good resources for learning Haskell, but as one does not want to read <em
>all</em
> the books, I would recommend <a href="http://www.realworldhaskell.org"
>“Real World Haskell”</a
>. If you just want a short introduction to Haskell take a look at alpheccar’s <a href="http://www.alpheccar.org/en/posts/show/67"
>“Haskell Study Plan”</a
>.</p><p
>Used software:</p><ul
><li
><p
>Mac OS X “Snow Leopard”, Version 10.6.2.</p></li>
<li
><p
>Xcode, Version 3.2.1.</p></li>
<li
><p
>The <a href="http://hackage.haskell.org/platform/"
>Haskell Platform</a
>, Version 2009.2.0.2.<sup
><a href="#fn1" class="footnoteRef" id="fnref1"
>1</a
></sup
></p></li>
<li
><p
>Python. The pre-installed version on Snow Leopard is fine.</p></li>
</ul><p
>We start with the up and running full Objective-C version of the <a href="http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/ObjCTutorial/01Introduction/01Introduction.html"
>“Currency Converter”</a
> application. It is used by Apple as an example of the Model-View-Controller (MVC) design pattern<sup
><a href="#fn2" class="footnoteRef" id="fnref2"
>2</a
></sup
>. In the course of the current tutorial we will step-by-step replace the Objective-C version of the model-layer by a Haskell implementation.</p></div><div id="using-haskell-with-objective-c"
><h2
>Using Haskell with Objective-C</h2><p
>There is currently no direct support of bridging Haskell and Objective-C via Haskell’s Foreign Function Interface <a href="http://www.haskell.org/haskellwiki/GHC/Using_the_FFI"
>(FFI)</a
>,<sup
><a href="#fn3" class="footnoteRef" id="fnref3"
>3</a
></sup
> so we will use C as a mediating layer.</p><p
>There are basically two ways of using Haskell and Objective-C in the same application:</p><ol style="list-style-type: decimal;"
><li
><p
>Use Objective-C as the “main language” and initializing Haskell from the Objective-C application. Here the Haskell part is basically provided as a library that will be called from C.</p></li>
<li
><p
>Call the Objective-C runtime from an outer Haskell layer.</p></li>
</ol><p
>The first approach is made complicated as currently GHC cannot create stand-alone libraries for use with Objective-C. <sup
><a href="#fn4" class="footnoteRef" id="fnref4"
>4</a
></sup
> One way the get around this limitation is to directly patch the GHC to create fully linked libs, as done in the <a href="http://projects.haskell.org/ghc-iphone/"
>GHC-iPhone</a
> project. Another approach is to compile the Haskell part to an object file (<code
>.o</code
>-file) and then include this into the Xcode project. As the <code
>.o</code
>-file has not the necessary Haskell libraries linked, this has to be done by hand in Xcode. A nice tutorial that shows this, can be found on the <a href="http://www.haskell.org/haskellwiki/Using_Haskell_in_an_Xcode_Cocoa_project"
>Haskel Wiki</a
>.</p><p
>For this tutorial I choose to use Haskell as the main language and to call the Objective-C runtime from inside the Haskell <code
>main</code
> function. This is actually quite easy using the Foreign Function Interface <a href="http://www.haskell.org/haskellwiki/GHC/Using_the_FFI"
>(FFI)</a
>:</p><p
>When we take a look at the standard <code
>main.m</code
> of a Cocoa project</p><pre class="sourceCode objectivec"
><code
><span class="Others Preprocessor"
>#import </span
><span class="Others Prep.Lib"
><Cocoa/Cocoa.h></span
>
<span class="DataType DataType"
>int</span
><span class="Normal NormalText"
> main</span
><span class="Normal Symbol"
>(</span
><span class="DataType DataType"
>int</span
><span class="Normal NormalText"
> argc</span
><span class="Normal Symbol"
>,</span
><span class="Normal NormalText"
> </span
><span class="DataType DataType"
>char</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
>argv</span
><span class="Normal Symbol"
>[])</span
>
<span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>return</span
><span class="Normal NormalText"
> NSApplicationMain</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>argc</span
><span class="Normal Symbol"
>,</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>(</span
><span class="DataType DataType"
>const</span
><span class="Normal NormalText"
> </span
><span class="DataType DataType"
>char</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>**)</span
><span class="Normal NormalText"
> argv</span
><span class="Normal Symbol"
>);</span
>
<span class="Normal Symbol"
>}</span
>
</code
></pre><p
>we see, that all we have to do is to call the <code
>NSApplicationMain</code
> function from Haskell to start a Cocoa application. The <a href="http://developer.apple.com/mac/library/documentation/cocoa/reference/ApplicationKit/Miscellaneous/AppKit_Functions/Reference/reference.html#//apple_ref/doc/uid/20000695-DontLinkElementID_15"
>documentation</a
> states that</p><blockquote
><p
><strong
>Return Value</strong
><br />
This method never returns a result code. Instead, it calls the exit function to exit the application and terminate the process. If you want to determine why the application exited, you should look at the result code from the exit function instead.</p></blockquote><p
>Furthermore the arguments to <code
>NSApplicationMain</code
> will be ignored.</p><p
>An example Haskell implementation of <code
>main.m</code
> would therefore look like that:</p><pre class="sourceCode haskell"
><code
><span class="Comment"
>{-# LANGUAGE ForeignFunctionInterface #-}</span
>
<span class="Keyword"
>import</span
><span class="Normal NormalText"
> Foreign</span
>
<span class="Keyword"
>import</span
><span class="Normal NormalText"
> </span
><span class="Normal ModuleName"
>Foreign.C.Types</span
>
<span class="Normal NormalText"
>foreign </span
><span class="Keyword"
>import</span
><span class="Normal NormalText"
> ccall </span
><span class="String"
>"Cocoa.h NSApplicationMain"</span
><span class="Normal NormalText"
> </span
>
<span class="Normal NormalText"
> </span
><span class="Function FunctionDefinition"
>c_NSApplicationMain ::</span
><span class="Normal NormalText"
> CInt -> Ptr (Ptr CChar) -> </span
><span class="DataType TypeConstructor"
>IO</span
><span class="Normal NormalText"
> CInt</span
>
<span class="Normal NormalText"
>main = c_NSApplicationMain </span
><span class="DecVal Decimal"
>0</span
><span class="Normal NormalText"
> nullPtr</span
>
</code
></pre><p
>The first lines just tell the GHC that we want to use the FFI and import the necessary modules. The line starting with <code
>foreign import</code
> makes the <code
>NSApplicationMain</code
> function available for usage in Haskell. It gives it a new name <code
>c_NSApplicationMain</code
> and defines its argument and return types. Having done that, we are ready to use this function as the only action in the <code
>main</code
> function. Please note that we are passing 0 arguments and the <code
>nullPtr</code
> which is basically the <code
>NULL</code
> pointer in C.</p><p
>We will now take this template and add the model functionality for inclusion in the Currency Converter application.</p></div><div id="currync-converter-a-first-try"
><h2
>Curry’n’C Converter: A first try</h2><p
>In order to plug-in a Haskell model we will start with modifying the <code
>ConverterController.m</code
> like this:</p><pre class="sourceCode objectivec"
><code
><span class="Keyword"
>extern</span
><span class="Normal NormalText"
> </span
><span class="DataType DataType"
>double</span
><span class="Normal NormalText"
> convert</span
><span class="Normal Symbol"
>(</span
><span class="DataType DataType"
>double</span
><span class="Normal NormalText"
> amount</span
><span class="Normal Symbol"
>,</span
><span class="Normal NormalText"
> </span
><span class="DataType DataType"
>double</span
><span class="Normal NormalText"
> rate</span
><span class="Normal Symbol"
>);</span
>
<span class="Keyword"
>@implementation</span
><span class="Normal NormalText"
> ConverterController</span
>
<span class="Normal Symbol"
>-</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>IBAction</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
>convert</span
><span class="Normal Symbol"
>:(</span
><span class="Normal NormalText"
>id</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
>sender </span
><span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> </span
><span class="DataType DataType"
>double</span
><span class="Normal NormalText"
> sourceAmount </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>dollarField doubleValue</span
><span class="Normal Symbol"
>];</span
>
<span class="Normal NormalText"
> </span
><span class="DataType DataType"
>double</span
><span class="Normal NormalText"
> rate </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>rateField doubleValue</span
><span class="Normal Symbol"
>];</span
>
<span class="Normal NormalText"
> </span
><span class="DataType DataType"
>double</span
><span class="Normal NormalText"
> targetAmount </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> convert</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>sourceAmount</span
><span class="Normal Symbol"
>,</span
><span class="Normal NormalText"
> rate</span
><span class="Normal Symbol"
>);</span
>
<span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>amountField setDoubleValue</span
><span class="Normal Symbol"
>:</span
><span class="Normal NormalText"
>targetAmount</span
><span class="Normal Symbol"
>];</span
><span class="Normal NormalText"
> </span
>
<span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>rateField selectText</span
><span class="Normal Symbol"
>:</span
><span class="Keyword"
>self</span
><span class="Normal Symbol"
>];</span
><span class="Normal NormalText"
> </span
>
<span class="Normal Symbol"
>}</span
>
<span class="Keyword"
>@end</span
>
</code
></pre><p
>The generation of a model object has been completely dropped from the controller and we now call a C-function in order to do the currency conversion. This function is declared as <code
>extern</code
> and it will finally be provided by Haskell, but for now — in order to make the project compile again — we will use a dummy implementation of this function in C.</p><p
>For this we will rename <code
>main.m</code
> to <code
>haskell_dummy_interface.m</code
> and include a dummy implementation of the <code
>convert</code
> function:</p><pre class="sourceCode objectivec"
><code
><span class="Others Preprocessor"
>#import </span
><span class="Others Prep.Lib"
><Cocoa/Cocoa.h></span
>
<span class="DataType DataType"
>double</span
><span class="Normal NormalText"
> convert</span
><span class="Normal Symbol"
>(</span
><span class="DataType DataType"
>double</span
><span class="Normal NormalText"
> amount</span
><span class="Normal Symbol"
>,</span
><span class="Normal NormalText"
> </span
><span class="DataType DataType"
>double</span
><span class="Normal NormalText"
> rate</span
><span class="Normal Symbol"
>)</span
>
<span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>return</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>-</span
><span class="Float"
>42.0</span
><span class="Normal Symbol"
>;</span
>
<span class="Normal Symbol"
>}</span
>
<span class="DataType DataType"
>int</span
><span class="Normal NormalText"
> main</span
><span class="Normal Symbol"
>(</span
><span class="DataType DataType"
>int</span
><span class="Normal NormalText"
> argc</span
><span class="Normal Symbol"
>,</span
><span class="Normal NormalText"
> </span
><span class="DataType DataType"
>char</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
>argv</span
><span class="Normal Symbol"
>[])</span
>
<span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>return</span
><span class="Normal NormalText"
> NSApplicationMain</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>argc</span
><span class="Normal Symbol"
>,</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>(</span
><span class="DataType DataType"
>const</span
><span class="Normal NormalText"
> </span
><span class="DataType DataType"
>char</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>**)</span
><span class="Normal NormalText"
> argv</span
><span class="Normal Symbol"
>);</span
>
<span class="Normal Symbol"
>}</span
>
</code
></pre><p
>We still have the <code
>main</code
> function in there as well as the new <code
>convert</code
> function. All these functions are just provided to make Xcode compile the project as before. In a second step these implementations will be replaced by the Haskell versions. The dummy implementation of <code
>convert</code
> just gives back a nonsense value no matter what values you want to convert. Build & run the application with Xcode as a test: the build process should work without warnings or errors just as before.</p><p
>Now it’s time to replace the dummy C functions with the real Haskell implementations! Create a new file in Xcode called “Converter.hs” with the following content:</p><pre class="sourceCode haskell"
><code
><span class="Comment"
>{-# LANGUAGE ForeignFunctionInterface #-}</span
>
<span class="Keyword"
>import</span
><span class="Normal NormalText"
> Foreign</span
>
<span class="Keyword"
>import</span
><span class="Normal NormalText"
> </span
><span class="Normal ModuleName"
>Foreign.C.Types</span
>
<span class="Normal NormalText"
>foreign </span
><span class="Keyword"
>import</span
><span class="Normal NormalText"
> ccall </span
><span class="String"
>"Cocoa.h NSApplicationMain"</span
><span class="Normal NormalText"
> </span
>
<span class="Normal NormalText"
> </span
><span class="Function FunctionDefinition"
>c_NSApplicationMain ::</span
><span class="Normal NormalText"
> CInt -> Ptr (Ptr CChar) -> </span
><span class="DataType TypeConstructor"
>IO</span
><span class="Normal NormalText"
> CInt</span
>
<span class="Normal NormalText"
>foreign export ccall </span
><span class="Function FunctionDefinition"
>convert ::</span
><span class="Normal NormalText"
> CDouble -> CDouble -> CDouble</span
>
<span class="Function FunctionDefinition"
>convert ::</span
><span class="Normal NormalText"
> CDouble -> CDouble -> CDouble</span
>
<span class="Normal NormalText"
>convert amount rate = amount * rate</span
>
<span class="Normal NormalText"
>main = c_NSApplicationMain </span
><span class="DecVal Decimal"
>0</span
><span class="Normal NormalText"
> nullPtr</span
>
</code
></pre><p
>This file is just the previously discussed <code
>main</code
> Haskell implementation plus the definition of the <code
>convert</code
> function that will be called by the C code: The <code
>convert</code
> function just takes two <code
>CDouble</code
> arguments, multiplies them, and returns the result again as a <code
>CDouble</code
>. This definition is made public for C via the <code
>foreign export</code
> line. The type <code
>CDouble</code
> is translated to a plain <code
>double</code
> value in C by the FFI (this type — and others — is provided by the <code
>Foreign.C.Types</code
> module, as you might have guessed).</p><p
>After getting all the source files right we now have to build the application. For this we proceed in the following way:</p><ol style="list-style-type: decimal;"
><li
><p
>Do a plain vanilla build of the pure Xcode project for the <code
>*.m</code
> files. Using our <code
>haskell_dummy_interface.m</code
> file as a placeholder for the Haskell part. This is done via the normal Xcode build mechanism.</p></li>
<li
><p
>Remove the <code
>haskell_dummy_interface.o</code
> object file from the build directory as it will be replaced by a Haskell version.</p></li>
<li
><p
>Compile the Haskell file <code
>Converter.hs</code
> with GHC. We will pass the necessary frameworks to GHC via its <code
>-framework</code
> flag. Also, if we later want to call functions defined in the C part of the project we want to pass all necessary <code
>*.o</code
> object files to the GHC linker. It’s ok to just pass <em
>all</em
> <code
>*.o</code
> files (apart from the <code
>haskell_dummy_interface.o</code
>) to the GHC. The linker will take what it needs and just leave the rest.</p></li>
<li
><p
>Finally copy the finished Haskell executable to the application’s <code
>MacOS</code
> folder and give it the right name.</p></li>
</ol><p
>The result will be a complete Mac OS X application.<sup
><a href="#fn5" class="footnoteRef" id="fnref5"
>5</a
></sup
></p><p
>In order to automate this process I have written a short <a href="http://www.timscheffler.com/downloads/2010-02-03/ghc_xcode_script.py"
>python script</a
>. This script can now be called during the Xcode build as the last step of the build phase: Just choose the target in Xcode and add a new build phase via <em
>Project > New Build Phase > New Run Script Build Phase</em
>. This will create a new subfolder in the target build phase “Run Script” folder under the main target. Double-click this folder which will bring up an info box where you can paste the the python script. Just set the shell to <code
>/usr/bin/python</code
> and the script will be called as the last step of the build process.</p><p
>For your convenience I have provided this example Xcode project as a <a href="http://www.timscheffler.com/downloads/2010-02-03/CnC_Converter_A.zip"
>download</a
>.</p></div><div id="a-somewhat-more-complete-converter"
><h2
>A somewhat more complete converter</h2><p
>But this first try is some kind of cheating. We are just replacing a very simple C function with an extremely simple Haskell function. Most of the time the simplest solution is the best, but as the Currency Converter should be an example for more interesting problems — where the job of the model would involve more than just multiplying two numbers — it would be better to build a “real” Haskell model implementation.</p><p
>For a model we would like to be able to create data structures and then use functions on these data structures or, with the Currency Converter as an example, we would like to</p><ol style="list-style-type: decimal;"
><li
><p
>Create <code
>Converter</code
> “entities” (whatever this means).</p></li>
<li
><p
>Use these entities to <code
>convert</code
> dollar <code
>origAmount</code
> into another currency using an exchange <code
>rate</code
>.</p></li>
</ol><p
>This <code
>Converter</code
> model can easily be defined in Haskell:</p><pre class="sourceCode haskell"
><code
><span class="Keyword"
>data</span
><span class="Normal NormalText"
> Converter = Converter {</span
>
<span class="Normal NormalText"
> </span
><span class="Function FunctionDefinition"
>origAmount ::</span
><span class="Normal NormalText"
> </span
><span class="DataType TypeConstructor"
>Double</span
><span class="Normal NormalText"
>,</span
>
<span class="Normal NormalText"
> </span
><span class="Function FunctionDefinition"
>rate ::</span
><span class="Normal NormalText"
> </span
><span class="DataType TypeConstructor"
>Double</span
>
<span class="Normal NormalText"
> }</span
>
<span class="Function FunctionDefinition"
>convert ::</span
><span class="Normal NormalText"
> Converter -> </span
><span class="DataType TypeConstructor"
>Double</span
>
<span class="Normal NormalText"
>convert (Converter amount xrate) = amount * xrate</span
>
</code
></pre><p
>But this is just the Haskell part. In order to use the <code
>Converter</code
> implementation from C we will need at least three functions: one function two create a <code
>Converter</code
> entity and to pass some kind of reference for this entity to C, another function to call <code
>convert</code
> on this entity using the reference, and a last function to dispose of the created entity again.</p><p
>As the <em
>create</em
> and <em
>convert</em
> steps will be separated in the final C code we will have to tell the Haskell implementation to “store” the <code
>Converter</code
> entity after creation for a later use. This is exactly the use case for the <em
>Stable Pointer</em
> of the FFI: <code
>StablePtr</code
>.</p><p
>To quote the <a href="http://haskell.org/ghc/docs/latest/html/libraries/base/Foreign-StablePtr.html"
>documentation</a
>:</p><blockquote
><p
>A stable pointer is a reference to a Haskell expression that is guaranteed not to be affected by garbage collection, i.e., it will neither be deallocated nor will the value of the stable pointer itself change during garbage collection (ordinary references may be relocated during garbage collection). Consequently, stable pointers can be passed to foreign code, which can treat it as an opaque reference to a Haskell value.</p></blockquote><p
>Perfect!</p><p
>With the provided functions of the <code
>Foreign.StablePtr</code
> module we can now implement the C interface:</p><pre class="sourceCode haskell"
><code
><span class="Normal NormalText"
>foreign export ccall </span
>
<span class="Normal NormalText"
> </span
><span class="Function FunctionDefinition"
>c_newConverter ::</span
><span class="Normal NormalText"
> CDouble -> CDouble -> </span
><span class="DataType TypeConstructor"
>IO</span
><span class="Normal NormalText"
> (StablePtr Converter)</span
>
<span class="Normal NormalText"
>foreign export ccall </span
>
<span class="Normal NormalText"
> </span
><span class="Function FunctionDefinition"
>c_freeConverter ::</span
><span class="Normal NormalText"
> StablePtr Converter -> </span
><span class="DataType TypeConstructor"
>IO</span
><span class="Normal NormalText"
> ()</span
>
<span class="Normal NormalText"
>foreign export ccall </span
>
<span class="Normal NormalText"
> </span
><span class="Function FunctionDefinition"
>c_convert ::</span
><span class="Normal NormalText"
> StablePtr Converter -> </span
><span class="DataType TypeConstructor"
>IO</span
><span class="Normal NormalText"
> (CDouble)</span
>
<span class="Function FunctionDefinition"
>c_newConverter ::</span
><span class="Normal NormalText"
> CDouble -> CDouble -> </span
><span class="DataType TypeConstructor"
>IO</span
><span class="Normal NormalText"
> (StablePtr Converter)</span
>
<span class="Normal NormalText"
>c_newConverter amount rate = newStablePtr $ </span
>
<span class="Normal NormalText"
> Converter (</span
><span class="Function"
>realToFrac</span
><span class="Normal NormalText"
> amount) (</span
><span class="Function"
>realToFrac</span
><span class="Normal NormalText"
> rate)</span
>
<span class="Function FunctionDefinition"
>c_freeConverter ::</span
><span class="Normal NormalText"
> StablePtr Converter -> </span
><span class="DataType TypeConstructor"
>IO</span
><span class="Normal NormalText"
> ()</span
>
<span class="Normal NormalText"
>c_freeConverter = freeStablePtr</span
>
<span class="Function FunctionDefinition"
>c_convert ::</span
><span class="Normal NormalText"
> StablePtr Converter -> </span
><span class="DataType TypeConstructor"
>IO</span
><span class="Normal NormalText"
> (CDouble)</span
>
<span class="Normal NormalText"
>c_convert = (liftM (</span
><span class="Function"
>realToFrac</span
><span class="Normal NormalText"
> . convert) ) . deRefStablePtr</span
>
</code
></pre><p
>The type <code
>StablePtr Converter</code
> is seen in C as an opaque pointer of type <code
>void*</code
>. It is best to define a new C type <code
>HsStablePtr</code
> like <code
>typedef void *HsStablePtr</code
>, that will represent this opaque Haskell reference in the C code. This can be done in a header file <code
>FFI.h</code
> as described <a href="http://www.haskell.org/haskellwiki/GHC/Using_the_FFI"
>here</a
>.</p><p
>The resulting C types for the <code
>Converter</code
> API are then</p><pre class="sourceCode objectivec"
><code
><span class="Keyword"
>extern</span
><span class="Normal NormalText"
> HsStablePtr c_newConverter</span
><span class="Normal Symbol"
>(</span
><span class="DataType DataType"
>double</span
><span class="Normal NormalText"
> amount</span
><span class="Normal Symbol"
>,</span
><span class="Normal NormalText"
> </span
><span class="DataType DataType"
>double</span
><span class="Normal NormalText"
> rate</span
><span class="Normal Symbol"
>);</span
>
<span class="Keyword"
>extern</span
><span class="Normal NormalText"
> </span
><span class="DataType DataType"
>void</span
><span class="Normal NormalText"
> c_freeConverter</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>HsStablePtr converter</span
><span class="Normal Symbol"
>);</span
>
<span class="Keyword"
>extern</span
><span class="Normal NormalText"
> </span
><span class="DataType DataType"
>double</span
><span class="Normal NormalText"
> c_convert</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>HsStablePtr converter</span
><span class="Normal Symbol"
>);</span
>
</code
></pre><p
>This can now be called from the <code
>ConverterController.m</code
> directly like this:</p><pre class="sourceCode objectivec"
><code
><span class="Normal Symbol"
>-</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>IBAction</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
>convert</span
><span class="Normal Symbol"
>:(</span
><span class="Normal NormalText"
>id</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
>sender </span
><span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> </span
><span class="DataType DataType"
>double</span
><span class="Normal NormalText"
> sourceAmount </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>dollarField doubleValue</span
><span class="Normal Symbol"
>];</span
>
<span class="Normal NormalText"
> </span
><span class="DataType DataType"
>double</span
><span class="Normal NormalText"
> rate </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>rateField doubleValue</span
><span class="Normal Symbol"
>];</span
>
<span class="Normal NormalText"
> HsStablePtr converter </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> c_newConverter</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>sourceAmount</span
><span class="Normal Symbol"
>,</span
><span class="Normal NormalText"
> rate</span
><span class="Normal Symbol"
>);</span
>
<span class="Normal NormalText"
> </span
><span class="DataType DataType"
>double</span
><span class="Normal NormalText"
> targetAmount </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> c_convert</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>converter</span
><span class="Normal Symbol"
>);</span
>
<span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>amountField setDoubleValue</span
><span class="Normal Symbol"
>:</span
><span class="Normal NormalText"
>targetAmount</span
><span class="Normal Symbol"
>];</span
><span class="Normal NormalText"
> </span
>
<span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>rateField selectText</span
><span class="Normal Symbol"
>:</span
><span class="Keyword"
>self</span
><span class="Normal Symbol"
>];</span
><span class="Normal NormalText"
> </span
>
<span class="Normal NormalText"
> c_freeConverter</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>converter</span
><span class="Normal Symbol"
>);</span
>
<span class="Normal Symbol"
>}</span
>
</code
></pre><p
>And we have some kind of Haskell implementation of the model.</p><p
>But it’s not really that beautiful: First of all the controller has to deal directly with the Haskell values and secondly we have to free the Haskell entity manually inside the controller. It would be nice if all this Haskell handling can be abstracted away and the memory management should really be business of the Objective-C garbage collector.</p></div><div id="the-final-implementation"
><h2
>The final implementation</h2><p
>In order to hide the actual Haskell implementation from the controller, we will create a new wrapper model class, called <code
>HSConverter</code
>. It provides two methods for the controller:</p><pre class="sourceCode objectivec"
><code
><span class="Normal Symbol"
>-(</span
><span class="Normal NormalText"
>id</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
>initWithAmount</span
><span class="Normal Symbol"
>:(</span
><span class="DataType DataType"
>double</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
>amount rate</span
><span class="Normal Symbol"
>:(</span
><span class="DataType DataType"
>double</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
>rate</span
><span class="Normal Symbol"
>;</span
>
<span class="Normal Symbol"
>-(</span
><span class="DataType DataType"
>double</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
>convert</span
><span class="Normal Symbol"
>;</span
>
</code
></pre><p
>and the Haskell <code
>HsStablePtr</code
> reference is stored as a data member of the object and will be freed by <code
>c_freeConverter()</code
> during the garbage collection in Objective-C. <sup
><a href="#fn6" class="footnoteRef" id="fnref6"
>6</a
></sup
></p><p
>With this we can now define the implementation of <code
>HSConverter.m</code
>:</p><pre class="sourceCode objectivec"
><code
><span class="Keyword"
>extern</span
><span class="Normal NormalText"
> HsStablePtr c_newConverter</span
><span class="Normal Symbol"
>(</span
><span class="DataType DataType"
>double</span
><span class="Normal NormalText"
> amount</span
><span class="Normal Symbol"
>,</span
><span class="Normal NormalText"
> </span
><span class="DataType DataType"
>double</span
><span class="Normal NormalText"
> rate</span
><span class="Normal Symbol"
>);</span
>
<span class="Keyword"
>extern</span
><span class="Normal NormalText"
> </span
><span class="DataType DataType"
>void</span
><span class="Normal NormalText"
> c_freeConverter</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>HsStablePtr converter</span
><span class="Normal Symbol"
>);</span
>
<span class="Keyword"
>extern</span
><span class="Normal NormalText"
> </span
><span class="DataType DataType"
>double</span
><span class="Normal NormalText"
> c_convert</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>HsStablePtr converter</span
><span class="Normal Symbol"
>);</span
>
<span class="Keyword"
>@interface</span
><span class="Normal NormalText"
> HSConverter </span
><span class="Normal Symbol"
>:</span
><span class="Normal NormalText"
> NSObject </span
><span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> HsStablePtr converter</span
><span class="Normal Symbol"
>;</span
>
<span class="Normal Symbol"
>}</span
>
<span class="Keyword"
>@implementation</span
><span class="Normal NormalText"
> HSConverter</span
>
<span class="Normal Symbol"
>-(</span
><span class="Normal NormalText"
>id</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
>initWithAmount</span
><span class="Normal Symbol"
>:(</span
><span class="DataType DataType"
>double</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
>amount rate</span
><span class="Normal Symbol"
>:(</span
><span class="DataType DataType"
>double</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
>rate</span
><span class="Normal Symbol"
>;</span
>
<span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Keyword"
>super</span
><span class="Normal NormalText"
> init</span
><span class="Normal Symbol"
>];</span
>
<span class="Normal NormalText"
> converter </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> c_newConverter</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>amount</span
><span class="Normal Symbol"
>,</span
><span class="Normal NormalText"
> rate</span
><span class="Normal Symbol"
>);</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>return</span
><span class="Normal NormalText"
> </span
><span class="Keyword"
>self</span
><span class="Normal Symbol"
>;</span
>
<span class="Normal Symbol"
>}</span
>
<span class="Normal Symbol"
>-</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>(</span
><span class="DataType DataType"
>void</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
>finalize</span
><span class="Normal Symbol"
>;</span
><span class="Normal NormalText"
> </span
><span class="Comment"
>// this will be called by the GC</span
>
<span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> c_freeConverter</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>converter</span
><span class="Normal Symbol"
>);</span
>
<span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Keyword"
>super</span
><span class="Normal NormalText"
> finalize</span
><span class="Normal Symbol"
>];</span
>
<span class="Normal Symbol"
>}</span
>
<span class="Normal Symbol"
>-(</span
><span class="Normal NormalText"
>id</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
>init</span
><span class="Normal Symbol"
>;</span
>
<span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> </span
><span class="Comment"
>// hardened, in case someone uses the standard init method</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>return</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Keyword"
>self</span
><span class="Normal NormalText"
> initWithAmount</span
><span class="Normal Symbol"
>:</span
><span class="Float"
>1.0</span
><span class="Normal NormalText"
> rate</span
><span class="Normal Symbol"
>:-</span
><span class="Float"
>1.0</span
><span class="Normal Symbol"
>];</span
>
<span class="Normal Symbol"
>}</span
>
<span class="Normal Symbol"
>-(</span
><span class="DataType DataType"
>double</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
>convert</span
><span class="Normal Symbol"
>;</span
>
<span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> </span
><span class="Keyword"
>return</span
><span class="Normal NormalText"
> c_convert</span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>converter</span
><span class="Normal Symbol"
>);</span
><span class="Normal NormalText"
> </span
>
<span class="Normal Symbol"
>}</span
>
<span class="Keyword"
>@end</span
>
</code
></pre><p
>And accordingly the final version of the <code
>ConverterController.m</code
>:</p><pre class="sourceCode objectivec"
><code
><span class="Others Preprocessor"
>#import </span
><span class="Others Prep.Lib"
>"ConverterController.h"</span
>
<span class="Others Preprocessor"
>#import </span
><span class="Others Prep.Lib"
>"HSConverter.h"</span
>
<span class="Keyword"
>@implementation</span
><span class="Normal NormalText"
> ConverterController</span
>
<span class="Normal Symbol"
>-</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>(</span
><span class="Normal NormalText"
>IBAction</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
>convert</span
><span class="Normal Symbol"
>:(</span
><span class="Normal NormalText"
>id</span
><span class="Normal Symbol"
>)</span
><span class="Normal NormalText"
>sender </span
><span class="Normal Symbol"
>{</span
>
<span class="Normal NormalText"
> </span
><span class="DataType DataType"
>double</span
><span class="Normal NormalText"
> sourceAmount </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>dollarField doubleValue</span
><span class="Normal Symbol"
>];</span
>
<span class="Normal NormalText"
> </span
><span class="DataType DataType"
>double</span
><span class="Normal NormalText"
> rate </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>rateField doubleValue</span
><span class="Normal Symbol"
>];</span
>
<span class="Normal NormalText"
> HSConverter </span
><span class="Normal Symbol"
>*</span
><span class="Normal NormalText"
>converter </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[[</span
><span class="Normal NormalText"
>HSConverter alloc</span
><span class="Normal Symbol"
>]</span
><span class="Normal NormalText"
> </span
>
<span class="Normal NormalText"
> initWithAmount</span
><span class="Normal Symbol"
>:</span
><span class="Normal NormalText"
>sourceAmount rate</span
><span class="Normal Symbol"
>:</span
><span class="Normal NormalText"
>rate</span
><span class="Normal Symbol"
>];</span
>
<span class="Normal NormalText"
> </span
><span class="DataType DataType"
>double</span
><span class="Normal NormalText"
> targetAmount </span
><span class="Normal Symbol"
>=</span
><span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>converter convert</span
><span class="Normal Symbol"
>];</span
>
<span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>amountField setDoubleValue</span
><span class="Normal Symbol"
>:</span
><span class="Normal NormalText"
>targetAmount</span
><span class="Normal Symbol"
>];</span
><span class="Normal NormalText"
> </span
>
<span class="Normal NormalText"
> </span
><span class="Normal Symbol"
>[</span
><span class="Normal NormalText"
>rateField selectText</span
><span class="Normal Symbol"
>:</span
><span class="Keyword"
>self</span
><span class="Normal Symbol"
>];</span
><span class="Normal NormalText"
> </span
>
<span class="Normal Symbol"
>}</span
>
<span class="Keyword"
>@end</span
>
</code
></pre><p
>This controller now does not know that it is dealing with a Haskell implementation of the converter and has not to deal with manual memory management any more. In fact it looks more or less like the original implementation of the controller in the all Objective-C version. These <code
>HSConverter</code
> objects are plain Objective-C objects and can be passed to other methods or stored in <code
>NSArray</code
>s, for example.</p><p
>And here is the final Xcode project as a <a href="http://www.timscheffler.com/downloads/2010-02-03/CnC_Converter_B.zip"
>download</a
>.</p></div><div id="related-work"
><h2
>Related work</h2><p
>The way of calling the Objective-C runtime from Haskell via the FFI as presented in this posting is — to my knowledge — unique. The other Objective-C / Haskell works are calling the Haskell runtime from the Objective-C application:</p><ul
><li
><p
>The Objective-C / Haskell tutorial on the <a href="http://www.haskell.org/haskellwiki/Using_Haskell_in_an_Xcode_Cocoa_project"
>Haskell wiki</a
>, which is also used a basis for another Currency Converter tutorial by <a href="http://lachlanrambling.blogspot.com/2010/01/cocoa-vs-haskell.html"
>Lachlan O’Dea</a
>.</p></li>
<li
><p
>The <a href="http://projects.haskell.org/ghc-iphone/"
>GHC-iPhone</a
> project, that patches the GHC to produce complete static libs for inclusion into an Xcode project.</p></li>
</ul><p
>Of course there is also HOC: <a href="http://code.google.com/p/hoc/"
>A Haskell to Objective-C Binding</a
>. HOC offers a complete binding of Objective-C and Cocoa to Haskell, just like <a href="http://pyobjc.sourceforge.net/"
>PyObjC</a
> or <a href="http://rubycocoa.sourceforge.net/HomePage"
>RubyCocoa</a
>. HOC is much more sophisticated and is an ideal solution if you want to build large Cocoa applications completely in Haskell. But there might be a class of simple projects, where using the plain-vanilla approach — as shown here — is good solution.</p></div><div id="conclusion"
><h2
>Conclusion</h2><p
>This posting mainly reflects my own learning experience. I hope that it will help others in building Haskell/Objective-C applications. It surely is just a starting point.</p><p
>The way I am using a dummy C interface just to make Xcode compile the project and then throw away the dummy object file and use GHC to create the ‘real’ object can really only be called ‘work-around’, if not ‘hack’. But there is currently no clean way to compile mixed Haskell / Objective-C applications. The other <a href="http://www.haskell.org/haskellwiki/Using_Haskell_in_an_Xcode_Cocoa_project"
>approach</a
> also has to deal with this problem in its own way. Once the GHC is able to build complete (static) libs for the Mac it will be much simpler to create Cocoa applications with Haskell.</p><p
>The Currency Converter really is a too trivial problem and it seems like overkill to use Haskell and the whole bridging infrastructure for such a simple task. But it stands just as an example for how it might be possible to implement a model in Haskell for a more interesting MVC application.</p><!-- References --><br />
<br />
</div></div><div class="footnotes"
><hr
/><ol
><li id="fn1"
><p
>You will need to patch the GHC Haskell compiler to build 32-bit code in Snow Leopard. Please see <a href="http://obvioushints.blogspot.com/2009/09/running-haskell-ghc-on-snow-leopard.html"
>this posting</a
> by Alvaro Videla. A 64-bit GHC is as-of-today not yet ready for Snow Leopard. <a href="#fnref1" class="footnoteBackLink" title="Jump back to footnote 1">↩</a></p></li>
<li id="fn2"
><p
>The MVC design pattern is explained in the linked <a href="http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/ObjCTutorial/01Introduction/01Introduction.html"
>Apple tutorial</a
>. <a href="#fnref2" class="footnoteBackLink" title="Jump back to footnote 2">↩</a></p></li>
<li id="fn3"
><p
>There is <a href="http://hackage.haskell.org/trac/ghc/wiki/ObjectiveC"
>some research</a
> going on to extend the existing foreign function interface for direct Haskell/Objective-C support. <a href="#fnref3" class="footnoteBackLink" title="Jump back to footnote 3">↩</a></p></li>
<li id="fn4"
><p
>There seems to be some support for <a href="http://www.haskell.org/ghc/docs/6.4/html/users_guide/win32-dlls.html"
>windows DLLs</a
>, though. Also, the next <a href="http://www.reddit.com/r/haskell/comments/ancqi/cocoa_vs_haskell/c0igikr"
>version of GHC</a
> is supposed to have support for dynamic libraries for the Mac. <a href="#fnref4" class="footnoteBackLink" title="Jump back to footnote 4">↩</a></p></li>
<li id="fn5"
><p
>It will just be the 32-bit i686 version of the application as GHC will not create the PowerPC binary or the 64-bit version. <a href="#fnref5" class="footnoteBackLink" title="Jump back to footnote 5">↩</a></p></li>
<li id="fn6"
><p
>Please remember to switch on garbage collection support in the Xcode project! </p></li>
</ol></div>Tim Schefflerhttp://www.blogger.com/profile/00287780663826424312noreply@blogger.com5tag:blogger.com,1999:blog-3273671087241726593.post-30834828343746519242010-01-26T11:54:00.001+01:002010-02-15T12:43:02.297+01:00TorchFS — New public repository<div id="torchfs---new-public-repository"
><p
>In the last days I did some cleaning up of the source code and today I published <a href="http://tscheff.blogspot.com/2010/01/torchfs-filter-for-your-files-and.html"
>TorchFS</a
> as open source on <a href="http://github.com/tscheff/TorchFS"
>github</a
>.</p><div id="updates"
><h2
>Updates</h2><p
>Please follow the TorchFS twitter <a href="http://twitter.com/TorchFS"
>feed</a
> for updates!</p></div></div>Tim Schefflerhttp://www.blogger.com/profile/00287780663826424312noreply@blogger.com0tag:blogger.com,1999:blog-3273671087241726593.post-34084867985167273302010-01-21T17:42:00.002+01:002010-02-15T12:43:36.601+01:00TorchFS — A filter for your files and folders on the Mac<div id="torchfs---a-filter-for-your-files-and-folders-on-the-mac"
><p
>I am happy to announce the first <strong
>beta</strong
> test version of my new Mac project “TorchFS.”</p><div id="torchfs-is-a-filter-for-your-file-system"
><h2
>TorchFS is a filter for your file system</h2><p
>TorchFS displays your folder <em
>structure</em
> filtered by your search criteria. This helps to manage large amounts of files.</p><p
><em
>For example:</em
> You want to focus on stuff that happened in 2010 on your computer. TorchFS filters the given folder structure (“Documents”, “Pictures”, “Music”, …) and only displays entries, that have seen some action in 2010, dropping older stuff from the focus.</p><p
><img src="http://timscheffler.com/images/Blog/2010-01-21/finder_without_torchFS.png" alt="“A normal finder view of an user’s home folder”"
/> <br />
This is a picture of an example user home folder. You see a lot of folders that might be a bit older and that take up the user’s attention without need.</p><p
><img src="http://timscheffler.com/images/Blog/2010-01-21/finder_with_torchFS.png" alt="“A finder view of an user’s home folder, but filtered for files of 2010”"
/> <br />
Here you see the same home folder, but this time TorchFS has filtered out all unnecessary folders, which helps to focus on the folders that are relevant in 2010.</p></div><div id="raison-dêtre"
><h2
>Raison d’être</h2><p
>Computers get more powerful by the year and the amount of files we store on them gets bigger and bigger. You probably have files as old as 10 years (or even older) stored somewhere in the hierarchy of your home folder. All this stuff wont go away, hopefully. But we need an efficient way to deal with this abundance of information.</p><p
>My personal story is, that sometime in 2009 I noticed that my “Documents” folder had grown to a size that was unmanageable for me: it contained documents from several years and — as I am a lazy person — it had no deeper folder structure inside the “Documents” folder. So I decided to try the next best thing and started to create subfolders called 2009, 2008, 2007 and so on and moved the stuff to these subfolders.</p><p
>But after some weeks I got tired of this chore and remembered that I had a computer, that is more than able to do this boring work for me. And my computer could not only categorize my stuff by years, but by using Spotlight it was able to apply any criteria to a collection of files that I needed. This was basically the idea behind TorchFS: to move away from <em
>manual</em
> work of categorizing my files and to leave this work to the computer.</p></div><div id="behind-the-scenes-spotlight-and-macfuse"
><h2
>Behind the scenes: Spotlight and MacFUSE</h2><p
>TorchFS works by using Apple’s Spotlight search engine and Google’s <a href="http://code.google.com/p/macfuse/"
>MacFUSE</a
> user-space filesystem framework. TorchFS creates a Spotlight query and displays the found files in their original folder hierarchy in a virtual filesytem provided by MacFUSE. This way no new folders or files are created on your hard-drive. In fact the files are not touched at all, but TorchFS will display a virtual file alias that will take you to the original file.</p><p
>TorchFS builds on the SpotlightFS file system, which is part of the standard MacFUSE distribution by Google, but extends its functionality to preserve the original folder structure of the found results.</p></div><div id="differences-to-the-standard-spotlight-result-window"
><h2
>Differences to the standard Spotlight result window</h2><p
>The Finder can display the results of a Spotlight query, called “smart folder”, in any of its view options, but not in the hierarchical view, that can be used for “normal” folders. If you want to know the location of a smart folder item you have to click on it and its location will be displayed at the bottom of the Finder window.</p><p
>This way you lose much of the original meta-structure that could help you to tell the relevance of the found item in the context of other files.</p><p
>Additionally the normal Finder view of a smart folder usually gives you too many results for a broad query like “2009”: too much files to get a good overview of what happened last year.</p><p
>These two points are solved with TorchFS.</p></div><div id="how-to-get-torchfs"
><h2
>How to get TorchFS</h2><p
>First of all: Please remember, that this is a <strong
>beta</strong
> release. So this version might contain bugs and if you decide to use TorchFS you will do so <em
>at your own risk!</em
></p><p
>In order to use TorchFS you first have to install MacFUSE: Please go to the <a href="http://code.google.com/p/macfuse/"
>MacFUSE</a
> website and follow the instructions for installing.</p><p
>After you have installed MacFUSE, just download and unpack the zip-file containing the TorchFS app. This file can be found <a href="http://www.timscheffler.com/downloads/TorchFS.zip"
>here</a
>.</p><p
>To start TorchFS just double-click it. It will present some basic system smart folders like “All Documents” along with your own smart folders as TorchFS folder (Please consult you Mac’s help system on how to construct smart folders). If you like you can drag these TorchFS folders to your Finder’s “Places” shortcuts menu for easy access. (In order to see new smart folders in TorchFS you have to restart the TorchFS.app)</p></div><div id="why-torch"
><h2
>Why “Torch”?</h2><p
>Why this name? I wanted to keep a reference to Spotlight in the name. And a torch only illuminates a very small area and it can be used to follow a path. Just like the path through the folder structure. But on the other hand, it’s just a name.</p></div><div id="updates"
><h2
>Updates</h2><p
>Please follow the TorchFS twitter <a href="http://twitter.com/TorchFS"
>feed</a
> for updates!</p></div></div>Tim Schefflerhttp://www.blogger.com/profile/00287780663826424312noreply@blogger.com2