8 ms·
Format is awesome, it is a domain specific language for printing things. It can walk across lists, print all sorts of number radixes, make tables of data, and h
by mtreis86 4y ago
Format is awesome, it is a domain specific language for printing things. It can walk across lists, print all sorts of number radixes, make tables of data, and has its own condition system. I don't get the hate. It is not lispy at all in style, but isn't making a DSL just about the most lispy thing you could do?
- taeric 4y agoAgreed. The format string isn't an s-exp, but the general idea feels very lisp like to me. Similarly, I like LOOP more than I'm supposed to, if I go by what I read online.
- nerdponx 4y agoI feel like avoiding the use of LOOP could be a Master Foo koan. An ambitious young student of Lisp came to Master Foo, seeking to deepen her knowledge and understanding. One day, during their studies and practice, the students stopped and said, "Master, I am troubled. In Lisp we have minimal syntax, and we use S-expressions to convey structure, and this syntactic uniformity gives us great power to build our own composable syntax constructs. And yet we have LOOP in the standard, which is an ad-hoc imitation of the same arbitrary syntax of other languages that we use Lisp in order to avoid!" The next day, the Master brought the student on a hike into the hills. As the sun was setting late in the afternoon, they reached a small cabin near the summit of a hill. A village of huts was visible in the valley below them. "Observe that this building is constructed from many small pieces of wood, each nearly uniform in size and shape. With those pieces I long ago built a hut that resembles the shoddy huts of the village, yet this one is both strong and easy to modify. You could even build it yourself from plans." The student observed and agreed, and was very impressed, but was also confused. "Master, what does this teach me about Lisp?" she asked. Master Foo continued, "You shall stay here on the mountain tonight. Return to me in the morning." With that, the Master drew a key from his robe, firmly locked the door of the cabin, and hiked away. As he did so, a cold rain and steady wind began to blow. The student spent the night cold and damp on the ground beside the cabin, and awoke enlightened.
- srcreigh 4y agoRacket has for/list and even for/fold. They’re just different syntax for map and foldl. I prefer them to the functional versions sometimes so I don’t have to write lambda, the for loops have options for combining multiple lists, etc.
- taeric 4y agoThe loop macro is impressive for many of its esoteric keywords. Maximizing, collect, append, etc. There is also the for/then construct that I find much easier to work with than alternatives for the same behavior. I have no doubt much of this can be done with the racket constructs. But I still find I can more easily read loop usages I've written in the past. Whereas many similarly dense for/folds are opaque within a few minutes for me. :(
- LanceH 4y agoThe thing about functional purity is that the point of a lot of programming is to get the side effects.
- packetlost 4y agoI think that's still pretty well-understood. The real draw to FP in general is you reduce the globally modifiable state to be the things that are truly global to an application. For web applications, this may be a database. For systems applications it may be a filesystem.
- valenterry 4y agoThe point of fp is not to get rid of effects, but to make them explicit and better controllable and do that in a consistent way, not on a case by case decision.
- nyx_land 4y agoAgreed. People give a lot of hate to FORMAT and LOOP, but I think with both of them for the domains they're working in, it's kind of a natural progression of the evolution of a DSL (which as you said is very lispy since reader macros are one of the biggest strengths of CL) that you'll end up with something like FORMAT or LOOP that handles a lot of common use cases for string formatting or iteration as tersely as possible. It's inherently less flexible than pure s-exps (nothing is as flexible), but IME solves most problems where you need string formatting or iteration. Plus it's part of the ANSI CL spec, and sticking as close to the spec as possible and being conservative with external dependencies is usually a good thing in CL.
- nerdponx 4y agoWell the point is that it could have been an actual macro instead of a function that processes a syntactical-opaque control string. I do appreciate the convenience and brevity of FORMAT as it currently exists, but I also think it would be pretty cool to have a proper S-expression macro that expands to a FORMAT control string. It would be analogous to a macro that generates SQL or HTML.
- aidenn0 4y agoIt can't be a macro without requiring the string to be constant.
- nerdponx 4y agoThat's what I had in mind, which covers most use cases of FORMAT that I've seen.
- kazinator 4y agoCommon Lisp provides an explicit macro for this called formatter. The macro produces a function. The format function allows a functional argument in place of the format string for this reason. (format t (formatter "~a-~b") x y) is like (format t "~a-~b" x y) except that "~a-~b" is transformed into a lambda which takes a stream and two arguments, and which does the specified formatting.
- nerdponx 4y agoThere are so many corners of the spec that I have not yet explored, so I always appreciate trivia like this. However I was thinking more like the macro that the article was proposing.
- medo-bear 4y ago> but isn't making a DSL just about the most lispy thing you could do? 10000% agree. LOOP and FORMAT are excellent examples of using lisp to build dsls. so much so that they made it into the ANSI standard
- kazinator 4y agoThe formatter macro is an exercise in Lisp, but an interpretive format doesn't even have to be written in Lisp.
- deleted 4y ago[deleted]
- lisper 4y ago> isn't making a DSL just about the most lispy thing you could do? Yes, but with great power comes great responsibility. The problem with FORMAT and LOOP is not that they are DSL's, but rather that they are badly designed DSLs. The reasons are different in both cases. FORMAT is badly designed because it is write-only, kind of like Perl regexps. It is very, very hard to debug a complex FORMAT string. LOOP, by way of very stark contrast, is very readable, even more so than regular Lisp. But it is badly designed because it is chock-full of non-orthogonal constructs. For example, WITH is completely equivalent to a set of LET bindings outside the loop, so it doesn't actually add any functionality. All it does is give you a new non-lispy syntax for creating external bindings. The semantics of FOR depend on what comes after. "for x =" does something completely different from (for example) "for x in". And the list of problems with LOOP goes on and on and on. So the problem is not that DSLs are bad, the problem is that these DSLs are bad. But since they are part of the standard, we are stuck with them.
- lispm 4y agoThe idea is that the WITH variables are local to the LOOP construct. If one leaves the LOOP, the variables are gone. With an outside LET this would not be the case. The LOOP thus gives scope for all lexical variables inside of it. Additionally the LOOP sees the WITH definition in standard Common Lisp. It does not see any outside LET bindings, unless the Common Lisp would provide a feature to ask the current environment. This allows the LOOP to, for example, see a type declaration and provide a default value for the common cases. (let (a) ; a is initialized to NIL (declare (type integer a)) .... (loop repeat 10 do (incf a)) ... a) vs. (loop with a fixnum ; a is initialized to 0 repeat 10 do (incf a) finally (return a)) It provides a compact notation with ONE scoping construct, the LOOP macro. Similar for the LOOP name. In the same way you could argue that this is just a named BLOCK around the LOOP construct. But, again, the name inside the LOOP makes it clear that the this is the name of this LOOP construct. Btw., the ITERATE macro works in a similar way.
- lisper 4y ago> The idea is that the WITH variables are local to the LOOP construct. If one leaves the LOOP, the variables are gone. With an outside LET this would not be the case. Of course it would. This: (loop with a = ...) is exactly equivalent to: (let ((a ...)) (loop ...)) and so you can e.g. capture with-bindings in closures: (funcall (loop with a = 123 return (lambda () a))) ==> 123 > Additionally the LOOP sees the WITH definition in standard Common Lisp. It does not see any outside LET bindings, unless the Common Lisp would provide a feature to ask the current environment. This allows the LOOP to for example see a type declaration and provide a default value for the common cases. I think it is highly questionable whether providing implicit typed default values is actually a useful feature, but assuming for the sake of argument that it is, why not just make a general binding construct that does this? If it's useful, it should be useful (and usable) everywhere, not just inside a loop. > you could argue that this is just a named BLOCK around the LOOP construct Indeed you could. :-) > the name inside the LOOP makes it clear that the this is the name of this LOOP construct. Yes, but why is that useful? How is (loop named foo ...) any better than (block foo (loop ...)) ? And if it is better, then why not (progn named foo ...) ? (tagbody named foo ...) ?