Difference between revisions of "User:JRV"
Jump to navigation
Jump to search
(Got rid of draft tutorial) |
|||
(3 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
− | 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: |
||
− | -------------------------------------------------------------- |
||
− | == Why? == |
||
− | Why would you want to do this? Those with Xcode/Interface Builder/ |
||
− | Cocoa experience will not need an explanation. On the chance that someone |
||
− | else is browsing here, I'll list a few things. |
||
− | |||
− | * Cocoa is not a language, it is a vast library of Objective-C objects. |
||
− | |||
− | * Cocoa covers tasks such as: |
||
− | ** windowing, accessibility, |
||
− | ** pretty easy printing, with pdf automatically available, |
||
− | ** working with tables, trees, fonts, colors, images, and |
||
− | ** on and on... |
||
− | |||
− | * Objective-C is built on C, and is basically syntactic sugar for C. |
||
− | |||
− | * Interface Builder provides quick and easy construction of a complex user |
||
− | interface with |
||
− | |||
− | ** input fields, menus, toolbars, |
||
− | ** display of tables, outlines, trees, |
||
− | ** text fields (with built in editing), and |
||
− | ** more. |
||
− | |||
− | In Xcode together with Interface Builder, one can define most user |
||
− | interfaces with simple drag and drop from a library, then |
||
− | connect them with your code by dragging and drop in the IB screen. |
||
− | |||
− | You automatically get full integration with the Mac system. You get the |
||
− | Mac Aqua look. |
||
− | |||
− | 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 |
||
− | [[<Delhttp://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_before.png|thumb|350px|left]] |
||
− | Here are screen shots of the simple application we will develop. The one |
||
− | on the leftshows the application widow (together with the menu |
||
− | automatically generated by Xcode), ready to enter a number. |
||
− | The one on the right shows the same scene after entering a number in the |
||
− | text box, and pressing “return”. |
||
− | |||
− | The entire user interface was built in a short time using the graphical |
||
− | Interface Builder, by dragging text the entry box, and the labels from a |
||
− | library of “controls” onto the window. Notice that the labels are dynamic, |
||
− | in the line “Fibonacci Number for n = … is: … ”. |
||
− | |||
− | I won't discuss Interface Builder any further in this tutorial. |
||
− | |||
− | == The Haskell module == |
||
− | For this test I used the same code as used in |
||
− | [[Calling_Haskell_from_C|Calling Haskell from C], with some slight |
||
− | modifications. Here is the Haskell code, in a file called ''FibTest.hs''. |
||
− | |||
− | <haskell> |
||
− | |||
− | {-# 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 |
||
− | |||
− | </haskell> |
||
− | |||
− | 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: |
||
− | |||
− | <pre-c> |
||
− | #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 |
||
− | label. |
||
− | |||
− | And here is my implementation code: |
||
− | |||
− | <pre-c> |
||
− | |||
− | #import "CocoaFib.h" |
||
− | #include "HsFFI.h" |
||
− | #include "FibTest_stub.h" |
||
− | |||
− | @implementation CocoaFib |
||
− | |||
− | -(IBAction)generate:(id)sender{ |
||
− | int i = [intValue integerInput]; |
||
− | NSLog(@" in generate, integer input is %d\n",i ); |
||
− | unsigned 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''. |
||
− | |||
− | Note also the ''hs_exit'' call 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> |
||
− | #import <Cocoa/Cocoa.h> |
||
− | #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''. |
||
− | |||
− | == 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: |
||
− | |||
− | <pre> |
||
− | find . -name "lib*.a" | xargs nm > ~/develop/haskellLibInfo/libInfo |
||
− | </pre> |
||
− | 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: |
||
− | |||
− | <pre> |
||
− | ./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 |
||
− | … |
||
− | |||
− | </pre> |
||
− | |||
− | 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 output will say something like: |
||
− | |||
− | <pre> |
||
− | "_newCAF", referenced from: |
||
− | _FibTest_a3_info in FibTest.o |
||
− | "_base_GHCziBase_plusInt_closure", referenced from: |
||
− | _FibTest_a3_info in FibTest.o |
||
− | |||
− | … |
||
− | </pre> |
||
− | |||
− | Now go look in your libinfo folder and look 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. You will get a dialog asking if you want to add it to the project. |
||
− | Say yes, but don't copy the file into the project. Xcode will go and find |
||
− | it when it tries to link. |
||
− | |||
− | 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 looking for the library containing missing |
||
− | symbols you'll get a successful build. |
||
− | |||
− | Well, except I had one further problem. |
||
− | |||
− | === Symlink renamed libgmp.a === |
||
− | I have a bunch of libgmp.a, libgmp.so, and libgmp.dylib on my machine. |
||
− | Don't know where they all came from. Nevertheless, 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: |
||
− | |||
− | 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 |
||
− | 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 == |