freenode/lisp - IRC Chatlog
Search
1:49:45
White_Flame
since there's no fallback or anything. The output must be valid in the lower language
1:50:16
aeth
that doesn't mean anything, though, since you can use a reader macro to essentially completely change everything
1:50:29
Ober
for me running a clojure shop, the cto wanted to do all the CL ways with it, but in the end it's just java.
1:52:25
White_Flame
I have it bookmarked, but I don't know clojure yet so I don't know how well it works
1:55:03
aeth
eww, I don't know what's worse, that it uses .lisp for clojure files or that Github detects it
1:55:49
aeth
it uses the same library as Gitlab so that might explain why most of my Scheme is erroneously being identified as Scheme. My macros are too good at faking Scheme from CL.
2:21:01
no-defun-allowed
I use them enough that not having them could be annoying, but I don't try to use them frequently.
2:22:34
no-defun-allowed
In my opinion, the interactive environment is well above average (it could be better, but it's still superb).
2:24:24
no-defun-allowed
Here is a use of a macro I wrote that defines a program for my bytecode interpreter: https://gitlab.com/cal-coop/netfarm/netfarm/-/blob/master/Tests/benchmark.lisp#L77
2:25:59
no-defun-allowed
I guess the macro is trivial enough, in that (define-script name constants . body) expands to (defparameter name (compile-program constants body)), but having the code be normal lists and symbols instead of some other structure makes writing a program fairly simple.
2:26:24
phadthai
I like that it's fast for prototyping without caring about optimization and then it can be worked in where too slow; I like the interactive and incremental development; I like that for every problem that can be expressed more elegantly it doesn't always mean needing to rewrite a language or config parser or interpreter sacrificing performance (macros are great there)...
2:28:10
phadthai
there are things to dislike of course too, on some very small systems, if not wanting cross-compiling, lisp might just be too heavy vs another interactive language like forth
2:28:17
White_Flame
the thing is, many languages have taken on aspects of lisp (first class functions, GC, rich numeric stack & type system, REPL, etc) but none integrate it together as well as lisp does, because their code forms are not plain data
2:28:47
White_Flame
syntax becomes the enemy when you're running code at compile-time, or transforming data specs into runnable code
2:28:58
phadthai
another is that popular languages have tons of libraries that may lag behind for lisp or just be too sparsed among disparate individual developer repositories, the work to interface to other stuff like FFI can be challenging at first too
2:29:26
no-defun-allowed
There are also compilers (not batch compilers, like gcc or ghc, they can be invoked at runtime) that produce decent machine code for unoptimised programs, which as phadthai says, lets you leave the most optimisation for the slowest parts.
2:30:45
no-defun-allowed
There's a lot of context you would want, but I have a bytecode interpreter which can be run by other people over a network to do things reproducibly.
2:31:39
White_Flame
lisp is a great language to create small embedded utility languages inside of it; and/or create new high level languages on top of it and not worry about machine code details
2:32:02
White_Flame
if you haven't hit the limits in other langauges yet, it's nearly impossible to describe the benefit
2:32:33
no-defun-allowed
Okay, another one. You can invent your own namespaces in Lisp using macros, which IMHO is a bit cleaner and more general than using decorators in Python.
2:33:56
aeth
srandon111: It depends on what you're doing. Sometimes what you're doing is best expressed as a macro.
2:34:30
phadthai
it's a compiled language that evolved many dynamic aspects, as such it's also interesting: can be surprisingly fast with good code on a good implementation, while at the same time very dynamic; things like the distinction between defvar and defparameter are also important, it decides what happens at image load time or when recompiling some code in a live interactive image
2:34:38
srandon111
no-defun-allowed, aren't you talking about namespaces as i know them in java/ and so on?
2:34:55
aeth
srandon111: Once you're used to macros (which takes a while) a macro is just list traversal. The hard part is making it look like it was built into the language so people can actually read the macro. What you don't want is something like LOOP, but e.g. writing your own CASE with a different quality test is perfectly fine
2:35:42
no-defun-allowed
Say you have a web server framework, and you want to define a function for handling some page. In Python you might write @get("/url") def url_handler(...): ...
2:36:01
no-defun-allowed
In Lisp the framework could write (define-easy-handler (url-handler :url "/url") ...)
2:36:13
srandon111
no-defun-allowed, since i think this thing about macro is quite questioned by noobs like me... isn't there a website/resource which tries to explain the benefits with examples?
2:37:10
aeth
no-defun-allowed: right, properly written macros look more built-in than other a lot of other languages' metaprogramming, particularly C++<template<template<template<template... and Python/etc. @stuff
2:37:30
phadthai
and in lisp the html templates can result in much native code (much processed at compile time) vs all interpreted
2:37:37
aeth
And if you use the right names, nobody's going to confuse a macro and a function, especially since a macro probably should have a &body
2:37:49
aeth
And since you can see the API in something like Emacs, if you see &body you know it's a macro
2:38:45
srandon111
aeth, yes most likely i am missing all this stuff since i still don't really know well lisp
2:38:51
aeth
phadthai: right, any kind of HTML template system is either going to turn into a constant string at compile time, or (if it takes in input) it's going to generate something more or less like a FORMAT string
2:39:30
White_Flame
srandon111: simple case: You want to add instrumentation to your code, to track execution for profiling, coverage, whatever. The desire is "Every time I define a function, I want it statically recorded somewhere in a table, and on entry/exit it should call code and report on its parameters automatically. It's a right pain in other languages; in CL you simply wrap DEFUN in another macro
2:39:47
aeth
srandon111: I think Clojure is more culturally against macros than Common Lisp is. In Common Lisp, it's fine as long as it fits a simple pattern, like WITH- (usually wrapping an UNWIND-PROTECT, but sometimes just a LET), DEFINE- (almost always ultimately just a DEFUN), etc.
2:41:52
aeth
I kind of hate the DO-FOO pattern a bit because that can get annoying... iterators in LOOP (which aren't a thing) would be a lot more convenient
2:41:56
White_Flame
srandon111: and that DEFUN macro can look at the _source code_ of the DEFUN, and handle the name, the literal parameter list, etc, and generate the _source code_ which performs all the registrations and wrap the body source code of the defun wiht the tracking stuff
2:42:52
aeth
In general, the scariest macro you're going to see is a DEFINE-FOO that is built on DEFINE-BAR that is built on a DEFINE-BAZ that is built on a DEFINE-QUUX ... etc. that ultimately is a DEFUN
2:43:54
White_Flame
and that transformation is just plain lisp code, which can call all the same utility functions etc (as long as they're there at compile-time)
2:44:38
White_Flame
you can literally have your own DEFUN that source code will syntactically use, by setting up your packages right
2:44:47
no-defun-allowed
Usually a macro makes the language one works in "higher level", by allowing the client to ignore how it's implemented.
2:44:51
White_Flame
or, you can have DEFUN* or whatever variant that you specifically use intentionally
2:45:10
aeth
There's exactly one time when a macro is really, really bad and that's code-walker macros. No one does them correctly. Imagine a macro that walks through the source and replaces all (foo 42) with "Aeth is the best programmer because Aeth writes the most clever macros." There are so many ways that can break.
2:45:28
no-defun-allowed
It's more of a case of "I don't want to know", rather than "I don't get to know"; but MACROEXPAND (or the macroexpander mode in SLIME, invoked using C-c M-e) lets you figure out what the macro does.
2:46:11
White_Flame
and building onf n-d-a's comment, Lisp is one of the only runtime-compiled languages that actually lets you drill allt he way down to the assembly language to see what's going on
2:48:40
aeth
Keep in mind though that if a macro doesn't have a &body (more rarely, some other form of nested destructuring), it's almost always a bad idea because you can just inline a function instead. Obviously, it's needed for things like OR, though.
2:50:04
srandon111
ok so basically with macros i can pass "the body" of a function to it and modify as i wish it right?
2:50:32
aeth
I'm not a Clojure programmer, either, but I'm guessing that the more purely functional your language is, the more you prefer higher order functions, which do pretty much all of the same things, just syntactically differently. In fact, quite a few macros are just wrappers over a higher order function or a lambda (all the DEFINE- ones are basically just globally binding a lambda)
2:51:12
White_Flame
srandon111: consider IF. It has 'test', 'then' and 'else'. You treat those as opaque code blocks that you assemble a larger body with
2:53:00
aeth
srandon111: (let ((x 42) (y 43)) (+ x y)) is the effectively the same thing, and could be implemented, as ((lambda (x y) (+ x y)) 42 43) so you never need macros as long as you have lambdas, technically.
2:53:34
aeth
Of course, the LET is clearer, and if you want to do LET* the same way, you're going to get much, much messier because now you have to next lambdas of one variable... but it's not that bad if you have a currying macro or however that's done.
2:54:18
aeth
I don't know about Clojure, but I do know that in Scheme, they really love making everything ultimately just be a lambda if you dig deeply enough.
2:54:45
aeth
Common Lisp isn't as purist, a lot of things in macros are ultimately just contained gotos in the form of GO within a TAGBODY (or implicit TAGBODY)
2:55:19
no-defun-allowed
As another example of macrology, look at a demo like https://www.youtube.com/watch?v=prIwpKL57dM&t=1217 that I found in my logs. Instead of writing an interpreter for this little simulation as the presenter did, you could generate Lisp code that does what the rules do.
2:55:46
srandon111
White_Flame, ok so basically i can wrap my function to do e.g., debugging... but what are other typical applications?
2:55:46
White_Flame
yes, that's another thing. It's generally as easy (or easier) to write a compiler in lisp than an interpreter
2:55:51
aeth
This has some implications. Common Lisp and Scheme both have nearly-identical DO macros, but if you create a closure in Common Lisp, the DO will capture the final binding of the variable, while Scheme will remember the value, because Scheme used lambdas and Common Lisp used gotos so Common Lisp was updating the environment each time while Scheme was making a new one.
2:56:35
White_Flame
given a code body, trying to dissect it can be problematic because you don't ultimately know what's code and what's data in those sublists
2:57:02
White_Flame
so construction via macros is fine. Destructuring bodies inside macros can be problematic in edge cases
2:57:42
aeth
White_Flame: Well a compiler in Lisp just targets asm in s-expression form or native Common Lisp itself (since the compiler will just run on it eventually anyway), while an interpreter will target some abstract machine... so compilers are a bit easier than interpreters in Common Lisp imo.
2:57:56
White_Flame
sure, completel function generation, defining data tables & code bodies from specs, adding features to defun/defstruct/etc, doing custom tests, etc
2:58:29
White_Flame
srandon111: the thing is, I was a metaprogrammer in other languages, and it was a massive pain
2:58:58
White_Flame
lisp made all that complexity go away, and makes codegen such a simple and integrated operation that you don't even think anything special or difficult of it after a while
2:58:59
aeth
srandon111: if my codewalker expands (foo 42) to "Hello world" then (foo 42) looks like a function, but it never existed. Depending on the implementation, it might also erroneously substitute (foo 42) in (let ((foo 42)) ...)
3:02:16
aeth
srandon111: In Common Lisp a macro is basically just something like this, with bindings in and some quoted source form as the output: (defmacro foo (&body body) `(quote ,body))
3:02:47
aeth
I chose QUOTE as the simplest macro to demostrate because then you can do (foo a b c d) and get (A B C D) out. Otherwise, it would try to evaluate.
3:02:56
White_Flame
srandon111: here's some very simple syntactic sugar macros, to map one lisp-like langauge onto common lisp: https://github.com/white-flame/clyc/blob/master/subl-support.lisp#L218
3:04:06
White_Flame
things like passing in names of local variables; a macro can expand into doing something with that var, while you'd have to do tedious wrapping to make a function out of it
3:04:26
aeth
This is slightly more useful: (defmacro reverse-call (&body body) (reverse body)) (reverse-call 1 2 3 +) => 6
3:06:08
White_Flame
so the macro expands to destructively concatenating a list in the front of the existing value of the variable
3:06:20
aeth
90% of Common Lisp macros come down to understanding quasiquote, although if they're really trivial like my reverse-call macro, they don't even need that, they just need to return a list at the end.
3:07:28
White_Flame
but the model of what's going on is that it takes source code, and returns source code, which is compiled into your functions
3:08:07
aeth
` just makes things look like the resulting source code and aren't really needed, you can do (list a b 'c 'd) or you could do `(,a ,b c d) and it's the same thing. Generally, you prefer ` unless you need to put a ` in a ` in which case good luck knowing how to read the nested layers of unquoting ,
3:08:44
srandon111
White_Flame, ok let's continue this conversation in few days i mean i clearly need some studying
3:09:54
aeth
,@ is a bit trickier because it splices the following list in. It basically removes a layer of parens. So something like `(progn ,@body) is fairly common in macros because it takes a list of Lisp syntax and puts a PROGN in front so it doesn't try to funcall the first element, but instead executes them in order
3:12:07
srandon111
White_Flame, ok thanks i did that... also if i have to admit that for now... to me it seems more appealing the lambda philosophy of scheme languages
3:13:26
White_Flame
srandon111: yes. Scheme started as an academic minamalist language, which meant you need to build everything from scratch
3:13:36
aeth
Other than the use of ` , ,@ and GENSYM, macros are basically just regular list processing.
3:13:49
White_Flame
common lisp formed by coalescing together a bunch of commercial Lisp implementations, and has a very practical basis
3:14:00
aeth
GENSYM is... a bit more complicated. Basically, you want to do (let ((g (gensym))) `(defun foo (,g) ... ,@body)) if you don't want the code in body (which is the user's code) to be able to see the variable g
3:16:52
aeth
srandon111: well, Scheme prefers lambda and CL prefers (unhygienic) macros. You can do one style in the other language (at least if the Scheme provides a defmacro or define-macro)
3:18:02
aeth
Even Scheme has LET. Just like Scheme prefers non-generic code (compared to CL, which is often generic), but even Scheme's + is generic because converting between number types would be annoying.
7:38:26
no-defun-allowed
The core file I have to make SLIME start faster is 46MB, but I can't remember if it's compressed.
7:39:05
no-defun-allowed
Okay, it's uncompressed (you pass :compression <compression quality; 1 through 9> to compress)
7:39:15
beach
adam4567: That order of magnitude is to be expected. It contains things like the compiler.
7:40:07
no-defun-allowed
But yes, Common Lisp images usually contain the debugger, compiler, and everything that was present before you S-L-A-Died.
7:40:43
no-defun-allowed
I get 40MB and 11MB uncompressed and compressed (using :compression 9) images, respectively.
7:45:27
no-defun-allowed
Er, the image is basically a copy of everything in memory, SBCL doesn't need to load anything else.
7:47:36
no-defun-allowed
I sure hope not; the SLIME manual told me dumping an image would make it load faster.
7:49:05
adam4567
looking through sbcl manual now, trying to find how to Load the image .. do forgive me. sbcl -M image.mem I thought