Wednesday 3 February 2010

Curry’n’C Converter — Using Haskell with Objective-C in the Classic Cocoa Tutorial


Using the classic “Currency Converter” Objective-C tutorial application as an example I show how to implement the model layer of a Cocoa application in Haskell:

  • I explain how to modify the standard Xcode project for building a mixed Haskell/Objective-C application.

  • 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.

  • The result is a fully native OS X application.

Introduction

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.

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.

Preparation

This posting assumes that you have a basic knowledge of Objective-C and Cocoa. Ideally, you already have worked your way through the original “Currency Converter” tutorial before.

There are many good resources for learning Haskell, but as one does not want to read all the books, I would recommend “Real World Haskell”. If you just want a short introduction to Haskell take a look at alpheccar’s “Haskell Study Plan”.

Used software:

  • Mac OS X “Snow Leopard”, Version 10.6.2.

  • Xcode, Version 3.2.1.

  • The Haskell Platform, Version 2009.2.0.2.1

  • Python. The pre-installed version on Snow Leopard is fine.

We start with the up and running full Objective-C version of the “Currency Converter” application. It is used by Apple as an example of the Model-View-Controller (MVC) design pattern2. 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.

Using Haskell with Objective-C

There is currently no direct support of bridging Haskell and Objective-C via Haskell’s Foreign Function Interface (FFI),3 so we will use C as a mediating layer.

There are basically two ways of using Haskell and Objective-C in the same application:

  1. 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.

  2. Call the Objective-C runtime from an outer Haskell layer.

The first approach is made complicated as currently GHC cannot create stand-alone libraries for use with Objective-C. 4 One way the get around this limitation is to directly patch the GHC to create fully linked libs, as done in the GHC-iPhone project. Another approach is to compile the Haskell part to an object file (.o-file) and then include this into the Xcode project. As the .o-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 Haskel Wiki.

For this tutorial I choose to use Haskell as the main language and to call the Objective-C runtime from inside the Haskell main function. This is actually quite easy using the Foreign Function Interface (FFI):

When we take a look at the standard main.m of a Cocoa project

#import <Cocoa/Cocoa.h>

int main(int argc, char *argv[])
{
    return NSApplicationMain(argc,  (const char **) argv);
}

we see, that all we have to do is to call the NSApplicationMain function from Haskell to start a Cocoa application. The documentation states that

Return Value
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.

Furthermore the arguments to NSApplicationMain will be ignored.

An example Haskell implementation of main.m would therefore look like that:

