3 ms·
One of my biggest complaints about Lisp and Scheme are the dynamic typing aspects; in the C code, one can see what type is at least supposed to be in "oldfont"
by mdaniel 5y ago
One of my biggest complaints about Lisp and Scheme are the dynamic typing aspects; in the C code, one can see what type is at least supposed to be in "oldfont" and "tm"; in the corresponding Lisp, only an IDE can tell the user what type should be in "dstat"
- kazinator 5y agoSince oldfont is a lexical variable being initialized from (SelectObject fe.hdc font.font), you probably get the most information about this by going off to MSDN to study what is SelectObject. If we select an object, like a font or a brush, into a device context, we get the previous object of that type: new font kicks out old font, new brush kicks out old brush, and so on. SelectObject's return value is a HGDIOBJ whichi s just a void * in disguise. Thus oldfont is just a FFI foreign pointer (cptr). You can do nothing with it other than compare it for equality with NULL and pass it to Win32 functions. I have simply mirrored the C more or less as-is. tm is a local variable; again, you just have to look where it is bound: (tm (new TEXTMETRIC)) so you know the type from the lexical scope. This corresponds to the line of C which goes TEXTMETRIC tm; It's exactly the same process. In the C code if I see tm.tmAscent, I look backwards for the declaration of tm, perhaps using the Vim # key. If I see tm.tmAscent in Lisp, I look backwards to see where tm is bound, perhaps using the Vim # key. (new TEXTMETRIC) is exactly as informative as TEXTMETRIC tm;. Any time you see a simple variable being accessed in Lisp code, it is either lexically scoped or global. If you don't see it in the lexical scope, it is the latter. So that brings us to dstat. Another lexical which is a copy of fe.drawstatus. We can see that fe is a parameter, but what is drawstatus? No fancy IDE is needed; just Vim. Put the cursor on drawstatus, and Ctrl-] chases the tag to the definition, a line in a big struct definition: (defstruct win-frontend win-drawing // ... drawstatus ;; @(or :drawing :printing nil) // ... ) fe is an important parameter; it is the self object of the method. I used the pattern matching notation in the above comment to remind myself the values this can hold. The front end objects in Simon Tatham's design have a mode variable which indicates whether printing or ordinary rendering is taking place. If you're printing, you have to do different things with colors and such (possibly using hatched pens on a black-and-white printer, plus scaling of coordinates and whatnot). I just used the three symbols :drawing, :printing and nil. Popular languages like Javascript and Python have a worse version of this "what is drawstatus" problem, yet are used by legions of programmers in all sorts of successful projects, even large ones. In these languages there is no clear place where object properties are defined, and objects can even be monkey patched with new properties, which I do not allow. Finding out what is returned by SelectObject is the same: put the cursor on SelectObject, Ctrl-], and it jumps to the FFI definition: (deffi SelectObject "SelectObject" HGDIOBJ (HDC HGDIOBJ)) In reading Lisp, there is no panic if we are calling some function, and capturing its return value without that being declared. Firstly, most of the time we shouldn't have to know anyway; if the program is well designed, we shouldn't care about the implementation details. Unless we are specifically looking for a bug in that aspect of the code, we can just trust that it's okay. The trend in modern languages, including statically typed ones, is to have fewer declarations. If the original code here were C++ you could probably do: auto oldfont = SelectObject(...);
- jstimpfle 5y ago> auto oldfont = SelectObject(...); I've never grown to like that very much. Being a little bit more verbose, in my mind, only pessimizes (keyboard) typing efficiency - when scripting at full speed, but not to the extent that it would harm when "just" being productive and in the zone. But the explicitness comes with advantages, one I assume is compilation speed (to an extent), but then also having to fix issues when type resolution is (or becomes, after a change) ambiguous, which requires small local and sometimes not-so-local changes. Another advantage is ease of caching in a piece of code when bumping around the codebase - which is important even if I was the original author. Also navigation to the definition of the type is faster/easier if the type is written explicitly, when using bare-bones editors but also in IDEs.