<bitblit1>
So, I asked about compiling your program while it's running to see "live reload". I understand now that anything in the loop with it's own compilation unit will be able to reload while the application is running. However, how will I update things that should NOT be in the loop. Such as init functions. Let's say I am working on creating a game or whatever, I want to write the code to create a window and compile it while the program is running
<bitblit1>
and want to see it update live. However, putting the CREATE-WINDOW function in the main loop will create too many windows. I don't know how to get around this problem. Do I have to check if it is the first instance of the loop? If so, when I compile, will the loop rerun making it the first iteration of the loop?
gnoo has quit [Ping timeout: 265 seconds]
<beach>
What is this "loop" that you are talking about?
<bitblit1>
beach: The main event loop which runs every frame.
<bitblit1>
To update and render the application
<bitblit1>
Or the loop which runs and keeps checking for any events that might have to be processed
<beach>
If your application starts by executing some initialization code and then runs an application event loop or command loop, then if you want to alter the initialization code, it is very likely that you need to restart the application.
<beach>
You could execute additional initialization code at the REPL, like to create a window, but it would be complicated to make your application take it into account. You would have to think hard about how to structure your application in order for it to take such modifications into account.
<beach>
In other words, you can't have an application with some arbitrary structure and then expect it to respond to arbitrary code modifications at the REPL. You have to think about the modifications you might want and structure your application accordingly.
<bitblit1>
beach: Yes, so I understand most of the implementation and structure now. For example, I could create a render method which could look at a list of windows and keep on rendering/updating them. This is great as I could have an empty list and it would render nothing. Now all I have to do is change the window list, however, to do that, I would need to be able to access it easily, through a global variable. If i want to avoid global variables, I
<bitblit1>
would have to create a state class which would keep track of my state and update that. Is this correct? And if so, is it even a good idea?
<beach>
Sure. For instance in a CLIM application, the "application frame" is meant to hold "global" information about the application. At some point, though, you need at least one global variable, and in a CLIM application, this is CLIM:*APPLICATION-FRAME*.
<bitblit1>
Ooooh, this seems interesting. Thanks for all the info beach. Are there any pitfalls or caveats in this process?
<beach>
I am sure there are, but I can't think of anything general.
<bitblit1>
Okay, Thanks again!
<beach>
Pleasure.
<bitblit1>
What is this <CLASS> naming convention, don't see it much, is it good practice or smth?
<beach>
It is a convention from a Scheme-like language defined by Apple. I forget the name.
<beach>
It is not a Common Lisp convention.
<semz>
Dylan?
<beach>
Right, Dylan.
<semz>
I think some Schemes use it too. I might be thinking of Guile
<loke[m]>
<beach> "It is not a Common Lisp conventi..." <- I thought it was clever, until I had to deal with code that used that convention. Then I changed my mind.
<loke[m]>
I think it's Fukamachi that likes that style. He also uses one-package-per-file, which is also highly annoying.
<loke[m]>
I would of course like to clarify: That's my personal opinion. And the annoyance stemmed from me porting over code written in the package/file style to a single package and it was frustrating. I'm sure if you're used to the style and committed to it, it's not so annoying.
les has quit [Quit: Adios]
les has joined #commonlisp
rgherdt has joined #commonlisp
<jackdaniel>
loke[m]: what made you /not/ like the convention <foo>?
<bitblit1>
Yeah I used to use that convention but was like eh, package.lisp + using popular (non?)convention feels good
<jackdaniel>
bitblit1: if you have some "render" loop and you want to change the function that draws it, you may define a function update-window that is called from within that loop like (funcall 'update-window window), then if you redefine update-window then the new version will be used in said loop
pve has joined #commonlisp
<jackdaniel>
in contrary to (loop (draw-a) (draw-b)) where ou'd need to actually stop the loop to redefine its body
<loke[m]>
jackdaniel: Typing the class names. Really, it's that simple. 😀
<jackdaniel>
in /some/ projects I have a macro define-class that defines a class, a function of the same name (being de-facto make-instance) and a variable with a value being the class itself
<loke[m]>
I also decided that it didn't really increase readability, so it felt similar to hungarian notation to me, which is something I don't like.
<jackdaniel>
so I can type <foo> instead of (find-class '<foo>)
<jackdaniel>
of course the most important convention is to stick to the style in the codebase, otherwise it is a matter of taste (I like the convention <foo> personally)
<jackdaniel>
I remember someone arguing that special variables should not have earmuffs
<loke[m]>
That's a much worse take, because special and nonspecial variables have the same namespace.
<jackdaniel>
to deliberely hide the difference in the client code; I'm not sold on that though
<loke[m]>
Classes are in a namespace of its own, so marking them up is not necessary.
<bitblit1>
<jackdaniel> "CodeBitCookie: if you have some..." <- Oh, I think I see
<loke[m]>
jackdaniel: I think anyone who's done any elisp programming knows how annoying it is when dynamic variables are not marked up.
<jackdaniel>
some languages other than common lisp even make up separate accessors for special variables (so they do not share the same namespace)
<bitblit1>
<jackdaniel> "CodeBitCookie: you may find this..." <- Woahhhh, Thats so cool!!!
<jackdaniel>
enjoy :)
<bitblit1>
It's crazy how you guys do this for free and to help people out, inspires others to do the same, growing the community. Can't go without appreciation.
attila_lendvai has joined #commonlisp
<jackdaniel>
don't worry, soon this boring detail will be abstracted away by chatbots - they'll consume the content someone created and answer any question you want
<bitblit1>
Hahaha, probably true
<bitblit1>
:(
Cymew has joined #commonlisp
<bitblit1>
If i make a class tmp something like this:
<bitblit1>
(defclass tmp-class ()
<bitblit1>
((state :accessor state
<bitblit1>
:initarg :state
<bitblit1>
:type (cons integer))))
<bitblit1>
(sorry for the indenting)
<beach>
bitblit1: Please use a paste site for code with more than one line.
kenran has joined #commonlisp
<bitblit1>
one line? Oh, I thought it was like 5
<bitblit1>
sry
<bitblit1>
Why can I create an instance of tmp-class and have the state be set to 'single-float?
<bitblit1>
for example:
<jackdaniel>
the type is your propomise to the compiler, some implementations ignore it, some enforce it on adequate optimize values
<bitblit1>
`(make-instance 'tmp-class :state 2.2)` -> Why does this work???
<beach>
Because your Common Lisp implementation is not obliged to check type declarations.
<bitblit1>
Oh... so like what should I do if I want to make sure the types in my classes are right?
<beach>
bitblit1: I believe recent versions of SBCL will check this if you have a high value of some OPTIMIZE qualities.
<bitblit1>
Or should I just not care about that?
<jackdaniel>
also thanks(?) to the fact that classes can be redefined (so the type declaration may actually change), the compiler can't make assumptions about slot types at compilation time - even if it knows what is the class of the argument
<jackdaniel>
bitblit1: set (safety 3), I think that even sbcl will take a hint
<jackdaniel>
but then keep in mind that NIL is not of the type cons, so if you want to allow default NIL value, then the type should be (or null (cons integer))
<beach>
bitblit1: I strongly recommend you change the default OPTIMIZE settings of SBCL, at least during development.
<bitblit1>
jackdaniel: beach Oh. what should I change the settings too beacch
<bitblit1>
Didn't know about the nil thing either
<bitblit1>
bitblit1: (safety 3)?
<beach>
I use (safety 3) (speed 0) (debug 3) (compilation-speed 0).
<bitblit1>
Oh, I understand so if we want to finally publish a high performance library we can just (declare (optimize 3))?
<bitblit1>
in the specific funcction?
<bitblit1>
or is that bad and optimizaiton is already great
<cpli>
if i only require `open` and `close` (posix) i feel as though there should be some simple posix library; i.e. not osicat
<beach>
bitblit1: I find that the choice of algorithms and data structure is way more important for performance than the OPTIMIZE settings, so I don't bother with such declarations.
lagash has joined #commonlisp
<bitblit1>
Nice. I understand now.
<beach>
bitblit1: From #clschool I have also observed that many newbies are concerned about low-level optimization, including things like (SPEED 3), but those are often the same newbies that have insufficient knowledge about the design of algorithms and data structures. So they have highly micro-optimized, yet slow, code.
<beach>
... which of course often implies unmaintainable code as well.
<jackdaniel>
the code is fast, it just takes longer to run :)
<beach>
Heh, you could put it that way, sure. :)
<bitblit1>
Hahaha. Yeah.
<bitblit1>
IDK why I am dragging on this but one edge case I found was that the :type being (or null (cons integer)) is that only the first element (or cons cell) has to be an integer, and the rest can be anything they want. Is this intended?
<bitblit1>
Or do I have to adjust types for that.
<bitblit1>
So using the previous class we can do (setf (state *tmp*) '(2 2.2))
<bitblit1>
but not (setf (state *tmp*) '(2.2 2))
<beach>
You can nest the type specifier, like (cons integer (cons float null))
<bitblit1>
Okay, but I want ALL my elements to be integers.
<beach>
The thing you can't do is to specify a list of arbitrary length with a particular element type. Other than is you use SATISFIES of course.
<bitblit1>
Oh, I see.
<beach>
But then, should you really be using a list?
<bitblit1>
Huh? Ooooooh lore expands. You mean using an array. Dunno I don't need that level of detail just a list of windows or integers
<beach>
In that case, if you really want to check the type, use SATISFIES. Speed for that kind of thing doesn't matter.
<bitblit1>
Thanks beach.
<beach>
Also, you would probably define abstractions like ADD-WINDOW so that you don't have to use the concrete data type all over your code.
<beach>
Then you can check the type in the abstractions.
gnoo has quit [Ping timeout: 264 seconds]
LW has joined #commonlisp
<bitblit1>
Ohhhh, that's so much better
<splittist>
(deftype window-specifier () '(or window integer)) or something
<bitblit1>
There are already many functions like add-window create-window etc. Although I haven't :USE XLIB, Isn't it kinda wrong to do that
<beach>
It is usually not that good. And by not :USEing it, you can create your own ADD-WINDOW function with a name in your own package.
lagash has joined #commonlisp
<bitblit1>
Okay, will do.
<beach>
bitblit1: I highly recommend you think of the objects you manipulate as part of a (usually CLOS) protocol so that you do not litter your code with concrete representations.
<beach>
Establish the types (usually classes) and operations (usually generic functions) used to manipulate your objects.
<phoe>
what would be an elegant to say "return true if all these elements are EQUAL to one another" in CL?
<phoe>
an elegant way*
<splittist>
serapeum:equal* (:
jmdaemon has quit [Ping timeout: 265 seconds]
<splittist>
which is every 'equal, but making a choice about what to do with no or one element.
<pve>
Is equal transitive? Feels like it must be.. (stupid question, sorry)
<bitblit1>
So with the canonical application class, close, render etc should be methods right? Also, since I won't extending application class, do I need to make explicit defgenerics?
<beach>
DEFGENERIC forms are (unfortunately) optional, and omitting them does not prevent extensions. But I always include those forms, if only to give an explicit name to the parameters so that they are visible in an IDE or in SLIME.
<beach>
I wrote "unfortunately", because it is one of those design mistakes that can make life harder for the programmer. A simple typo in a DEFMETHOD form will silently define another generic function. Fun debugging stuff like that.
<beach>
We have gotten rid of most such design mistakes over the years. Fortran contained lots of those (maybe still does?) and the same thing holds for C.
<bitblit1>
Ohhhh, yeah could definitely see that happening. Will do.
<bitblit1>
So, I should make close, render, and other important functions methods of my canonical class?
<loke[m]>
beach: That's why you have to put `implicit none` in the beginning of your fortran source ;-)
<loke[m]>
s/source/files/, s/;-/:-/
<beach>
bitblit1: Methods are not part of classes, but of generic functions.
lottaquestions_ has quit [Quit: Konversation terminated!]
lagash has joined #commonlisp
jmdaemon has quit [Ping timeout: 240 seconds]
<bitblit1>
<beach> "CodeBitCookie: Methods are not..." <- Oh, I see the difference
<splittist>
bitblit1: a literal CLOSE is going to conflict with CL:CLOSE if (as seems likely) you've USEd the cl package. But you probably meant CLOSE-WINDOW (:
tibfulv has quit [Remote host closed the connection]
tibfulv has joined #commonlisp
<bitblit1>
Ohhh, yeah I was lliterally just trying to debug that problem, thanks a lot splittist
lottaquestions has joined #commonlisp
<splittist>
Has anyone been working on a baserow/smartsheet style thing (web-based, user-configurable db frontend) in lisp?
<phantomics>
splittist: I built a system that's basically a web-based visualizer for CL forms and systems that includes a spreadsheet module
lottaquestions has quit [Client Quit]
<phantomics>
Wasn't a db frontend though
lottaquestions has joined #commonlisp
<splittist>
phantomics: possibly even better. The forms/systems were persisted as source, in the image, with backups, all of the above, or I'm completely misunderstanding things (:
<phantomics>
The forms and systems were persisted as source, specifically ASDF systems were the top-level division
<phantomics>
It could have been used as a DB frontend, just wasn't limited to that. The project is dormant with plans to build a new version once some of my other dev goals are reached
<phantomics>
It's called Seed, you could say it was a very different kind of IDE with support for multiple visual programming frontends whose input composed to CL source code in an ASDF system
<ixelp>
GitHub - robert-strandh/Cluster: Assembler with input in the form of standard instances
<bitblit1>
The safety 3 and other optimizations made my life sooooo much easier... hahaha I was coding with a (little) pain for so long
<beach>
phantomics: I don't see that there is any implementation-specific code in an assembler, so I don't see why any care needs to be taken to make it portable.
<phantomics>
Very cool, so there's potential to expand it to other architectures as well? Could it be used to build functions and load them in a running CL instance the way you would any other function?
<phantomics>
About implementations and assemblers: I've been working with SBCL's vop system to do JIT assembly of optimized kernels, one caveat with the vops is that there are some registers you shouldn't use because SBCL uses them and you can clobber values that are needed by the compiler
<beach>
Yes, I want to add support for RISC-V, but most architectures are trivial compared to x86. Whether you could use it in an implementation would depend on how that implementation is designed, I would think.
<phantomics>
For instance, in 2.3.0 putting anything in R12 and R13 will cause memory faults, I was able to avoid this by putting them on the stack for the duration of the function and popping them at the end, but I was advised not to do this and also that the restricted registers may change from version to version of SBCL
<beach>
phantomics: I had no ambition to create an assembler for any existing implementation, so I didn't give any particular thought to that aspect.
<hayley>
Yes, don't do that. From memory, R12 is used for thread-local state, and R13 for the card map used by the garbage collector.
<hayley>
Are you performing your own register allocation? I think the register allocation done for VOPs would already avoid those registers.
<phantomics>
So in order to assemble loadable functions you need to know the gotchas for a given implementation, and those may change between versions.
<phantomics>
hayley: I was experimenting with manual allocation, now I only manually allocate the A and D registers because you need to handle them specifically due to their role with MUL, DIV and some other stuff
<beach>
phantomics: Sure, an assembler just takes representations of instructions (in this case as instances of standard classes) and produces a sequence of bytes. It is up to the user to put in the right instructions and the right arguments to those instructions, including the registers.
<phantomics>
Perhaps CL implementers could include a parameter listing the registers that shouldn't be changed and any other gotchas that an assembler needs to avoid on a given impl
<hayley>
It would also be necessary to have a way to install a function from a byte vector, which might be awkward for ECL, and more awkward for ABCL.
<phantomics>
The performance of the JIT-built vops has been amazing, April's performance for the specific operations I've optimized may be the fastest of all CPU-based APLs, unfortunately it's limited to SBCL for now
<hayley>
Any APL\3000-esque optimisation between operations?
<phantomics>
Was 3000 doing data flow optimizations?
<phantomics>
Yeah ABCL may not be feasible, would be great to support ECL though
<beach>
phantomics: It is not that simple. Some registers could be used for other things in some parts of the code, but not in other parts of the code.
<hayley>
It was, and it was compiling code involving multiple operations.
<phantomics>
Short answer: yes. April has been rebuilt implementing lazy evaluation in the last year. The code generated by the compiler produces a tree of CLOS objects whose methods generate an optimized function for transforming an array
tursom_ has joined #commonlisp
jonatack has quit [Ping timeout: 268 seconds]
<hayley>
(Or, in a Common Lisp context, I might as well ask if it's more like Petalisp (which does all the data flow analysis, and compiles larger "kernels") or numcl (with functions individually hand-tuned).)
<phantomics>
One of the most dramatic things it lets you do is for example (april-f "5 5↑¯20 ¯20↓80 80↓3/20⌽2000000 2000000⍴⍳10*10")
<phantomics>
This is creating a 2 million square matrix, rotating, stretching and cropping the entire thing, then taking the upper-left 5x5 of it
<hayley>
.oO(take 5 [1..] is infinitely more dramatic)
<phantomics>
Because it starts from the end and works backwards only the 25 output elements are actually computed
<phantomics>
The array restructuring functions work by processing array indices as encoded integers. For instance, if you have a 3D array whose dimensions are all under 256, its coordinates can be encoded in a 32-bit int. Coordinates (1, 2, 3) can be encoded as #x00010203 for example
<phantomics>
So for instance if you're doing a take of the array which crops off 10 elements on all sides, all you need to do is add #x000A0A0A to each coordinate value
<phantomics>
You can chain these operations together to do multiple array transforms in a single loop without writing the intermediate arrays into memory
<phantomics>
Using CLOS initialize-instance and metaclass allocator overrides, it's also possible to obviate some classes when they're created
lagash has joined #commonlisp
<phantomics>
For example, 3⌽3⌽X becomes 6⌽X and ⍉⍉A becomes just A because two successive permutes cancel each other
edgar-rft has joined #commonlisp
easye has quit [Ping timeout: 265 seconds]
X-Scale has joined #commonlisp
easye has joined #commonlisp
<bitblit1>
cool
<bitblit1>
i like APL and april
<phantomics>
Thanks, you've been working on GUI stuff?
<bitblit1>
phantomics: Not really, just random stuff. I am making a window manager called blitter-wm and other small projects like a calculator for CLOSOS.
<phantomics>
Fun, a WM for CLOSOS?
dcb has joined #commonlisp
<bitblit1>
Not really, just for myself. I was tired of StumpWM being wierd and unmaintined. Wanted a simple X window manager entirely in CL which would be super customizable. However, this was my secret preperation for making a WM for CLOSOS
<bitblit1>
Am on StumpWM rn btw
<phantomics>
Very cool, with a better CL WM I may want to switch to one
kenran has quit [Remote host closed the connection]
<phantomics>
I see you're working on figuring out libx11 now
<bitblit1>
Yeah!
<phantomics>
If you could get some workflow display logic in there would be cool, like being able to spawn collections of windows and desktops for a specific task
<bitblit1>
You give me more motivation
<bitblit1>
phantomics: Could you elaborate a little more? I would love suggestions! Also, you can be sure to add them as TODOs (issues in git repo)
<ixelp>
bitblit/blitter-wm: A minimalist and configurable window manager made entirely in Common Lisp. - blitter-wm - Codeberg.o [...]
<phantomics>
Say I want to define a task like video editing, which will involve the video editor, Inkscape for designing caption graphics and Audacity for creating the audio track. It will start those programs and display them in a specific defined layout with constraints based on the monitor form factor
<phantomics>
And also give a set of key shortcuts for moving between them. Another simpler workflow could be "image editing with April" where I have an Emacs window for interacting with April and an image viewer window for viewing the image I'm generating. This can be done entirely in Emacs but it could also be a workflow based on multiple desktop apps
X-Scale has quit [Quit: Client closed]
<phantomics>
I could put a TODO but it's not very concrete as a goal, something more specific that you could improve over Gnome/KDE/xfce is configuring keyboard shortcuts for doing different things, a Lisp-based config would be great for stuff like that with unlimited options
lisp123 has quit [Remote host closed the connection]
<bitblit1>
Ah, I think I understand phantomics Great idea. Will put that as a GOAL
<bitblit1>
How do I create a macro wrapper around a function which is sensitive to keyword arguments passed to it effeciently? For example:
<bitblit1>
I have a function whose argument list looks like this: `(&key (host "") (display 0 display-p) (screen 0 screen-p))`
<bitblit1>
I want to create a macro/function which would run it in a separate thread and take the exact arguments
<bitblit1>
so it's argument list should also look exactly the same.
silasfox has joined #commonlisp
<flip214>
bitblit1: you can use &rest to capture all arguments and relay them on; but be aware that a macro gets _unevaluated_ arguments whereas the function gets _evaluated_ args