{-# LANGUAGE ForeignFunctionInterface #-}

import Foreign
import Foreign.C.Types

foreign import ccall "Cocoa.h NSApplicationMain" 
    c_NSApplicationMain :: CInt -> Ptr (Ptr CChar) -> IO CInt

main = c_NSApplicationMain 0 nullPtr

The first lines just tell the GHC that we want to use the FFI and import the necessary modules. The line starting with foreign import makes the NSApplicationMain function available for usage in Haskell. It gives it a new name c_NSApplicationMain and defines its argument and return types. Having done that, we are ready to use this function as the only action in the main function. Please note that we are passing 0 arguments and the nullPtr which is basically the NULL pointer in C.

We will now take this template and add the model functionality for inclusion in the Currency Converter application.

Curry’n’C Converter: A first try

In order to plug-in a Haskell model we will start with modifying the ConverterController.m like this:

extern double convert(double amount, double rate);

@implementation ConverterController

- (IBAction)convert:(id)sender {
    double sourceAmount = [dollarField doubleValue];
    double rate = [rateField doubleValue];

    double targetAmount = convert(sourceAmount, rate);

    [amountField setDoubleValue:targetAmount]; 
    [rateField selectText:self]; 
}

@end

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 extern 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.

For this we will rename main.m to haskell_dummy_interface.m and include a dummy implementation of the convert function:

#import <Cocoa/Cocoa.h>

double convert(double amount, double rate)
{
    return -42.0;
}

int main(int argc, char *argv[])
{
    return NSApplicationMain(argc,  (const char **) argv);
}

We still have the main function in there as well as the new convert 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 convert 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.

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:

{-# LANGUAGE ForeignFunctionInterface #-}
import Foreign
import Foreign.C.Types

foreign import ccall "Cocoa.h NSApplicationMain" 
    c_NSApplicationMain :: CInt -> Ptr (Ptr CChar) -> IO CInt
foreign export ccall convert :: CDouble -> CDouble -> CDouble

convert :: CDouble -> CDouble -> CDouble
convert amount rate = amount * rate

main = c_NSApplicationMain 0 nullPtr

This file is just the previously discussed main Haskell implementation plus the definition of the convert function that will be called by the C code: The convert function just takes two CDouble arguments, multiplies them, and returns the result again as a CDouble. This definition is made public for C via the foreign export line. The type CDouble is translated to a plain double value in C by the FFI (this type — and others — is provided by the Foreign.C.Types module, as you might have guessed).

After getting all the source files right we now have to build the application. For this we proceed in the following way:

  1. Do a plain vanilla build of the pure Xcode project for the *.m files. Using our haskell_dummy_interface.m file as a placeholder for the Haskell part. This is done via the normal Xcode build mechanism.

  2. Remove the haskell_dummy_interface.o object file from the build directory as it will be replaced by a Haskell version.

  3. Compile the Haskell file Converter.hs with GHC. We will pass the necessary frameworks to GHC via its -framework flag. Also, if we later want to call functions defined in the C part of the project we want to pass all necessary *.o object files to the GHC linker. It’s ok to just pass all *.o files (apart from the haskell_dummy_interface.o) to the GHC. The linker will take what it needs and just leave the rest.

  4. Finally copy the finished Haskell executable to the application’s MacOS folder and give it the right name.

The result will be a complete Mac OS X application.5

In order to automate this process I have written a short python script. 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 Project > New Build Phase > New Run Script Build Phase. 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 /usr/bin/python and the script will be called as the last step of the build process.

For your convenience I have provided this example Xcode project as a download.

A somewhat more complete converter

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.

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

  1. Create Converter “entities” (whatever this means).

  2. Use these entities to convert dollar origAmount into another currency using an exchange rate.

This Converter model can easily be defined in Haskell:

data Converter = Converter {
      origAmount :: Double,
      rate       :: Double
    }

convert :: Converter -> Double
convert (Converter amount xrate) = amount * xrate

But this is just the Haskell part. In order to use the Converter implementation from C we will need at least three functions: one function two create a Converter entity and to pass some kind of reference for this entity to C, another function to call convert on this entity using the reference, and a last function to dispose of the created entity again.

As the create and convert steps will be separated in the final C code we will have to tell the Haskell implementation to “store” the Converter entity after creation for a later use. This is exactly the use case for the Stable Pointer of the FFI: StablePtr.

To quote the documentation:

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.

Perfect!

With the provided functions of the Foreign.StablePtr module we can now implement the C interface:

foreign export ccall 
    c_newConverter :: CDouble -> CDouble -> IO (StablePtr Converter)
foreign export ccall 
    c_freeConverter :: StablePtr Converter -> IO ()
foreign export ccall 
    c_convert :: StablePtr Converter -> IO (CDouble)

c_newConverter :: CDouble -> CDouble -> IO (StablePtr Converter)
c_newConverter amount rate = newStablePtr $ 
                             Converter (realToFrac amount) (realToFrac rate)

c_freeConverter :: StablePtr Converter -> IO ()
c_freeConverter = freeStablePtr

c_convert :: StablePtr Converter -> IO (CDouble)
c_convert = (liftM (realToFrac . convert) ) . deRefStablePtr

The type StablePtr Converter is seen in C as an opaque pointer of type void*. It is best to define a new C type HsStablePtr like typedef void *HsStablePtr, that will represent this opaque Haskell reference in the C code. This can be done in a header file FFI.h as described here.

The resulting C types for the Converter API are then

extern HsStablePtr c_newConverter(double amount, double rate);
extern void c_freeConverter(HsStablePtr converter);
extern double c_convert(HsStablePtr converter);

This can now be called from the ConverterController.m directly like this:

- (IBAction)convert:(id)sender {
    double sourceAmount = [dollarField doubleValue];
    double rate = [rateField doubleValue];

    HsStablePtr converter = c_newConverter(sourceAmount, rate);
    double targetAmount = c_convert(converter);

    [amountField setDoubleValue:targetAmount]; 
    [rateField selectText:self]; 

    c_freeConverter(converter);
}

And we have some kind of Haskell implementation of the model.

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.

The final implementation

In order to hide the actual Haskell implementation from the controller, we will create a new wrapper model class, called HSConverter. It provides two methods for the controller:

-(id)initWithAmount:(double)amount rate:(double)rate;
-(double)convert;

and the Haskell HsStablePtr reference is stored as a data member of the object and will be freed by c_freeConverter() during the garbage collection in Objective-C. 6

With this we can now define the implementation of HSConverter.m:

extern HsStablePtr c_newConverter(double amount, double rate);
extern void c_freeConverter(HsStablePtr converter);
extern double c_convert(HsStablePtr converter);

@interface HSConverter : NSObject {
    HsStablePtr converter;
}

@implementation HSConverter
-(id)initWithAmount:(double)amount rate:(double)rate;
{
    [super init];
    converter = c_newConverter(amount, rate);
    return self;
}

- (void)finalize;       // this will be called by the GC
{
    c_freeConverter(converter);
    [super finalize];
}

-(id)init;
{
    // hardened, in case someone uses the standard init method
    return [self initWithAmount:1.0 rate:-1.0];
}

-(double)convert;
{
    return c_convert(converter);    
}
@end

And accordingly the final version of the ConverterController.m:

#import "ConverterController.h"
#import "HSConverter.h"

@implementation ConverterController
- (IBAction)convert:(id)sender {
    double sourceAmount = [dollarField doubleValue];
    double rate = [rateField doubleValue];

    HSConverter *converter = [[HSConverter alloc] 
                                initWithAmount:sourceAmount rate:rate];
    double targetAmount = [converter convert];

    [amountField setDoubleValue:targetAmount]; 
    [rateField selectText:self]; 
}
@end

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 HSConverter objects are plain Objective-C objects and can be passed to other methods or stored in NSArrays, for example.

And here is the final Xcode project as a download.

Conclusion

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.

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 approach 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.

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.




  1. You will need to patch the GHC Haskell compiler to build 32-bit code in Snow Leopard. Please see this posting by Alvaro Videla. A 64-bit GHC is as-of-today not yet ready for Snow Leopard.

  2. The MVC design pattern is explained in the linked Apple tutorial.

  3. There is some research going on to extend the existing foreign function interface for direct Haskell/Objective-C support.

  4. There seems to be some support for windows DLLs, though. Also, the next version of GHC is supposed to have support for dynamic libraries for the Mac.

  5. 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.

  6. Please remember to switch on garbage collection support in the Xcode project!

5 comments:

  1. This is a very interesting approach, thanks!

    I don't understand why __weak is necessary. I thought that only Objective-C object pointers were __strong by default.

    ReplyDelete
  2. Thanks for the comment!

    Yes, you are right: "__weak" is default for non Objective-C pointers.

    I will adjust the blog entry.

    ReplyDelete
  3. Excellent article, thanks for writing this up. Maybe the Objective-C interface can be generated from the datatype directly, using generic programming. I hope to do some more Cocoa/Haskell coding soon, I'll definitely look into this technique.

    ReplyDelete
  4. Thanks!

    Yes, some way to automate the interface files would be of great help. I have not done it yet, but using generic programming for that seems like a good idea.

    ReplyDelete
  5. Using generic programming you could write, for example, the init function and the accessor functions. Using Template Haskell (or some other preprocessor) you could embed the logic functions (those from Converter -> a and a -> Converter).

    ReplyDelete