5 ms·
> why do people keep inventing languages which are not lisps Apparently people like to make syntax optimized for what they do most frequently. I like C program
by jstimpfle 5y ago
> why do people keep inventing languages which are not lisps
Apparently people like to make syntax optimized for what they do most frequently.
I like C programming, and not having a concise syntax to dereference, index, and access struct members would drive me nuts. Something like foo->bar.baz += quux(42); is not too rare to have to write, but feels very natural to write this way. In Lisp, that would be terrible in comparison.
Sometimes, just sometimes, I try to add some metaprogramming and imagine how nice it would be to code in Lisp, only to realize soon after that I was overthinking it. It's a rare situation that the "special" syntax that is in my preferred language causes friction for what I do. 99% of the time, it is there to support me, to help me read and write code faster.
- kazinator 5y agoI am very attuned to this specific problem. Whereas the lack of infix for numeric calculation is hardly an impediment, good ways of dealing with objects and structures strike at the heart of program organization. Numeric calculations are confined to small isolated pieces of code; they do not involve reference and collaboration. In some ways, parenthesized prefix improves the readability of nested expressions by eliminating ambiguity, and n-ary operators like * and + taking multiple arguments can even reduce clutter. But struct access; it helps that have that convenient. This is why in TXR Lisp there is a comprehensive referencing dot notation. What's more, there is also a blueprint for how to integrate that into any Lisp. This is the thing about Lisp; the research is not over. Here is some example code. (defmeth win-drawing draw-text (fe x y fonttype fontsize align colour text) (whenlet ((dstat fe.drawstatus)) (if (eq dstat :printing) (upd fontsize (* fe.printpixelscale) toint)) (let ((xy (win-transform-point fe x y)) (key (cons fonttype fontsize))) ;; find or create the font. ;; ;; NB: We don't implement the C versions "font stack marker" for ;; allocating printing fonts within the array and then discarding them. ;; In Lisp, it's trivial to save and clear the entire font list before ;; printing, and restore it after. (let* ((font (or (find key fe.fonts) (win-new-font fe fonttype fontsize))) (oldfnt (SelectObject fe.hdc font.font)) (tm (new TEXTMETRIC)) (size (new SIZE))) (if (GetTextMetrics fe.hdc tm) (if (logtest align align-vcentre) (dec xy.y (trunc (+ tm.tmAscent tm.tmDescent) 2)) (dec xy.y tm.tmAscent))) (if (GetTextExtentPoint32 fe.hdc text (len text) size) (if (logtest align align-hcentre) (dec xy.x (trunc size.cx 2)) (dec xy.x size.cx))) (SetBkMode fe.hdc TRANSPARENT) (win-text-colour fe colour) (ExtTextOut fe.hdc xy.x xy.y 0 nil text (len text) NULL) (SelectObject fe.hdc oldfnt))))) (defun win-new-font (fe fonttype fontsize) (let ((lf (znew LOGFONT lfHeight (- fontsize) lfWeight (if (eq fe.drawstatus :printing) 0 FW_BOLD) lfCharSet DEFAULT_CHARSET lfOutPrecision OUT_DEFAULT_PRECIS lfClipPrecision CLIP_DEFAULT_PRECIS lfQuality DEFAULT_QUALITY lfPitchAndFamily (if (eq fonttype :font-fixed) (logior FIXED_PITCH FF_DONTCARE) (logior VARIABLE_PITCH FF_SWISS))))) (push (new font font (CreateFontIndirect lf) type fonttype size fontsize) fe.fonts) (car fe.fonts))) This is a translation of the following function, which is from Simon Tatham's Puzzles. The code to get the font was factored out into a separate function in the above Lisp: static void win_draw_text(void *handle, int x, int y, int fonttype, int fontsize, int align, int colour, const char *text) { frontend *fe = (frontend *)handle; POINT xy; int i; LOGFONT lf; if (fe->drawstatus == NOTHING) return; if (fe->drawstatus == PRINTING) fontsize = (int)(fontsize * fe->printpixelscale); xy = win_transform_point(fe, x, y); /* * Find or create the font. */ for (i = fe->fontstart; i < fe->nfonts; i++) if (fe->fonts[i].type == fonttype && fe->fonts[i].size == fontsize) break; if (i == fe->nfonts) { if (fe->fontsize <= fe->nfonts) { fe->fontsize = fe->nfonts + 10; fe->fonts = sresize(fe->fonts, fe->fontsize, struct font); } fe->nfonts++; fe->fonts[i].type = fonttype; fe->fonts[i].size = fontsize; memset (&lf, 0, sizeof(LOGFONT)); lf.lfHeight = -fontsize; lf.lfWeight = (fe->drawstatus == PRINTING ? 0 : FW_BOLD); lf.lfCharSet = DEFAULT_CHARSET; lf.lfOutPrecision = OUT_DEFAULT_PRECIS; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; lf.lfQuality = DEFAULT_QUALITY; lf.lfPitchAndFamily = (fonttype == FONT_FIXED ? FIXED_PITCH | FF_DONTCARE : VARIABLE_PITCH | FF_SWISS); fe->fonts[i].font = CreateFontIndirect(&lf); } /* * Position and draw the text. */ { HFONT oldfont; TEXTMETRIC tm; SIZE size; WCHAR wText[256]; MultiByteToWideChar (CP_UTF8, 0, text, -1, wText, 256); oldfont = SelectObject(fe->hdc, fe->fonts[i].font); if (GetTextMetrics(fe->hdc, &tm)) { if (align & ALIGN_VCENTRE) xy.y -= (tm.tmAscent+tm.tmDescent)/2; else xy.y -= tm.tmAscent; } if (GetTextExtentPoint32W(fe->hdc, wText, wcslen(wText), &size)) { if (align & ALIGN_HCENTRE) xy.x -= size.cx / 2; else if (align & ALIGN_HRIGHT) xy.x -= size.cx; } SetBkMode(fe->hdc, TRANSPARENT); win_text_colour(fe, colour); ExtTextOutW(fe->hdc, xy.x, xy.y, 0, NULL, wText, wcslen(wText), NULL); SelectObject(fe->hdc, oldfont); } }
- mdaniel 5y agoOne 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(...);