freenode/#lisp - IRC Chatlog
Search
21:01:25
p_l
CL is dynamic/strongly typed language. The former because of runtime type checks (with gradual static support as optimizations in compilers), the latter because it has a very strong notion of what a type is etc. (a bit too strong, in a sense, with satisfies)
21:18:48
Xof
mgsk: none at all. (I was only a speaker, not the poor victim who has to edit the videos)
21:20:02
aeth
p_l: Calling CL dynamic is misleading when some compilers, perfectly permitted by the standard, do both static and dynamic, making "gradual typing" a more accurate descriptor.
21:22:31
White_Flame
"gradual" never seems right to me. Each instance is a boolean, either assuming or checking the type
21:22:33
aeth
Calling it "dynamic" implies that you can't (defun foo (x) (declare (single-float x)) ...) although, yes, that isn't entirely portable behavior because in theory some non-existent (afaik) implementation could assume types instead of either statically typing (SBCL) or ignoring the declaration (most implementations).
21:23:09
aeth
SBCL at safety 0 is a bit tricky. It assumes the types at runtime, but probably still does its limited static type checking at compile time.
21:24:52
aeth
I said probably because I'm not going to write a test case right now, but I'd be surprised if it removed the limited static type checking.
21:27:35
aeth
White_Flame: "gradual typing" implies that you're going to start out dynamic and then gradually add static typing as your program advances. I think "mixed typing" would probably be a better name, but since dynamic/static is often confused with strong/weak, I'm sure "mixed typing" could confuse people into thinking that the language is both strongly typed in places and weakly typed in places or something.
21:28:21
White_Flame
but again, the doesn't distinguish itself as being part of the strong/weak or static/dynamic spectrum
21:28:27
p_l
aeth: dynamic is perfectly fine with (defun foo (x) (declare (single-float x)) ...) - it just means that type check will be done at runtime
21:28:40
aeth
One problem with CL, though, is that :type in CLOS isn't really respected. Only CCL seems to respect it by default, although SBCL respects it with high debug values, implying that it's only useful for debugging.
21:29:28
aeth
Another problem with static-typing-in-CL is that there aren't typed lists (trivial to write your own with structs if you can accept a performance loss) or hash tables, and non-T arrays are very limited in what types that they can have.
21:30:12
p_l
that's so long as you keep to portable standard, yes (though lists I believe are untyped in all implementations)
21:31:04
aeth
p_l: You're incorrect about type declarations. SBCL will do the type check at compile time as well, as long as it's within the same compilation-unit. SBCL actually has typed functions, with typed parameters and a typed return value (although the latter is rarely seen).
21:31:43
aeth
It's limited, though, because the function can be recompiled at any moment to have a completely different function type so SBCL will only trust it in limited cases, most commonly when it's in the same file.
21:31:56
p_l
aeth: I'm trying to stick to what the standard says, and even on SBCL you can end up with uncompiled code trying to call the functions so unless you do (safety 0) the type check will happen
21:32:45
aeth
p_l: Yes, in SBCL it's both statically and dynamically typed when you use type declarations, unless it's safety 0. Although I would assume most gradually typed languages that come at the problem from the dynamically-typed side would behave similarly.
21:32:59
White_Flame
I'm always amused when SBCL compiles an expression it warns about to a simple call to raise an error
21:33:51
p_l
aeth: that's what I'm getting at. By default we get dynamic, but compilers are free to go further where possible. Meanwhile with many "statically" typed you need to write your own type checks and variant code
21:34:24
aeth
pjb: Typed collections are wonderful. You type-check on setting or initialization and you can trust the type on access, providing more information to the compiler's type inference, without having to have a fancy macro inserting THEs all over the place (which SBCL won't even trust, so you'd need truly-the for SBCL)
21:35:25
White_Flame
there's always Haskell, if you want to program in types as your primary feature
21:35:26
aeth
And you couldn't even trust a the/truly-the macro because the user somewhere else could just bypass it.
21:36:09
pjb
aeth: it's ludicruous: all the reference types, and most integer types are compiled into the same binary!
21:36:43
pjb
aeth: in anycase, you can always use libecl, or greenspun like crazy, of course. But this is not what C programmers do.
21:36:54
copec
imo going into experimental programming thinking about type is like going in thinking about optimization before you've even flushed out your idea
21:38:06
aeth
White_Flame: I'm not sure why you'd recommend Haskell when I said "set rarely and accessed often". I think Haskell wouldn't be the ideal language for setting (i.e. mutation) because it's discouraged.
21:38:33
White_Flame
no, but it's focused on programming in types first, then adding a bit of implementation glue
21:40:40
p_l
excuse me, we are very obsessed with optimizations, we just do it without creating crazy UB in compiler
21:41:21
aeth
p_l: You could write an entirely statically typed program in CL. Sure, the staticness wouldn't be portable, but you could probably write a portable static checker like people do for various languages that don't even have an implementation like SBCL. The define syntax would be inconvenient, but a wrapper macro on top of defun is so trivial I've probably done at least a dozen such define-foos in one program.
21:41:59
Fare
scymtym: thanks for noticing. Why didn't gitlab.common-lisp.net warn me that the issue was open???
21:42:14
p_l
aeth: I'm actually tempted to branch out further from "keep to very simple CL" and do a lot of macrology this time, because I'm trying to write a performant Lisp Machine emulator :)
21:42:29
aeth
p_l: And assuming type T when no type is specified is actually the reasonable default. Why do otherwise?
21:43:02
p_l
White_Flame: it's something I'm actually tackling recently, where I have a function that recompiles a bunch of very statically controlled stuff when I need to
21:43:27
p_l
(in my case, it involves a lot of macros iterating over objects and constructing a function from them)
21:44:56
White_Flame
p_l: yeah, CL needs to move into the JIT world if further gains on speed are going to be introduced
21:45:31
White_Flame
well, that's what you're talking about, isn't it? recompiling things if the assumptions change?
21:46:30
White_Flame
but yeah, the literal acronym "JIT" isn't sufficient to describe such optimizations. I guess "dynamic recompilation" would be a better fit
21:46:32
p_l
White_Flame: I'm essentially making macros that build functions that are then processed by macros to build an uber-function that implements the dispatch unit of a Symbolics Ivory chip
21:47:04
p_l
the function to rebuild this is going to be called manually, when I change something like adding new instruction or modifying the macros that help me build the instruction-functions
21:47:39
aeth
White_Flame: You only need JIT if you can't directly specify what you mean. That's probably why LuaJIT is such a performance win over Lua. When everything is a table, you need JIT magic to magic things into more appropriate representations.
21:48:12
aeth
I'm not sure a JIT could beat SBCL in performance because of all of the engineering effort put into SBCL
21:48:13
White_Flame
aeth: specifications are burdensome, and heuristics can find specs that you might not be able to
21:48:27
p_l
most CL implementations already include the kind of dynamic programming that JIT optimizers do, btw
21:48:51
aeth
White_Flame: But once you know what you're doing, you can make a macro out of it and get rid of almost all of the boilerplate.
21:50:14
copec
I would like to do some investigation into CL performance as it relates to the graph on https://julialang.org/ starting from here: https://github.com/JuliaLang/Microbenchmarks/
21:52:17
aeth
White_Flame: Imo what CL's best at is this: Specify exactly what to do, in a macro, and give a high-level declarative interface. Most languages that give you the opportunity to do the latter don't give you enough control over the former.
21:53:21
White_Flame
I want to teach it how to get things done, and how to figure out how to gain performance on its own. I've spent decades doing all that by hand, and it's a repetitive waste of time
21:54:43
White_Flame
especially for large, complex projects, hand-tuning everything gets superlinearly burdensome
21:54:59
Fare
If you have an account on gitlab.common-lisp.net, you may want to check your email setting
21:55:21
White_Flame
and that includes creating simple macros and such to help manually declare the hand-tuning
21:56:49
aeth
White_Flame: I find it more convenient to use a define-function macro as a target for my macros, rather than directly using defun. It'll handle things like (declaim (inline foo)), type declarations, etc., and since I directly control it I can add whatever else I need.
21:57:44
aeth
I originally intended only for the highest level to use fancy macros that hide most things, but it seems to work well at most middle levels, too.
21:58:53
White_Flame
"since I directly control it", ie, you put yourself & manual effort in the critical path ;)
21:59:05
aeth
I've been aggressively reducing reptition. e.g. I wrote a with-accessors* that is just like with-accessors except (foo bar (quux baz)) becomes ((foo foo) (bar bar) (quux baz)) because it's very common for me to just name the symbol macro created by with-accessors after the accessor.
21:59:19
aeth
And, sure, I directly control most of these convenience macros, but most of them are trivial.
21:59:29
aeth
I compose quite a few trivial macros to do more elaborate things and simplify the more advanced macros.
22:00:27
aeth
Using define-function and with-accessors* gets rid of a lot of repetition in the macros. Combine the two in one macro and now I no longer have to handle type declarations directly in that macro and I don't have to have the (lambda (x) `(,x ,x)) all over my code that with-accessors (and similar interfaces) would produce
22:00:57
White_Flame
it's about offloading actual autonomous programming & optimization work to the environment
22:01:41
aeth
That's the thing, though. This work only has to be done once, then it's a framework for someone else to use.
22:02:00
aeth
The CL community is small. That's the only reason why "doing it once" is often you doing it, and not finding a framework
22:02:28
White_Flame
but again, as I said, this is my soapbox. People who are working on different problems don't necessarily see the need for other people's desired solutions
22:04:45
aeth
White_Flame: I guess my point is that you don't have to worry about performance in CL if someone else has done that worry for you in the domain you're in. It's just that the small community means that that is unlikely.
22:07:19
p_l
White_Flame: you might want to search for information about "Programmer's Apprentice", I believe that was the name
22:07:27
White_Flame
the domain is highly dynamic, changing assumptions that should be reflected in compiled code
22:08:32
p_l
White_Flame: also, look for Marko Heisig (iirc) presentation at ELS2015 (I think it was 2015) where he describes dynamic programming (i.e. auto-adapting) on a supercomputer :)
22:08:44
White_Flame
as such, the entire body of work for statically arriving at an optimal static compilation is a red herring
22:10:53
rpg
Oddball question: if I have a structure of class node, and there's an extension of node, counter-node, that adds a slot, is there some way to copy a node instance to a fresh counter-node instance? I was trying to copy-node my node instance, and then change-class the fresh node into a counter-node, but change-class doesn't work on structure-class objects...
22:11:39
rpg
possibly the right thing is to simply replace my structure class with an object class, but that's at least a mild pain.
22:54:01
zazzerino
Is `eq` the correct equality function to compare keywords? (It works on sbcl, but I'm wondering if this is portable...)
23:00:07
aeth
From looking at the spec: eq, eql, equal, and equalp should be identical for symbols (including keywords) and there is no symbol= in http://www.lispworks.com/documentation/HyperSpec/Body/c_symbol.htm
23:00:48
aeth
eq will be slightly faster if the type is unknown because eql will use = on numbers and char= on characters, but I think most people would use eql in their style
23:01:47
aeth
the spec will describe how they all differ, e.g. http://www.lispworks.com/documentation/HyperSpec/Body/f_eql.htm
23:04:10
pierpa
EQL means equivalent. The two objects must act identically on every possible operation, with the only possible exception of EQ.
23:06:02
aeth
I wonder if (eql -0f0 0f0) => nil is correct because "If x and y are both numbers of the same type and the same value" seemed to give me the impression that (eql -0f0 0f0) => t
23:07:37
White_Flame
zazzerino: symbols are usually interned. So :foo and :foo will always be the exact same object in memory, so you can use the most specific comparator
23:08:20
edgar-rft
aeth: In a spec it's sufficient to look at the pictures, you don't need to read the text.
23:08:24
White_Flame
at a practical leve, you can consider eq to be a CPU register equality comparison
23:08:39
aeth
edgar-rft: well, you'd think that they'd put the edge cases in the examples in addition to the text
23:08:47
White_Flame
eql will also compare larger numbers and characters, which might be boxed (ie, larger than 1 machine register, living on the heap somewher)
23:09:54
White_Flame
and there are more type specialized comparisons, like = for numbers specifically, string=, etc
23:09:55
aeth
equalp uses = for numbers, equal uses eql for numbers. eql, I guess, can be thought of as using = on numbers of the same type except for the -0 vs +0 issue on floats if the float representation has them as distinct values.
23:12:16
aeth
I guess there's no concise way of explaining eql's behavior there, especially because of bignums and boxed floating point representations.
23:18:08
aeth
White_Flame: time for an equal? that ignores string case including unicode string case
23:19:04
White_Flame
so if you want to value-compare composite data structures, you either do it case-insensitively, or write your own descender (or override EQUALP to a custom function)
23:22:30
pillton
White_Flame: It is possible that equalp was defined in accordance to principles of the "Common" in Common Lisp.
23:24:21
aeth
On the one hand you get an ugly, inelegant language that is compatible with things no one uses anymore. On the other hand, attempts to start a Lisp from scratch miss very obvious features that large applications need.
23:24:43
White_Flame
pillton: certainly, I"m curious what that history is, for that particular issue
23:25:38
White_Flame
I've gone through pretty much all the genera manuals, but I think that was probably too recent
23:32:45
aeth
"Note: Every time you use EQUAL on a number, you will get a warning that the function EQL is undefined. Don't worry. This will be fixed later."
23:40:48
rme
Xach: FYI, I finally found and committed a fix for the call-next-method problem that you ran into in the 1.12-dev branch of ccl.
3:30:21
edgar-rft
skidd0, is the Lisp REPL not text enough? Or what specifically are you looking for?
3:31:38
skidd0
so, mostly placing text around the terminal display, with some | bars and +---+ beams
3:36:04
edgar-rft
I usually use cl:format for ASCII graphics, but I think there are ncurses bindings for colors and stuff in the <https://www.cliki.net/>
3:48:47
ZombieChicken
skidd0: Might also try termbox. I think there is a lib to interface with it via FFI
7:37:30
xificurC
sbcl seems to disagree with me. Probably it needs to compile and execute the require before understanding foo:bar?
7:40:16
beach
xificurC: The FOO package needs to exist when the reader reaches FOO:BAR, or else an error will be signaled.
7:41:54
xificurC
beach: building a script that will be called from the command line. When you write ./command foo xyz I need to load package foo. When you write ./command bar xyz I might need to load bar or some other packages
7:42:59
xificurC
of course this is not set in stone and I can slice things as I wish but I would like to have a toplevel script that dispatches appropriately
7:45:25
xificurC
I could call a defined function and the requiring of a package would register it somewhere for the function to find it but that means foo needs to know about something above it
7:47:23
beach
Sorry, I don't use Common Lisp that way, so I haven't thought about the kludges that might be require to make it work.
7:48:14
beach
But if you want to write FOO:BAR in your code, the package FOO needs to exist when that code is read.
7:48:26
xificurC
beach: do you mean you don't use it like I'm describing or you don't write scripts that would be run from the command line?
7:48:45
beach
If you don't want to do it that way, you may have to do something like FIND-PACKAGE, then INTERN, then SYMBOL-FUNCTION.
7:49:42
scymtym
xificurC: you could make that work using (uiop:symbol-call '#:PACKAGE '#:SYMBOL) but if you want something like git, it seems better to dump everything into a single executable file ahead of time
7:50:52
beach
xificurC: If you write FOO:BAR in your code and try to compile it, then the READ function will first read it. When READ sees FOO:BAR, it looks for a package named FOO. If that package does not exist, an error will be signaled.
7:51:11
beach
xificurC: So you can't even COMPILE your code unless the FOO package has been created.
7:52:17
beach
xificurC: So, the code that reads (funcall (symbol-value 'foo:bar)) [note the quote] can not even be read by the compiler unless the package FOO exists.
7:52:53
beach
xificurC: What scymtym is suggesting is basically a shorthand for what I suggested, i.e, find the package with that name, find the symbol with that name in that package, find the function with that name, call it.
7:53:19
xificurC
I'll just require it in the toplevel for now. This is a habit taken from the previous version of this tool (written in python)
7:54:50
White_Flame
Even consider this, at the toplevel: (when <test> (require :foo)) (when <test> (foo:bar))
7:56:41
xificurC
White_Flame: well sure, but that's like saying (progn (let ((x 1)) (+ x 1)) (+x 10)) breaks
7:57:08
White_Flame
it has to do when the side effects of creating a package take place, vs READing source code
7:57:45
xificurC
eh, I'm not saying it's the same thing, just that you have to think of the consequences and structure your code
7:57:46
White_Flame
I don't know if you can get away with #+ style omission of an entire source code term if that term contains unreadable symbols