Difference between revisions of "User:JRV"

From HaskellWiki
Jump to navigation Jump to search
Line 4: Line 4:
 
Very rough draft:
 
Very rough draft:
 
--------------------------------------------------------------
 
--------------------------------------------------------------
  +
In the interest of full disclosure—I'm not an old time Mac person. I've
  +
had my Mac for two years. I only decided to learn Objective-C and Cocoa
  +
after I explored doing an application using Python and Qt, and someone in
  +
the Python community said “Objective-C is easy. Why don't you write a real
  +
Mac application”. Neither am I a Haskell expert.
  +
 
== Why? ==
 
== Why? ==
Why would you want to do this? Those with Xcode/Interface Builder/
+
Why would you want to do use a combination of Objective-C and Haskell in an
  +
Xcode/Interface builder project? Those with Xcode/Interface Builder/
Cocoa experience will not need an explanation. On the chance that someone
 
  +
Cocoa experience will not need an explanation.
else is browsing here, I'll list a few things.
 
   
  +
For others that may browse here, here is a short explanation.First,
* Cocoa is not a language, it is a vast library of Objective-C objects.
 
  +
Xcode/Interface builder is a great (my opinion, of course) IDE for
  +
developing Mac applications. Xcode keeps track of your project, provides
  +
browsing of your code, building and testing, and more. It is tightly
  +
integrated with Interface Builder. Interface Builder provides an extensive
  +
(and extendible) library of interface elements (windows, tabbed windows,
  +
windows with drawers, text boxes with built in editing capability, buttons,
  +
and more), all based on Cocoa classes, ready to drag and drop to create
  +
your user interface.
   
  +
Cocoa is an extensive library of Objective-C classes. Objective-C is
* Cocoa covers tasks such as:
 
  +
basically syntactic sugar for plain old C, and C functions can be used
** windowing, accessibility,
 
  +
directly with no modification.
** pretty easy printing, with pdf automatically available,
 
** working with tables, trees, fonts, colors, images, and
 
** on and on...
 
   
  +
It's easy to get carried away here.
* Objective-C is built on C, and is basically syntactic sugar for C.
 
   
  +
One final shot, though. The Xcode and the Cocoa library are built to
* Easy visual interface with Interface Builder
 
  +
encourage the Apple Model–View–Controller design pattern. Haskell fits
  +
nicely into this as a vehicle for a model that has interesting behaviour.
  +
