<div dir="ltr">Wow!  I should have asked that a long time ago.<div><br></div><div>Actually, I'm glad your response was as in-depth as it was.  It gave me time to really think about the things that have been stumbling blocks, whether externally (the size and scope of ObjC & Cocoa), or internally (adapting my brain to ObjC's syntax).</div>
<div><br></div><div>After working through the currency converter (first in ObjC, then CCL), I could see how the things were working, but it wasn't what I would call "independent knowledge" (i.e., I couldn't have taken what I knew and written something from scratch).  Realizing this, I cast about for a small project to tinker with (as you suggested), and remembered a book that I had bought years ago (<span class="Apple-style-span" style="font-family: Helvetica; font-size: 12px; "><a href="http://www.amazon.com/dp/0387202293">http://www.amazon.com/dp/0387202293</a>) that had drawing exercises at the end of each chapter written in Basic.  (Hmm.  Basic, ObjC, Lisp.  There's a joke in there somewhere, but, at the moment, it escapes me.)</span></div>
<div><span class="Apple-style-span" style="font-family: Helvetica; font-size: 12px;"><br></span></div><div><span class="Apple-style-span" style="font-family: Helvetica; font-size: 12px;">From what I've gathered, NSBezierPath will be my friend for accomplishing the drawing.</span></div>
<div><span class="Apple-style-span" style="font-family: Helvetica; font-size: 12px;"><br></span></div><div><span class="Apple-style-span" style="font-family: Helvetica; font-size: 12px;">Given Matthew's suggestion, it sounds like working through the Hillegass book first, and translating the exercises from the aforementioned book to ObjC might be the most optimal approach.</span></div>
<div><br></div><div>Thank you for the thoughtful response,</div><div>Chris.<br></div><div><br></div><div><div class="gmail_quote">On Thu, Aug 28, 2008 at 5:18 AM, Gary Byers <span dir="ltr"><<a href="mailto:gb@clozure.com">gb@clozure.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;"><div class="Ih2E3d"><br>
On Wed, 27 Aug 2008, Chris Van Dusen wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Agreed.  I saw AppKiDo mentioned in the online documentation, and have been<br>
using it pretty heavily since.<br>
One thing I've been curious about (and may produce on my own, if none<br>
exists) is a cheat sheet for the various ways of interacting with<br>
Objective-C.  Until I get used to it, when to use the appropriate reader<br>
macro is a source of frustration.<br>
All of that said, does anyone have suggestions/recommendations as to how<br>
best to learn to commingle CCL & Objective-C?  I'm fairly comfortable with<br>
Lisp in general.  Would it be worth my while to devote my time to learning<br>
Objective-C, Cocoa, etc. to the point that I'm conversant in those things,<br>
then come back at it from the CCL perspective?<br>
When learning CL, there were probably some fairly major concepts<br>
that needed to be absorbed, including:<br>
  - macros<br>
  - syntax (or the near total absence of it)<br>
  - the general notion that everything's a function that returns<br>
    some number of values, and that most primitive operations<br>
    (arithmetic, array/string access) are expressed as function calls<br>
  - closures and higher-order functions<br>
  - special variables and dynamic binding<br>
  - lambda-lists and variadic functions<br>
  - garbage collection (which might or might not be a new concept)<br>
  - CLOS<br>
  - the reader, packages ..<br>
  - the fact that there's a fairly rich library of standard functions<br>
    for dealing with common data structures<br>
  - ...<br>
