libera/#commonlisp - IRC Chatlog
Search
19:16:35
doomduck
a question about CFFI, I'm trying to call this https://github.com/longlene/cl-raylib/blob/master/src/raylib.lisp#L2252 which wants a :pointer via CFFI, but uhm ... looking at the CFFI manual I'm not really sure how to construct a pointer to a number on the CL side?
21:34:07
doomduck
can I somehow access a constant defined in a package that doesn't export it? specifically this https://github.com/longlene/cl-raylib/blob/master/src/raylib.lisp#L1241 ... I guess I could just copy the number in this case if there's no way tho lol
21:37:28
doomduck
oh awesome thanks! they have a TODO on this whole section, so I assume it's probably something they want changed
21:41:11
phoe
basically, in Lisp, using the foo:bar syntax means "can I please use this symbol?" whereas using foo::bar means more or less "*reloads gun* I aren't asking this time"
21:41:44
phoe
at which point lisp is nice enough to give you the symbol, but yeah at this point it's just your responsibility to use it without breaking stuff
21:43:48
verisimilitude
Note, doomduck, that the :: syntax works even if the symbol be exported, so changing it if the symbol becomes exported isn't necessary.
22:07:33
kakuhen
no harm in always referencing a symbol SYM in a package PKG as PKG::SYM, but the reader will have no immediate answer on whether PKG::SYM is exported or not -- my argument is mostly for the reader's convenience
22:09:33
phoe
lisp is a language for nice people and nice people generally ask for access, hence using single colon is preferable
22:14:55
doomduck
phoe: I thought lisp was a language for people who don't ask for anything and just take what they want because they can literally patch anything in anything :D
22:31:52
aeth
Ime, nobody exports their classes. Maybe because they're afraid of someone doing (make-instance 'foo) when that's not the proper way to make it, but it also stops you from doing (check-type x foo)
22:32:21
aeth
This is essentially the only time I use :: the "right" way, outside of my own unit tests or when I'm doing something I probably shouldn't do (messing with the internals of something).
22:33:46
phoe
I kinda pointed this out a few times - if a function returns some sort of type then I should be able to TYPEP it
22:34:10
phoe
and if you are really paranoid about people making instances the wrong way, there's ways to guard againt that too
22:34:58
phoe
(defvar *paranoia* t) (defun make-foo () (let ((*paranoia* nil)) ...)) (defmethod i-i ((foo foo) ...) (when *paranoia* (error ...)) ...)
22:35:43
aeth
the lispy way is to use some arcane incantation of the MOP that nobody really understands, but that seems to work
22:37:11
verisimilitude
Yes, Lisp doesn't really support encapsulation or whatever one wishes to call it.
22:43:32
kakuhen
well, being able to access internal symbols easily is convenient when you're dealing with a library and wanting to use some utility function it has but marked private, or maybe extend a function in the library and unfortunately it invokes private functions
22:43:50
kakuhen
I've ran into issues like this when writing Clojure. There is a clever workaround to it, but it's much more gross than just :: in CL
22:45:28
aeth
(let ((x 42)) (defun x () x) (defun (setf x) (value) (setf x value))) ; the variable X is actually encapsulated by the functions X and (SETF X)
22:47:18
aeth
verisimilitude: The debugger may or may not actually expose x. All lexical variables are only expected to show up when (optimize (debug 3)) and otherwise could be eliminated. In this toy example, it's unlikely, but who knows? As long as the getter/setter accessors still work as expected.
22:47:37
verisimilitude
I've used Ada for several years now, and it's so much better than Common Lisp in this and other ways.
22:48:19
jcowan
"Data and procedures and the values they amass / Higher-order functions to combine and mix and match / Objects with their local state, the messages they pass / A property, a package, a control point for a catch — / In the Lambda Order they are all first-class.
22:48:20
jcowan
One Thing to name them all, One Thing to define them / One Thing to place them in environments and bind them / In the Lambda Order they are all first-class."
22:48:33
verisimilitude
I can define types in a package and totally control their subprograms, initialization, and even things such as assignment or equality.
22:48:58
aeth
There are other ways to use scope/names to encapsulate that may or may not be fragile/portable. e.g. uninterning a function after having callers using it, redefining an inline function, redefining a macro, etc.
22:49:07
jcowan
I think the CL answer to that is, If you want to chop off your foot, don't let *us* stop you.
22:49:32
aeth
verisimilitude: Yes, but by your own standards, you can probably use the Ada debugger to interfere.
22:50:19
aeth
Failing that (if the Ada debuggers somehow guard this), you might be able to use a cheat program (since they're mainly used for cheating in games) to edit the memory.
22:52:39
jcowan
See also Henry Baker's paper "How to Steal from a Limited Private Account -- Why Mode IN OUT Parameters for Limited Types Must be Passed by Reference" <https://web.archive.org/web/20191008050810/http://home.pipeline.com/~hbaker1/LimitedRobbery.html>
23:24:07
jmes
I want to initialize an object with some extra necessary parameters that aren't initargs. My current approach is defining an initialize-instance :after method with some keyword params which errors when the params are not given. Is there a better way?
23:25:06
jmes
The motivation for doing this is I need some extra information to perform the initialization but that information is not going to be stored in a slot.
23:25:13
kakuhen
what's wrong with leaving the slots unbound until you're ready to provide bindings?
23:25:40
kakuhen
alternatively you can do what people do for structs, where a slot defaults to an error being signalled
23:29:18
jmes
kakuhen: Well the object has a slot which should be computed based on some initial information coming from those other keyword parameters. But saying this aloud (so to speak) makes me realize I can probably expect that computation to happen beforehand, then pass the result into make-instance.
0:09:12
doomduck
being new to this, what do people use for the regular functional list/sequence manipulation? I know there's mapcar, but I'm imagining things like stuff in LINQ with all the where/first/flat-map/etc. ... I mean I know how to write these myself, it's more like "is there a nice/popular library?" ... not exactly sure how to search for quicklisp stuff
0:10:19
aeth
Not always functional but e.g. (loop :for i :from 0 :below 20 :collect i) or (loop :for i :from 0 :below 20 :sum i)
0:14:23
fe[nl]ix
aeth: I compiled that on my laptop, so not Ubuntu 18.04 but I want to test the build
0:15:06
fe[nl]ix
aeth: src/runtime/sbcl.extras is statically linked to libfixposix and openssl-1.1.1l with openSUSE patches
0:17:21
doomduck
aeth: is it recommended to learn loop instead of composable tools? it feels very very adhoc, but maybe that's just me being a noob, but not sure if i'm a fan of a big magic macro
0:18:26
verisimilitude
It's always preferable to use a standard function than some library, at least for me.
0:18:56
verisimilitude
It's not fun to audit some code and see a single usage of some library where LOOP would've sufficed.
0:19:45
fe[nl]ix
doomduck: learn loop because it's in the standard. its deficiencies won't matter until you want to write and maintain large amounts of code
0:20:54
aeth
doomduck: The big messy monoliths of LOOP and FORMAT don't have to be composable, but they can be used that way.
0:21:19
aeth
FORMAT can take in an arbitrary stream, while LOOP can be used as I just used it to return values
0:21:38
aeth
So you can mix-and-match them with other things even though some people probably (ab)use them as an all-in-one solution
0:22:39
aeth
for a toy example that probably isn't useful: (defun mapeven (function list) (loop :for item :in list :for evenp := t :then (not evenp) :when evenp :collect (funcall function item)))
0:23:27
aeth
if you're writing composable functions, you're probably defining them from fairly simple LOOPs
0:31:56
doomduck
speaking of this, how do I do something like (and ,@(list t t nil)) but not inside a macro? like I have something that gives me a list of truthy things, and I just want to `and` them, and I can't (apply and things) because it's not a function
0:32:53
aeth
(defun mapfizzbuzz (function list) (loop :for item :in list :for i :from 1 :for fizz := (zerop (mod i 3)) :for buzz := (zerop (mod i 5)) :for result := (funcall function item) :when fizz :collect result :into fizzes :when buzz :collect result :into buzzes :unless (or fizz buzz) :collect result :into rest :finally (return (values fizzes buzzes rest))))
0:36:24
doomduck
can I somehow do that in a loop so I don't have to do (every p (loop for x in xs collect (thing x)))?
0:36:37
doomduck
I guess that's kinda a silly question considering I literally just asked how to get a "more composable loop" lol
0:38:10
aeth
(mapfizzbuzz (lambda (x) (when (zerop (mod x 3)) (format t "Fizz")) (when (zerop (mod x 5)) (format t "Buzz")) (unless (or (zerop (mod x 3)) (zerop (mod x 5))) (format t "~D" x)) (write-char #\Space) x) (loop :for i :from 1 :to 15 :collect i))
0:41:06
doomduck
verisimilitude: hmm I'm not sure if that does the same thing, this (loop for x in '(1 2 3 4 5) thereis (progn (print x) (evenp x))) just prints 1 2 and returns T, evne tho clearly not evenp for every element
0:43:43
doomduck
except in my case the list is generated by a loop, hence my question how to include that in the loop to avoid nesting
0:48:20
doomduck
I guess the very nice things a bout using `always` in place of `every` is avoiding the ugly LAMBDA
0:54:18
_death
(collect (mapping ((x (scan-range :from 1 :upto 15)) (fb (mask (scan-range :from 14 :by 15))) (f (mask (scan-range :from 2 :by 3))) (b (mask (scan-range :from 4 :by 5)))) (cond (fb "Fizzbuzz") (f "Fizz") (b "Buzz") (t x))))
1:06:00
_death
maybe more series-y would be (collect (mapping ((x (scan-range :from 1 :upto 15)) (b (expand (mask (scan-range :from 4 :by 5)) (series "Buzz"))) (f (expand (mask (scan-range :from 2 :by 3)) (series "Fizz")))) (if (and f b) (concatenate 'string f b) (or f b x))))