Parsing comes immediately to mind, as does information visualization (see
  +
[http://themonadreader.wordpress.com The Monad Reader, 14]).
   
  +
== Overview of the process ==
** input fields, menus, toolbars,
 
  +
The “how to” consists of the following steps.
** display of tables, outlines, trees,
 
** text fields (with built in editing), and
 
** more.
 
   
  +
# Develop and test Haskell module, callable from C.
In Xcode together with Interface Builder, one can define most user
 
  +
# Put together and test Xcode/Cocoa project with dummy Haskell file.
interfaces with simple drag and drop from a library, then
 
  +
# Add Haskell .h and .o files to your Xcode project.
connect them with your code by dragging and drop in the IB screen.
 
  +
# Add required Haskell library dependencies to your Xcode project.
  +
# Resolve naming conflicts between Haskell libraries and other libraries.
   
  +
Steps 1 and 2 can be done in either order. Step 4 should be easy, using
You automatically get full integration with the Mac system. You get the
 
  +
facilities of GHC and/or Cabal, but I haven't been able to make these work.
Mac Aqua look.
 
  +
I show how to do this iteratively. This is a bit tedious, but not bad if
  +
you don't plan on changing your Haskell code frequently.
   
Enough said. What Cocoa doesn't provide are the tools for mathematical
 
modeling of an application domain that Haskell provides. Parsing comes
 
immediately to mind, as does information visualization (see
 
[http://themonadreader.wordpress.com The Monad Reader, 14]).
 
 
Use of a Haskell model fits nicely with the Apple Model–View–Controller
 
design pattern.
 
 
In the interest of full disclosure—I'm not an old time Mac person. I've
 
had my Mac for two years. I only decided to learn Objective-C and Cocoa
 
after I explored doing an application using Python and Qt, and someone in
 
the Python community said “Objective-C is easy. Why don't you write a real
 
Mac application”. Neither am I a Haskell expert.
 
 
== Overview of the app ==
 
 
[[image:JRV_CocoaHaFib_after.png|thumb|350px|right]]
 
[[image:JRV_CocoaHaFib_after.png|thumb|350px|right]]
Here is a screen shot of the simple application we will develop.
+
Here is a screen shot of the upper left corner of the Mac screen, including
  +
the app menu (generated by Interface builder) and the application window
When the user types a number in the entry box, the line below changes to
 
  +
itself. When the user types a number in the entry box, the line below changes to
 
give the corresponding Fibonacci Number.
 
give the corresponding Fibonacci Number.
 
The entire user interface was built in a short time using the graphical
 
Interface Builder, by dragging the entry box, and the labels from a
 
library of “controls” onto the window.
 
 
I won't discuss Interface Builder any further in this tutorial.
 
   
 
== The Haskell module ==
 
== The Haskell module ==
Line 115: Line 113:
 
implementation file (.m).
 
implementation file (.m).
   
Here is my interface file:
+
Here is my interface file (.h file):
   
 
<pre-c>
 
<pre-c>
Line 133: Line 131:
   
 
The three outlets are for the user input, fibonacci number output, and the
 
The three outlets are for the user input, fibonacci number output, and the
  +
number following “n =” in the label.
label.
 
   
  +
It's worth noting that the three outlets are connected to their respective
And here is my implementation code:
 
  +
parts of the user interface by dragging in Interface Builder.
  +
  +
Here is my implementation code (.m file):
   
 
<pre-c>
 
<pre-c>
Line 147: Line 148:
 
-(IBAction)generate:(id)sender{
 
-(IBAction)generate:(id)sender{
 
int i = [intValue integerInput];
 
int i = [intValue integerInput];
  +
int j = fibonacci_hs(i);
NSLog(@" in generate, integer input is %d\n",i );
 
unsigned int j = fibonacci_hs(i);
 
 
[setIntValue:i forNis];
 
[setIntValue:i forNis];
 
[setIntValue:j fibOutput];
 
[setIntValue:j fibOutput];
 
 
}
 
}
   
Line 164: Line 163:
 
Note the include lines for 1) ''hsFFI.h'', and ''FibTest_stub.h''.
 
Note the include lines for 1) ''hsFFI.h'', and ''FibTest_stub.h''.
   
Note also the ''hs_exit'' call in ''dealloc''.
+
Also note the call to ''hs_exit'' in ''dealloc''.
   
 
=== Modify main.m ===
 
=== Modify main.m ===
Line 192: Line 191:
 
''include "FibTest_stub.h"'' and the call to ''hs_init''.
 
''include "FibTest_stub.h"'' and the call to ''hs_init''.
   
== Compile and run ==
+
== Add Haskell libraries, compile and run ==
 
This is the easy part. Just kidding. It should be easy, if it were easy
 
This is the easy part. Just kidding. It should be easy, if it were easy
 
to make a library or executable containing ''FibTest.o'',
 
to make a library or executable containing ''FibTest.o'',
Line 205: Line 204:
 
First, I added ''libffi.a'' to my project for good measure.
 
First, I added ''libffi.a'' to my project for good measure.
   
Then select build and go from the menu or the toolbar. This will result
+
Then select ''Build and go'' from the menu or the toolbar. This will result
 
something like 26 failures, all due to undefined symbols.
 
something like 26 failures, all due to undefined symbols.
   
Line 250: Line 249:
 
</pre>
 
</pre>
   
Now go look in your ''libinfo'' folder and search for ''T _newCAF''. It is in
+
Now go look in your ''libInfo'' folder (or whatever you called it)
  +
and search for ''T _newCAF''. It is in
 
''libHSrts.a''.
 
''libHSrts.a''.
   
Line 256: Line 256:
 
''libHSrts.a''
 
''libHSrts.a''
 
in it. Drag ''libHSrts.a'' into the ''Groups and Files'' pane of the Xcode
 
in it. Drag ''libHSrts.a'' into the ''Groups and Files'' pane of the Xcode
window. You will get a dialog asking if you want to add it to the project.
+
window. In Xcode, this doesn't move the file it provides a reference to it
  +
for the linker.
Say yes, but don't copy the file into the project. Xcode will go and find
 
  +
You will get a dialog asking if you want to add the file to the project,
it when it tries to link.
 
  +
and you'll be given an opportunity to copy the file into the project. Add
  +
the file to the project, but it is unnecessary to copy it.
   
 
Now when you redo ''Build and Go'', you will no doubt get even more
 
Now when you redo ''Build and Go'', you will no doubt get even more
Line 271: Line 273:
 
Well, except I had one further problem.
 
Well, except I had one further problem.
   
  +
=== Resolve name conflicts of Haskell libraries ===
=== Symlink renamed libgmp.a ===
 
I have a bunch of libgmp.a, libgmp.so, and libgmp.dylib on my machine.
+
I have a bunch of libgmp.a, libgmp.so, and libgmp.dylib on my machine, in
  +
addition to the one in ''…/usr/lib/ghc-6.10.4''.
I don't know where they all came from. Nevertheless, Xcode linking tries to
 
  +
I don't know where they all came from. Xcode linking tries to
 
use the ones early in its search path, and they don't work with Haskell.
 
use the ones early in its search path, and they don't work with Haskell.
   
To get around this, I made a symbolic link in the usr/lib/ghc-6.10.4
+
To get around this, I made a symbolic link in the ''usr/lib/ghc-6.10.4''
 
directory as follows:
 
directory as follows:
   
Line 286: Line 289:
 
== That's all there is to it ==
 
== That's all there is to it ==
 
Well, its a bit tedious, but consider the following:
 
Well, its a bit tedious, but consider the following:
 
You can (and probably should) test your Haskell model without any GUI, when
 
it's working as desired, then make a version with a C interface.
 
   
 
Once you import this into your Xcode project, with all the attendant adding
 
Once you import this into your Xcode project, with all the attendant adding
Line 296: Line 296:
   
 
== To do ==
 
== To do ==
  +
The biggest ''to do'' is to generate a single library or .o file that
  +
includes all the dependencies. There should be a way to do this, but I
  +
haven't been able to get it to work. I'll continue to try. Any help will
  +
be appreciated.
  +
  +
One might want to write an Objective-C wrapper for your haskell module.
  +
This would encapsulate the module as an Objective-C class, and each
  +
function call would be a method. I'm not sure why you would want to do
  +
this generally. If your Haskell module was managing some persistent data
  +
store it might make sense.

Revision as of 20:00, 29 October 2009

I'm developing a tutorial on this page (slowly). I'm keeping it here so I can check out format, figures, etc. before putting the whole think in the Tutorial area.

Very rough draft:


In the interest of full disclosure—I'm not an old time Mac person. I've had my Mac for two years. I only decided to learn Objective-C and Cocoa after I explored doing an application using Python and Qt, and someone in the Python community said “Objective-C is easy. Why don't you write a real Mac application”. Neither am I a Haskell expert.

Why?

Why would you want to do use a combination of Objective-C and Haskell in an Xcode/Interface builder project? Those with Xcode/Interface Builder/ Cocoa experience will not need an explanation.

For others that may browse here, here is a short explanation.First, Xcode/Interface builder is a great (my opinion, of course) IDE for developing Mac applications. Xcode keeps track of your project, provides browsing of your code, building and testing, and more. It is tightly integrated with Interface Builder. Interface Builder provides an extensive (and extendible) library of interface elements (windows, tabbed windows, windows with drawers, text boxes with built in editing capability, buttons, and more), all based on Cocoa classes, ready to drag and drop to create your user interface.

Cocoa is an extensive library of Objective-C classes. Objective-C is basically syntactic sugar for plain old C, and C functions can be used directly with no modification.

It's easy to get carried away here.

One final shot, though. The Xcode and the Cocoa library are built to encourage the Apple Model–View–Controller design pattern. Haskell fits nicely into this as a vehicle for a model that has interesting behaviour. Parsing comes immediately to mind, as does information visualization (see The Monad Reader, 14).

Overview of the process

The “how to” consists of the following steps.

  1. Develop and test Haskell module, callable from C.
  2. Put together and test Xcode/Cocoa project with dummy Haskell file.
  3. Add Haskell .h and .o files to your Xcode project.
  4. Add required Haskell library dependencies to your Xcode project.
  5. Resolve naming conflicts between Haskell libraries and other libraries.

Steps 1 and 2 can be done in either order. Step 4 should be easy, using facilities of GHC and/or Cabal, but I haven't been able to make these work. I show how to do this iteratively. This is a bit tedious, but not bad if you don't plan on changing your Haskell code frequently.

JRV CocoaHaFib after.png

Here is a screen shot of the upper left corner of the Mac screen, including the app menu (generated by Interface builder) and the application window itself. When the user types a number in the entry box, the line below changes to give the corresponding Fibonacci Number.

The Haskell module

For this test I used the same code as used in Calling Haskell from C, with some slight modifications. Here is the Haskell code, in a file called FibTest.hs.

{-# LANGUAGE ForeignFunctionInterface #-}

module FibTest where

import Foreign.C.Types

fibonacci :: Int -> Int
fibonacci n = fibs !! n
    where fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

fibonacci_hs :: CInt -> CInt
fibonacci_hs = fromIntegral . fibonacci . fromIntegral

foreign export ccall fibonacci_hs :: CInt -> CInt

We compile this with ghc, viz:

   $ ghc -c -O FibTest.hs

This produces the following files that we will import into Xcode:

  • FibTest.o
  • FibTest_stub.h
  • FibTest_stub.o

and the following files that we won't need:

  • FibTest_stub.c
  • FibTest.hi

Import into Xcode project

First we start an Xcode project in the usual way. I started this as a plain Cocoa application. I called my application CocoaHaskellFib.

Several steps can be done in any order:

Copy Files

FibTest.o, FibTest_stub.h, and FibTest_stub.o into the folder where you have saved your CocoaHaskellFib project.

Then add them to the project by Project ➝ Add To Project .

Create application class

Next, create a Cocoa Objective-C class using the Xcode menu, File→New. I named mine CocoaFib. Xcode will create both an interface file (.h) and an implementation file (.m).

Here is my interface file (.h file):

<pre-c>

  1. import <Cocoa/Cocoa.h>

@interface CocoaFib : NSObject {

   IBOutlet NSTextField *integerInput;
   IBOutlet NSTextField *fibOutput;
   IBOutlet NSTextField *forNis;

}

-(IBAction)generate:(id)sender;

@end </pre-c>

The three outlets are for the user input, fibonacci number output, and the number following “n =” in the label.

It's worth noting that the three outlets are connected to their respective parts of the user interface by dragging in Interface Builder.

Here is my implementation code (.m file):

<pre-c>

  1. import "CocoaFib.h"
  2. include "HsFFI.h"
  3. include "FibTest_stub.h"

@implementation CocoaFib

-(IBAction)generate:(id)sender{

   int i = [intValue integerInput];
   int j = fibonacci_hs(i);
   [setIntValue:i forNis];
   [setIntValue:j fibOutput];

}

-(void)dealloc{

   hs_exit();
   [dealloc super];

}

@end </pre-c>

Note the include lines for 1) hsFFI.h, and FibTest_stub.h.

Also note the call to hs_exit in dealloc.

Modify main.m

When creating a Cocoa project, Xcode automatically generates a file main.m. Normally, one never touches this file. However, when using a Haskell module, one needs to initialize the Haskell run-time by calling hs_init. Normally, I would do this in an init call of CocoaFib.m, rather than in main.m, but since hs_init needs an argc and argv, and since main.m already has them hanging around, I took the easy way out.

Here is main.m

<pre-c>

  1. import <Cocoa/Cocoa.h>
  2. include "FibTest_stub.h"

int main(int argc, char *argv[]) {

   hs_init(&argc, &argv);
   return NSApplicationMain(argc,  (const char **) argv);

}

</pre-c>

The only changes to the main.m provided by Xcode are the addition of include "FibTest_stub.h" and the call to hs_init.

Add Haskell libraries, compile and run

This is the easy part. Just kidding. It should be easy, if it were easy to make a library or executable containing FibTest.o, FibTest_stub.o and all their dependencies. Unfortunately I've been unable to do that, in spite of spending most of my spare time for a week trying, and posting questions on Haskell-cafe.

Anyway, here is how I did it. If you have a better way, here is a good place to edit this tutorial!

Build and go, the first time

First, I added libffi.a to my project for good measure.

Then select Build and go from the menu or the toolbar. This will result something like 26 failures, all due to undefined symbols.

What to do?

What I did was go to the …/usr/lib/ghc-6.10.4 directory of my installation, and run:

find . -name "lib*.a" | xargs nm > ~/develop/haskellLibInfo/libInfo

This results in a file with a list of all the available symbols from all the libraries in the Haskell installation.

Entries look something like this:

./array-0.2.0.0/libHSarray-0.2.0.0.a(Base__1.o):
         U _arrayzm0zi2zi0zi0_DataziArrayziBase_STUArray_con_info
000000b0 D _arrayzm0zi2zi0zi0_DataziArrayziBase_zdWSTUArray_closure
0000009c T _arrayzm0zi2zi0zi0_DataziArrayziBase_zdWSTUArray_info
00000090 t _arrayzm0zi2zi0zi0_DataziArrayziBase_zdWSTUArray_info_dsp
00000078 t _s7RK_info
00000070 t _s7RK_info_dsp
…

Entries with a flag T (for text) are code. U of course indicates undefined, and is no help to us. D indicates data, and is used (if I understood correctly) for defined global constants.

OK. So what do you do with this?

Your Xcode faiure output will say something like:

  "_newCAF", referenced from:
      _FibTest_a3_info in FibTest.o
  "_base_GHCziBase_plusInt_closure", referenced from:
      _FibTest_a3_info in FibTest.o

  …

Now go look in your libInfo folder (or whatever you called it) and search for T _newCAF. It is in libHSrts.a.

Now go back to Xcode. In a Finder window, locate the file with libHSrts.a in it. Drag libHSrts.a into the Groups and Files pane of the Xcode window. In Xcode, this doesn't move the file it provides a reference to it for the linker. You will get a dialog asking if you want to add the file to the project, and you'll be given an opportunity to copy the file into the project. Add the file to the project, but it is unnecessary to copy it.

Now when you redo Build and Go, you will no doubt get even more failures due to undefined symbols.

But don't dispair …

Iterate build and go

After a few interations of adding a library containing missing symbols you'll get a successful build.

Well, except I had one further problem.

Resolve name conflicts of Haskell libraries

I have a bunch of libgmp.a, libgmp.so, and libgmp.dylib on my machine, in addition to the one in …/usr/lib/ghc-6.10.4. I don't know where they all came from. Xcode linking tries to use the ones early in its search path, and they don't work with Haskell.

To get around this, I made a symbolic link in the usr/lib/ghc-6.10.4 directory as follows:

    ln -s libgmp.a lib-h-gmp.a

and added lib-h-gmp.a to my project.

That's all there is to it

Well, its a bit tedious, but consider the following:

Once you import this into your Xcode project, with all the attendant adding of .a files, you shouldn't have to change any of the .a file additions again. Now you can concentrate on the View and Control part of your project.

To do

The biggest to do is to generate a single library or .o file that includes all the dependencies. There should be a way to do this, but I haven't been able to get it to work. I'll continue to try. Any help will be appreciated.

One might want to write an Objective-C wrapper for your haskell module. This would encapsulate the module as an Objective-C class, and each function call would be a method. I'm not sure why you would want to do this generally. If your Haskell module was managing some persistent data store it might make sense.