At some point - when enough of these things make enough sense - one<br>
gets comfortable enough with enough of these concepts to be able to<br>
program productively in CL.  Learning to program in CL probably has<br>
more to do with becoming comfortable with a relatively small set of<br>
core concepts (like those above) than it does with memorizing<br>
reference material describing all of the library functions.  (If you<br>
quizzed me right now, I probably couldn't correctly enumerate all of<br>
the possible standard values that the :IF-EXISTS argument to OPEN can<br>
take; since we all know what keyword arguments are and how they<br>
can be used, we can look up those details in CLHS when we need<br>
to and expect that reference material to make sense.  Well, usually.)<br>
For ObjC and Cocoa, the number of core concepts that we need to<br>
learn in order to start to make sense of things is (mostly) pretty<br>
small; if we want to be able to read ObjC code/examples, there<br>
are some additional syntactic details.  For ObjC, these concepts<br>
include (some of this is a little circular):<br>
 - certain data structures are "classes"; they serve a role<br>
   very similar to that served by classes in CLOS and other<br>
   object systems.  It's possible to add new classes to a<br>
   running ObjC program (just as it is in CLOS), though ObjC<br>
   is less flexible than CLOS/MOP is about redefining/removing<br>
   classes procedurally.  ObjC classes are uniquely named (by<br>
   a string); many of the predefined classes in Cocoa (which<br>
   was derived from NeXT's NeXTStep framework) have names<br>
   that start with the prefix "NS".<br>
   ObjC classes are allocated in foreign memory (often as<br>
   part of a shared library's initialized data.)  A class<br>
   decribes the layout of its instances' instance variables<br>
   ("slots" in CLOS) and describes methods which are specialized<br>
   on its instances.<br>
   Redefining ObjC classes at runtime probably doesn't work<br>
   very well.  In CCL's ObjC bridge, accidentally mousing<br>
   on an unchanged class definition is probably harmless.<br>
   Adding/removing slots won't affect existing instances<br>
   in general, and I don't know/remember what Bad Things<br>
   might happen if the class hierarchy was changed.<br>
 - classes can be instantiated (just as in CLOS and most other<br>
   object systems); this involves allocating a (somewhat opaque)<br>
   block of foreign memory.  If one knows that a block of<br>
   foreign memory contains an ObjC instance, it is possible<br>
   (and cheap) to determine the class of that instance at<br>
 - unlike the case in CLOS - where classes are usually instances<br>
   of the metaclass STANDARD-CLASS - ObjC classes of unique<br>
   metaclasses.  (Among other things, this scheme allows methods<br>
   on classes to be specialized and customized.)  An ObjC class<br>
   is therefore an instance of its metaclass (which is itself<br>
   an instance of a special root metaclass), and it's possible<br>
   to consider any ObjC class, metaclass, or instance as being<br>
   an "ObjC object".  All three kinds of ObjC object are implemented<br>
   as "blocks of foreign memory"; the blocks occupied by classes<br>
   and metaclasses are "permanent" (for the lifetime of the<br>
   application), while those occupied by ObjC instances can be<br>
   freed (and possibly reallocated) when it appears that they<br>
   can no longer be referenced.  Apple is moving toward the<br>
   use of real garbage collection to determine when instances<br>
   can be reliably and automatically freed; traditionally, a<br>
   reference-counting scheme has been used.<br>
   A foreign type corresponding to "pointer to ObjC object"<br>
   is predefined in ObjC; this type is named "id" (or :id from<br>
 - some strings (usually in mixed case and often containing<br>
   embedded and/or trailing colons) name ObjC "messages".  An<br>
   ObjC message isn't quite a first-class object, but it can be<br>
   thought of as being similar to a kind of generic function<br>
   which is specialized on the class of its first argument<br>
   (which must be an ObjC object) and which may contain<br>
   additional arguments.  Those additional arguments may<br>
   be ObjC objects or low-level C pointers or numbers.<br>
   A string which names a message is sometimes called a<br>
   "selector" (in some other ObjC implementations, a "selector"<br>
   is distinct from the string which identifies it, but that's<br>
   a separate issue.)<br>
   The number of colons in a message name typically indicates<br>
   the number of arguments (other than the first, special<br>
   argument) that methods defined on the message should accept.<br>
 - It's possible (and useful) to define methods on any message<br>
   and any ObjC class or metaclass.  A large number of methods<br>
   (on a large number of classes ...) are predefined in Cocoa<br>
   and other ObjC libraries; methdods can also be added at<br>
   runtime.  In order for ObjC code to be able to pass arguments<br>
   (other than the first specialized argument of type "id")<br>
   to and return results from an ObjC method, the (foreign)<br>
   types of those arguments and return value must be specified.<br>
   If a method shadows another method defined in a superclass<br>
   (or is shadowed by a method defined in a subclass), the<br>
   types of those arguments and return value must agree.<br>
   (It's legal for methods defined on disjoint classes to<br>
   have different type signatures; this happens occasionally<br>
   in practice.)<br>
   All methods defined on an ObjC message must take the same<br>
   number of arguments, but (as noted above) the types of<br>
   their non-specialized arguments may differe in some cases.<br>
   It is possible to redefine an ObjC method at runtime; it<br>
   generally works reliably to change the implementation (body)<br>
   of the method, but may not work as well (or at all) to<br>
 - From within the body of an ObjC method M, it's possible to<br>
   invoke whatever method would have been applicable had M<br>
   not been defined.  (Conceptually, this is similar to<br>
 - In ObjC (as in SmallTalk), invoking a generic function on<br>
   a specialized argument ("the receiver") and some other<br>
   arguments is called "sending a message", and it's written<br>
   in a sort of infix syntax:<br>
[reciever messageName]   if the message accepts no other arguments<br>
[receiver messageNameFirstArg: first-arg-value nextArg: next-arg-value]<br>
   In the first case, the actual message name is "messageName"; in the<br>
   second  case, it's "messageNameFirstArg:nextArg:"<br>
 - Methods can be defined on classes (and therefore applicable to<br>
   their instances) or on metaclasses (and therefore applicable to<br>
   class methods).  When a class method is defined or described, it's<br>
   usually prefixed with a "+" character; an instance method is<br>
   usually prefixed with a "-".<br>
That's a fair amount of stuff that really should be written down<br>
someplace and in greater detail.  A short version is that ObjC offers<br>
an object system with dynamically typed objects and at least some<br>
amount of/some kinds of runtime extensibility, and a sort of<br>
restricted form of (something like) CLOS method dispatch that's based<br>
on the (runtime) class of the first/only specialized argument, and<br>
that ObjC methods typically deal with some combination of run-time<br>
typed Objc objects and statically-typed primitive C things (numbers,<br>
pointers, structures.)<br>
I suspect (I don't know for sure) that the thing that'd help most<br>
people the most in getting up to speed with using Cocoa in CCL is<br>
comfort and familiarity with CCL's FFI.  If that's true (and to the<br>
extent that it is), it's somewhat unfortunate; when he first started<br>
working on it, Randall Beer said that ideally he'd like the bridge to<br>
expose as much of Cocoa as possible while hiding as much of ObjC as<br>
possible.  It's gotten closer to that goal over the years, but I<br>
suspect that a lot of things that people have trouble with could<br>
be blamed on the fact that ObjC (and some of the arcana of dealing<br>
with it) still isn't hidden well enough.  (If this is true, it's<br>
probably the "C" part that's problematic; the "Obj" part seems<br>
to fit a lot better into CL/CCL.)  Looking back at it, I think<br>
that the bridge actually -does- do a good job of hiding a lot<br>
of the problematic C stuff (though admittedly not all of it),<br>
but a lot of it is ultimately nothing more than a few layers<br>
of syntactic sugar around the FFI, and I imagine that it's<br>
still hard to use unless one has some sense of what's going<br>
on underneath.<br>
For learning Cocoa itself ... well, it's a large class library (much<br>
larger than the standard CL library) that provides default behavior<br>
for a lot of things that an application needs to do (implemented as<br>
instances of predefined classes and predefined methods on those<br>
classes), and the general idea is that by subclassing some of those<br>
classes and overriding some of those methods you turn this generic<br>
application into something that offers custom behavior.  It's<br>
generally hard to absorb all of the functionality that's available<br>
in a library by reading the reference manual from cover to cover<br>
(it might help to skim reference material to get a sense of<br>
what's available and to read a few passages that seem interesting.)<br>
It's often helpful - especially when starting out - to have a<br>
specific goal in mind, and then try to learn what you need to<br>
learn in order to achieve that.  After you repeat that process<br>
a few times, it likely starts to get easier, and (as is often<br>
the case) the first foray into something new is the hardest.<br>
Suppose that we had a specific goal of writing an image-viewing<br>
program, like a watered-down version of Apple's Preview.app)<br>
(We could make the example more interesting, but let's not ...)<br>
If we've never done this before in Cocoa, there are clearly<br>
a bunch of specific questions we'd need answered, including:<br>
 - what predefined support is there for loading files containing<br>
   well-known image types (jpg, tiff, gif, png, ...) and displaying<br>
   them ?  If we wanted to construct an image from an unsupported<br>
   file type or procedurally, what's involved in doing that ?<br>
 - how exactly do you display a window and get an image to appear<br>
   in it ?  How do you control what happens when the window is<br>
   resized (e.g., does the image scale ?  do you want it to ?)<br>
 - how do you provide a user interface that allows images to<br>
   be loaded (and other behavior that a more interesting<br>
   application would want) ?<br>
Apple does provide very good overview documentation (in the<br>
form of "guides".)  At the moment, the Cocoa guides are<br>
available at:<br>
<<a href="http://developer.apple.com/documentation/Cocoa/index.html" target="_blank">http://developer.apple.com/documentation/Cocoa/index.html</a>><br>
and looking at the "Graphics and Imaging" section there we find links<br>
to guides describing "Image Views" (sounds promising) and a "View<br>
Programming Guide".  The image view guide is a little terse (it<br>
basically just says that NSImageViews are NSViews that display<br>
NSImages, whatever they are.  The "View Programming Guide" is a lot<br>
more to digest, and if we follow a link to NSImage we'd have even more<br>
to digest.  If we follow the link from the Image View guide to the<br>
NSImageView reference, we see that NSImageViews can be set to allow<br>
cut copy and paste of images as well as drag-and-drop.  That's maybe<br>
not ultimately what we want (if we want to load images from files),<br>
but it may be an easier way to get started.<br>
Let's make a window, put an NSImageView in it, set some options<br>
that enable drag-and-drop and cut-and-paste to work with the<br>
image view, center and display the window, and return the NSImageView<br>
(defun make-image-view-window ()<br>
  "Make a window whose content view is an empty NSImageView.  Allow<br>
the user to drag images to that view, and support transfer of images<br>
to/from the view via the clipboard.  Return the NSImageView."<br>
   (let* ((rect (ns:make-ns-rect 0 0 300 300))<br>
          (w (make-instance 'ns:ns-window<br>
                            :with-content-rect rect<br>
                            :style-mask (logior #$NSTitledWindowMask<br>
                            :backing #$NSBackingStoreBuffered<br>
                            :defer t))<br>
          (image-view (make-instance 'ns:ns-image-view)))<br>
     ;; Set the window's title to a constant NSString (#@"...").<br>
     (#/setTitle: w #@"Drag or paste an image here!")<br>
     ;; Make the image view respond to drag-and-drop<br>
     (#/setEditable: image-view t)<br>
     ;; and make it support the clipboard<br>
     (#/setAllowsCutCopyPaste: image-view t)<br>
     ;; Make the image view be the window's "content view".<br>
     (#/setContentView: w (#/autorelease image-view))<br>
     ;; Center and display the window.<br>
     (#/center w)<br>
     (#/orderFront: w nil)<br>
If we call this function, an empty 300x300 window should appear on<br>
the screen and an NSImageView object should be returned.<br>
? (make-image-view-window)<br>
#<NS-IMAGE-VIEW <NSImageView: 0x1279bf50> (#x1279BF50)><br>
? (defvar *v* *) ; so we can refer to the NS-IMAGE-VIEW later.<br>
If we then switch to a web browser or other image-viewing application<br>
and copy an image to the clipboard, we can switch back to the IDE and<br>
paste that image into the image view.<br>
I happened to copy a fairly small image:<br>
? (#/image *v*)<br>
#<NS-IMAGE NSImage 0x12795df0 Size={120, 80} Reps=(<br>
    NSBitmapImageRep 0x1279cf30 Size={120, 80} ColorSpace=NSCalibratedRGBColorSpace BPS=8 BPP=32 Pixels=120x80 Alpha=YES Planar=NO Format=2 CGImage=0x1279e340<br>
) (#x12795DF0)><br>
When I resized the window, the image stayed at 120x80 and happened to<br>
stay centered in the window.  It may be more impressive to let the image<br>
scale itself to fit the view bounds:<br>
? (#/setImageScaling: *v* #$NSScaleToFit)<br>
Getting images from the clipboard is all well and good, but the original<br>
goal was to display images that were loaded from files.   If only<br>
NSImage had an "initWithContentsOfFile:" method, this would be trivially<br>
easy in most cases.<br>
Oh, wait: it does (as revealed by a little bit of poking around in the<br>
NSImage reference doc.)  ObjC messages whose names start with<br>
"init" are treated specially by the bridge: MAKE-INSTANCE with keyword<br>
args derived from the rest of the message name will (among other<br>
things) send that initialization message to a newly-allocated instance<br>
of the class. So:<br>
(let* ((image (make-instance 'ns:ns-image :with-contents-of-file #@"/Library/Desktop Pictures/Nature/Aurora.jpg")))<br>
  (unless (%null-ptr-p image) ; unless the image couldn't be initialized<br>
    (#/setImage: *v* (#/autorelease image))))<br>
We know from an earlier message in this thread (if not otherwise)<br>
how to use NSOpenPanel to select a file, so we're maybe around 75%<br>
of the way towards the goal of developing a simple image-viewing<br>
program.  That's probably enough for an off-topic reply, but it's<br>
worth noting that so far it's been about 20 lines of code and that<br>
NSImage and NSImageView and the rest of Cocoa are doing all of the<br>
heavy lifting.<br>
I'm sorry that this reply is so long and not entirely responsive. To<br>
try to summarize:<br>
 - I think that the best way to learn Cocoa (or anything similar)<br>
   is probably from the bottom up and from the inside out, and that<br>
   it helps to have a concrete, narrowly focused goal and to try<br>
   to learn just what you need to learn in order to achieve that.<br>
   After repeating that process a few times, more stuff tends to<br>
   resonate and make sense.<br>
   It's certainly not practical to go in the other directions,<br>
   as in "here are several hundred classes and a few thousand<br>
   methods, go learn them."<br>
 - From your comments and others, I think that it's fair to say<br>
   that the goal of hiding ObjC (the C part of it, especially)<br>
   hasn't been fully reached.   It's still sometimes necessary<br>
   to allocate foreign objects and pass pointers to them by<br>
   reference, and that sort of thing is disconsonant with what's<br>
   otherwise a very high-level and powerful framework (Cocoa.)<br>
   That stuff is also inherently hard and un-lispy.<br>
One of the problems that I personally have here is that while<br>
I can believe that there are aspects of CCL's FFI that are hard<br>
to use and/or understand, it's hard for me to guess exactly<br>
what those things are (since it all makes perfect sense to me<br>
for some reason.)<br>