freenode/lisp - IRC Chatlog
Search
2:45:25
basket
It is still an imperative construct built on jumps and mutable assignment, and that mutable assignment is part of its visible semantics
2:45:25
aeth
That is a huge difference. for loops statements are necessarily used for their side effects while do is an expression
2:46:11
basket
I don't understand how you can defend DO because you can write code that is oblivious to the mutable semantics, but you simultaneously write off out parameters because they allow writing code that takes advantage of it
2:46:31
basket
DO also allows writing code that takes advantage of it, and you can also write code that is oblivious to the mutability in out parameters
2:49:16
aeth
Using multiple return values of non-mutable objects like numbers enforces purely functional code in that section of the program. The performance overhead isn't high, and afaik most of that performance overhead is already paid for by implementing support for multiple return values in the first place, whether or not you use them. Inline those pure functions and good compiler magic can happen.
2:50:05
basket
When you were talking about out parameters, you explained why your argument about DO was faulty
2:50:23
_death
here is a simplistic functional DO: https://gist.github.com/death/3ca4dab9770ffce2910f853f08ad1b66
2:50:27
aeth
Using do directly isn't perfect. It's much better to write a macro on top of do if you can get away with it.
2:51:06
aeth
It's not a contradiction to make different design compromises in different parts of the program. It's idiomatic CL to do so. CL is incredibly multi-paradigm, perhaps the most multi-paradigm language out there.
2:52:04
aeth
do, like many parts of the CL standard, can be used in a functional way but is not required to be used in a functional way. This is especially apparent in iteration, e.g. map.
2:54:02
basket
Mutating the object being traversed in a call to map is explicitly undefined behaviour in the standard
2:55:10
basket
The fact that you can pass an impure function to MAP is not the same thing as MAP's interface *forcing* you to use mutable state
2:55:40
aeth
"map nil" pretty much forces you to use a mutable state, just not the sequence(s) being iterated over.
2:56:37
basket
It calls a function that it's passed; the body of that function is not part of MAP's definition
2:56:48
aeth
It has everything to do with how map works. "map" provides support for a "nil" return so you can use it for dirty, impure things.
2:58:46
aeth
map nil is essentially CL's foreach, especially since the other equivalent, dolist, only works on lists, not sequences in general.
3:01:35
aeth
Is "for foo in foobar" pure if the language makes it UB if you mutate foobar? And if it's not pure, is there really that much of a difference between a lambda in map nil and a statement body in a for each statement?
3:03:54
basket
aeth: Yes, the specified part of its semantics are pure, provided that `foo` is a fresh binding on each iteration
3:04:48
basket
It could be implemented as a macro on top of pure lambda application; it does not take advantage of mutable state
3:06:09
aeth
The do operator is more of a super-iteration thing that wants to make everything possible. If you want to build your own iteration, pure or impure, and restrict it, you can directly use do, or use macros that probably directly use do like dotimes or dolist (although they're not required to use do)
3:07:52
basket
aeth: And you can implement mutable semantics on top of lambdas, too; that's just turing completeness
3:10:31
aeth
CL is basically a language for writing (mostly) efficient declarative languages, while not itself being declarative (which wouldn't really be possible, there are many kinds of declarative languages)
3:11:08
aeth
I'm using the definition of declarative that includes FP under declarative. Wikipedia uses this definition.
3:12:31
aeth
Another possible phrase other than 'declarative languages' would be 'fifth-generation programming languages'. This is sufficiently archaic enough that it might have been an explicit goal of CL.
3:13:47
whoman
some people like to work with limbs, some like to play with guys. others prefer the clothings or outer garments, while others yet freely form their style
3:14:01
aeth
You want ugly mutability to be possible in something like CL's do because you might need it when writing an interpreter or compile-to-CL compiler or whatever you're writing. If you want it to be functional, either restrict your use of do (through discipline) or build a pure macro on top of do (trivial)
3:15:47
whoman
when skeleton protrudes flesh, we have horns and other undesirable jagged and bony features. it is good that the system works right all together with itself. CL is ultra-powerful about that
3:16:53
dmiles
aeth: speaking of mutibility.. i have be struggling within DO deciding between compiling to prolog to use a mutable loop vs a non mutable loop :) non mutable loops are ugly to look at and debug
3:19:10
tko
which lisp do you guys prefer , I've been using clojure and at times elisp but I'm about to try common lisp
3:19:46
nyef`
tko: This is a Common Lisp channel, so the argument here will be about particular Common Lisp implementations. SBCL being the best, of course. d-:
3:20:14
dmiles
aeth: well i started out basing everything on tagbody compiel design for a worsecases jumping arround.. but indeed i might simplify it
3:20:15
tko
aha oh ok, this is about my first time on irc, I'm connecting through emacs and just searched "lisp"
3:21:04
aeth
tko: #lisp is for Common Lisp and possibly direct historical precursors to Common Lisp, ##lisp is for the Lisp family of languages.
3:23:23
nyef`
tko: Also, whatever version of SBCL you just installed is probably out-of-date already. Last release was SBCL 1.4.2. Current git HEAD is 1.4.2.215 or so.
3:24:00
aeth
dmiles: What I personally find useful for advanced things (and Prolog would count) is to write a lot of macros, and perhaps use those macros in other macros.
3:24:06
dmiles
not that nayone aked but this is my DO / DO* https://github.com/TeamSPoon/wam_common_lisp/blob/master/prolog/wam_cl/block.pl#L86-L103
3:25:25
nyef`
tko: But don't let that stop you. My primary installed SBCL is 1.3.8.26 or so, and I keep a 1.0.23 around for some things.
3:27:54
dmiles
aeth: i write in imperative prolog about 50/50 from declarative.. i think they only teach declaritive
3:29:11
dmiles
aeth: my favorite cartoon: http://www.pathwayslms.com/swipltuts/teacher/images/cartoon_08.png
3:29:38
nyef`
tko: Fair enough. I'm looking forwards to the 1.4.3 release, as a bug that I really need fixed just got fixed recently. As in within the last twelve hours.
3:30:37
aeth
dmiles: I guess people who learn Lisp in school think it's about purely functionally processing lists (which is wrong on multiple levels) and people who learn Prolog in school probably think it's... sort of like SQL for classical logic, I guess. Define some relations and deduce things in a query.
3:32:46
aeth
nyef`: If you like abuse of SQL, wait until I complete the transitioning of my entity-component-system to an in-memory database. Then you'll see some fun abuse of queries.
3:34:51
aeth
dmiles: Does the MOP let you abstract over the concept of a slot completely? I might make an "ORM" of sorts to make things easier to debug and inspect interactively.
3:35:46
dmiles
so far as a can tell so far, it does let me abstract over the concept of as slot (or field)
3:36:58
aeth
well, if I can just say that the slot foo is really (aref whatever (id-to-index 42)) and slot bar is really (array-row-of-4 something-else (id-to-index 42)) then nothing's stopping me from writing an "ORM" of sorts.
3:37:38
dmiles
teh difficult part for me (when putting objects into the cold storage of the database) is when people are messign with cones suppoedly in a slot
3:38:37
dmiles
i have to keep such a CONS in warm livving memory.. and make the coldly stored slot value a ppointer to live memory
3:39:04
aeth
In case I was unclear, (array-row-of-2 some-array some-index) returns (values (aref some-array some-index 0) (aref some-array some-index 1)) and the setter is similar, except it'll take in n values (2, 3, or 4 depending on which one)
3:40:30
aeth
dmiles: That's not an issue for me, since my "database" is just going to be abstractions over 1D or 2D arrays, depending on what's being stored (i.e. either a simple value or an array row)
3:41:20
aeth
It can, of course, be written to disk for persistence, such as saving a game, although maybe save-lisp-and-die should be the only way to save a game :-p
3:43:17
aeth
Types will be a bit of a problem because only character and bit arrays are guaranteed. In fact, with my approach, character arrays themselves are problematic.
3:43:21
dmiles
what is returned by (values (aref some-array some-index 0) (aref some-array some-index 1)) going to be the values copied over or more liek places?
3:44:01
aeth
At the moment, I only put in things that are (probably) specialized arrays so I don't have to worry about it. e.g. single-float or various integers or bits
3:44:25
aeth
If I want to support more types, I'll have issues, and I'll also have to wrap around check-type in places etc.
3:46:35
aeth
I'm pretty sure the only time you copy in CL is explicitly, e.g. copy-foo (a struct foo automatically define a copy-foo) or copy-seq
3:47:50
dmiles
before you said specialized typed arrays i was sorta was imagining that you were copying a reference .. like a pointer to the nth index of the array
3:49:56
dmiles
i was thinking about how in some situations seeing what is even there at that refenrce is not needed until it was needed
3:52:17
dmiles
Bike any thoughts on aeth's and my question? <aeth> dmiles: Does the MOP let you abstract over the concept of a slot completely?
3:52:47
Bike
i'm not sure what that means. you can definie pretty different slot concepts, at least.
3:52:52
aeth
Well the actual use right now is to essentially copy with-accessors, but with the addition of an index because the accessors all have an additional index, with three possibilities: (aref foo index) (aref foo index another-index) (array-row-of-n foo index) where n is 2, 3, or 4 at the moment (doing it generally cannot be as efficient). The first two return a value, the third returns n values. The first two set one value, the third sets n val
3:53:17
aeth
the third sets n values. (These are accessors, setf array-row-of-n is defined for sizes 2, 3, and 4)
3:54:03
beach
dmiles: Your recent questions make me more and more convinced that you don't know the semantics of Common Lisp.
3:55:23
beach
Well, not a question this time, but things like "like a pointer to the nth index of the array".
3:56:18
aeth
an example of my abstraction in its current state: (with-selection ecs (entity-id) ((location (old-location old-location :row-of 3) (location location :row-of 3))) (setf old-location location))
3:57:59
aeth
accessors can be ":row-of n" where n is 2, 3, or 4. These work with multiple values (either providing them or requiring them in the setf). It can also be e.g. "1" such as (location.y location 1) and it can also be something simple if it's not a 2D array, e.g. (foo? foo?) where foo? is, unfortunately a bit of 1 or 0 instead of a boolean. This is one place where the abstraction leaks. bit arrays are optimized, boolean ones are not.
3:59:57
aeth
An object abstraction over this would probably be to get everything that's defined for some ID, and pretend that it's one object so it's easy to do something like e.g. print the object or inspect it in slime. This would be very easy if slots can return and set multiple values. I'm not sure if they can. And if they can, would it break the SLIME inspector?
4:02:21
aeth
That's not that big of an issue because there are two ways to access the 2D arrays, either one slot at a time (like my example where I bind a location.y) or as multiple values
4:04:01
aeth
I may have to define official names for them, though. At the moment any slot names for a slot in a 2D array are only bound at the accessor level, i.e. (location.y location 1) binds (aref location some-index 1) to location.y in a symbol-macrolet not unlike with-accessors
4:04:14
aeth
dmiles: Well, not really, since that would require bounds checking and this is for a game engine.
4:05:39
aeth
dmiles: I defined array-row-of-2, array-row-of-3, and array-row-of-4 as accessors (both to get values and set values). You can use them improperly. If you e.g. (setf (array-row-of-2 some-array-with-length-four-rows 42) (values 1f0 2f0)) it'll just set the two fields. If you do the reverse and do array-row-of-4 on something with length-two rows, it will fail with a runtime error unless safety is 0, which no one should ever use.
4:07:10
Bike
i don't understand what multiple values have to do with slots. you defined some multiple value accessors, no big.
4:08:10
dmiles
aeth: oh, i hear you, you are thinking about wether or not it is sensible to create an accessor
4:08:34
aeth
Bike: If I can abuse the MOP to work somewhat like this as well, I create an alternative interface into the database that's more first-class within the language, so I could have more debugging power.
4:08:59
dmiles
aeth: sensible in the sense that there is at least some translation in which debugging would be livable
4:09:28
Bike
i don't see what multiple values adds to debugging. they're not fun to debug, in my experience.
4:09:51
Bike
if you were dedicated, you could probably define an alternate standard-object with a completely overriden allocate-instance so that slot values are stored in an array or whatever.
4:10:34
jasom
anyone know of a decent library for making a fininte state machine in lisp? something like a LABELS inside LOOP would be nice. Mutually recursive functions work with proper optimization settings, but I've been told not to do that in common lisp :P
4:11:06
aeth
Bike: well, I was wondering if I could make a similar interface through CLOS, although I guess I *can*, just as accessors, rather than as slots, and the slots would have to use the direct one-value accessor.
4:11:31
Bike
yes. that's what i'm saying. i don't understand why you want multiple value slots when you already have multiple value accessors.
4:12:41
aeth
The main thing I'd want from CLOS is a way to see an ID as an object rather than a bunch of arrays that mostly contain irrelevant values. It doesn't even need to be efficient, since ideally it'd only be used for debugging purposes like defining a print-object.
4:13:48
jasom
nyef`: well I find tagbody looks good for writing out longhand, but labels with a loop has the logic inside-out and is problematic to indent automatically.
4:14:39
aeth
Bike: Yes, but what I would want is a CLOS object that can have slots called e.g. location.x, location.y, location.z, etc. And then if I inspect that object, location.x will show the value of (aref location (id-to-index entity-id) 0) and location.y would show the value of (aref location (id-to-index entity-id) 1) etc.
4:15:30
aeth
It is an entity's position in the world. 3 single-float values, normally. Some systems use double-float if the worlds are massive.
4:16:15
Bike
so you want an object that doesn't actually store its position, it just stores an index into some global array.
4:18:04
aeth
But I would like to be able to use the SLIME inspector, have defined print-objects, etc.
4:18:18
dmiles
having slots is so you can at least feel like you are holding an object in your hands?
4:18:22
aeth
i.e. I would like to make a CLOS object whose slots are accessors, if that's at all possible
4:19:59
dmiles
having slots is so you can at least feel like you are holding an object in your hands? I see yes.. so you can use things like print-object can even change values on the target if needbe
4:20:06
jasom
" defstruct without a :type option defines a class with the structure name as its name. The metaclass of structure instances is structure-class. "
4:20:28
aeth
Bike: I can have an entity with the label cube which is mapped to the ID 42 which is mapped to the index 37 (well, I haven't written the third part quite yet, but that's coming very soon... at the moment IDs are the indices). The entity doesn't exist. There are many arrays.
4:21:49
Bike
perhaps i should be more explicit. there is no need for you to use print-object for printing some non-object. you can have your own printing function to print it. this is no loss to you since nothing could possibly call print-object on your object because there is no object.
4:22:28
Bike
if you wanted, you could define a kind of proxy object that just stores 37 and defines a bunch of accessors, but you haven't, which presumably you have some reason for.
4:22:35
aeth
Well, I could (if I modify my query system to allow for an "all") write something called print-entity that queries all of the places where 37 is a valid index, and prints it as if it's one object.
4:24:58
aeth
But I'm wondering how deep I can go in making a proxy object, which could have uses where the query macro(s) fail, although the performance would probably not make it anything useful outside of debugging.
4:25:27
Bike
you can obviously define a proxy object and define a print object method on it that prints whatever you want, and the object having data in its slots is immaterial.
4:25:28
aeth
If I could actually have proxy slots instead of proxy accessors, I could use the SLIME inspector itself.
4:26:40
Bike
i think swank has some kind of interface you can customize, but it might not be exported.
4:28:00
aeth
It would be very useful (more useful than just a print, which I can do through a query macro with some small changes) if I could somehow inspect an object and see the slots as if it was an object, even though they're just slots in various arrays.
4:35:10
aeth
Bike: thanks, looks like going beyond that it's defmethod all-slots-for-inspector ((object standard-object))
4:36:06
Bike
shorter version: if you want to inspect weird things, you don't need to parlay that into sweeping ideas about clos
4:38:56
aeth
It looks like swank uses the MOP, so if the MOP is powerful enough to change the concept of what a slot is, I should be able to make it SLIME inspectable without having to modify swank. i.e. if I can make a slot a call to an accessor (well, actually two since I don't actually know what the index is, only what the ID is)
4:40:59
aeth
The inspector is just what I know would be useful now. If I use the MOP, it's possible that there's a future use to the object abstraction.
4:49:22
aeth
One thing I could do that's probably easier and even less efficient is to have a query object, i.e. query into an object or something.
4:49:42
aeth
This is probably the first thing that I would have thought of in a less powerful language without a MOP
4:55:12
aeth
i.e. instead of having the object constructed via the MOP to directly access the slots, I construct an object in a similar way that I would do something like print-entity, except returning an object with populated slots from the accessors instead of writing to a stream.
4:58:36
tko
hmm I just started CL a few minutes about coming from Clojure but I'm curious what this convo is all about
4:59:00
tko
I don't know anything about MOP / CLOS, although CLOS is one of the first things I wanted to look into with common lisp
5:01:24
aeth
tko: I have a very elaborate data structure. Because it's elaborate, it's not easy to inspect the contents in an intuitive way.
5:02:09
aeth
One way to inspect the contents in an intuitive way would be to allocate a temporary standard-object (i.e. an object that's created with a CLOS defclass) that somehow has the same contents because those are trivial to inspect.
5:03:24
aeth
Things are inspected through SLIME, the CL IDE for Emacs. swank is the protocol that SLIME uses to talk to a running CL.
5:04:17
aeth
Technically, other editors and IDEs can write their own SLIME-like thing using swank, but nearly everyone programs CL in Emacs+SLIME.
5:05:35
tko
so the temporary standard object would be like an object that contains all the values of your structure (or less) and that is organized in a way thats easier to read or interpret?
5:06:30
aeth
It would combine all of the pieces of data that represent an entity into one object. Which would be 1/2500 of the data structure, if it's filled to the arbitrary constant cap I set for it.
5:09:26
aeth
I think Bike's alternative approach would be to define methods that swank uses that are ultimately used by the SLIME inspector, i.e. create new things that swank will dispatch on as various relevant defmethods
5:09:40
nyef`
aeth: Or you could "just" declare for SBCL and unportability and implement an INSPECTED-PARTS method.
5:10:54
Bike
i mean, with the whole novel clos thing you're still relying on the inspector working in some particular way with slots
5:11:21
aeth
Would that work, though? I want to inspect one particular ID stored in a large data structure. A simpler example would be e.g. inspecting one node in a tree by providing the tree and what to look up into the tree.
5:13:11
aeth
But since it's only used for debugging it doesn't need to be efficient. I could just allocate an object from a query and then inspect that. Although then if I'm inspecting it live I'd have to do some trick to avoid filling the heap.
5:15:08
aeth
Bike: Hmmm... Or... I could be really lazy and make an object that encapsulates two objects...
5:15:46
Bike
so that you can take advantage of all the standard customization facilities that arne't real, makes sense to me
5:16:24
aeth
Bike: however non-standard things are, my own solution would be even more non-standard and even less supported
5:17:16
Bike
well, my main advice is to try something out rather than write a dissertation over the course of two hours, i'd do well to remember that
5:24:10
dmiles
aeth: well i am glad that I was correct in imagining that you were copying a reference setf-able place
5:29:36
aeth
dmiles: Well, I'm making local bindings to arrays (which does not copy the arrays themselves), and then using symbol-macrolet over accessors that are too complicated for with-accessors (such as aref or my custom array-row accessors)
5:31:40
dmiles
you know for these synthesized objects you are thinking about there is a system for synchronising them.. https://bitbucket.org/tarballs_are_good/cl-locatives
5:32:37
dmiles
you point the object at your proxy and your real thing.. when your proxy changes.. so does the real thing
5:33:03
earl-ducaine
Out of fairness to other CLs and the libraries in the Quicklisp repo, the problem that I described as 'dependancy hell' (for ccl) was only that I was using the wrong CLX library. Once I installed a compatible one using ASDF everything else worked.
5:34:28
aeth
with accessors is the thing that can turn (foo some-object) into foo, so you can (setf foo 42) and it's really (setf (foo some-object) 42)
5:36:10
dmiles
hrrm the package i was thinking of was actually was more in depth.. pjb suggested it to me the other week)
5:36:47
aeth
I took the recommended way of implementing with-accessors and modified it to work on (aref a 42) (aref a 42 0) and my custom array row accessors
6:10:05
aeth
dmiles: proxy with-accessors? as in an object that can call with-accessors (or, actually, with-entity-accessors in this case) on itself every second or something, to keep a somewhat up to date picture of what's going on? Could become a threading mess, though.
14:23:04
ebzzry
I have `sbcl --load quicklisp.lisp --eval '(progn (quicklisp-quickstart:install) (let ((ql-util::*do-not-prompt* t)) (ql:add-to-init-file) (sb-ext:quit))'` but it doesn’t wokr.
14:32:04
scymtym
ebzzry: try sbcl --load quicklisp.lisp --eval '(quicklisp-quickstart:install)' --eval '(let …)' --quit