admich has quit [Quit: ERC (IRC client for Emacs 26.2)]
gareppa has joined #lisp
admich has joined #lisp
gareppa has quit [Remote host closed the connection]
abarbosa has quit [Remote host closed the connection]
FreeBirdLjj has joined #lisp
moei has joined #lisp
wxie has quit [Quit: wxie]
gareppa has joined #lisp
jcowan has joined #lisp
heisig has quit [Quit: Leaving]
pierpal has joined #lisp
LiamH has joined #lisp
jprajzne has quit [Quit: Leaving.]
jmercouris has joined #lisp
ym555 has quit [Ping timeout: 246 seconds]
anewuser has quit [Quit: anewuser]
ym555 has joined #lisp
sjl_ has joined #lisp
scymtym has joined #lisp
ym555 has quit [Ping timeout: 258 seconds]
abarbosa has joined #lisp
<abarbosa>
up to create a pug-like html templates generator with lisp syntax...
<abarbosa>
maybe i'll prototype it first as Elisp... then CL hehe
scymtym has quit [Ping timeout: 264 seconds]
<abarbosa>
and late JS :D
<jmercouris>
This may be more of a database question than a CL question. I am using Mito and when I am writing to a part of the database that a user tries to read, it aborts the write
<jmercouris>
How should I go about handling this? Should I deny the request to the user trying to read the data?
iovec has quit [Quit: Connection closed for inactivity]
<Xach>
What is Mito?
scymtym has joined #lisp
keep_learning_M has quit [Quit: This computer has gone to sleep]
<beach>
abarbosa: What would be the advantage of prototyping in Emacs Lisp before writing it in Common Lisp?
<abarbosa>
beach: emacs fanboysm
<dim>
Xach: mito is an ORM for CL
<dim>
jmercouris: the #1 reason to use a database is concurrent access to the data, including read/write of course, so I think we need more information on what's happening here...
<dim>
my advice is to not use it because it has the same problem as most ORM have, it does not understand what a Relation is in the relational model and its SQL implementation
<dim>
examples of projects that know what a relation is: JOOQ, POMM
decent-username has quit [Read error: Connection reset by peer]
DGASAU has quit [Ping timeout: 258 seconds]
schjetne has quit [Ping timeout: 268 seconds]
pierpal has quit [Ping timeout: 258 seconds]
longshi has joined #lisp
moei has quit [Ping timeout: 248 seconds]
rippa has joined #lisp
rdh has joined #lisp
schweers has quit [Ping timeout: 252 seconds]
ym555 has joined #lisp
ikki has quit [Ping timeout: 246 seconds]
<pjb>
abarbosa: emacs lisp is too rudimentary to be a good prototype language, when compared to Common Lisp. (of course, if you write in any other programming language (but perhaps prolog and haskell), emacs lisp is a perfectly find prototype language).
<pjb>
abarbosa: the only reason to write emacs lisp code, when you have CL at your fingertip, is to implement the user interface in emacs, instead of say, CLIM.
<pjb>
There are very good reasons to implement a UI in emacs. see for example slime.
cosimone has joined #lisp
gareppa has quit [Quit: Leaving]
<pjb>
dim: are JOOQ or POMM usable in CL?
ym555 has quit [Quit: leaving...]
tko has quit [Ping timeout: 268 seconds]
tko has joined #lisp
hhdave has quit [Quit: hhdave]
admich has quit [Ping timeout: 244 seconds]
FreeBirdLjj has quit [Remote host closed the connection]
longshi has quit [Ping timeout: 252 seconds]
ggole has quit [Quit: Leaving]
ricekrispie2 has joined #lisp
ricekrispie has quit [Ping timeout: 248 seconds]
iovec has joined #lisp
pankajgodbole has quit [Ping timeout: 272 seconds]
meepdeew has joined #lisp
rdh has quit [Ping timeout: 244 seconds]
longshi has joined #lisp
CEnnis91 has joined #lisp
Lycurgus has quit [Quit: Exeunt]
<abarbosa>
pjb: that is exactly what I am after, Emacs editing features are powerful what favors doing such a tool in/for it. Like emmet-mode..
tko has quit [Ping timeout: 248 seconds]
aindilis has quit [Remote host closed the connection]
flip214 has quit [Read error: Connection reset by peer]
flip214 has joined #lisp
<phoe>
pjb: don't think so. JOOQ is Java, POMM is PHP.
anamorphic has joined #lisp
meepdeew has quit [Remote host closed the connection]
<anamorphic>
If create a FFI for a library has function that take strings for filenames, what could possibly go wrong if I created a CFFI deftype that mapped those filenames to pathnames, and from path names to filenames via translate-logical-pathname/namestring?
<dim>
pjb: JOOQ is Java, so I guess yes, with ABCL, and POMM is PHP, so I guess no. The scarcity of ORM like projects that understand that a SQL query actually defines a relation is such that I only know of those.
<pjb>
ok. Thanks.
<pjb>
anamorphic: pathnames are not POSIX paths and vice-versa.
<pjb>
anamorphic: translations go from logical-pathnames to physical-pathnames (and then from physical-pathnames to paths), but not the other way.
<dim>
it took me lots of time to understand why people would think that the Object-Relational Mapping is very hard (cf. the Vietnam of Computing Science article); they're trying to solve the wrong problem
<pjb>
anamorphic: you can get from path to physical-pathnames, but it's not guaranteed that 100% of the paths will have a physical-pathname representation.
tko has joined #lisp
<pjb>
anamorphic: and you cannot go from physical-pathnames to logical-pathnames. You could write a function to try to do it, that could work in a few cases. It would be very brittle.
<dim>
if you map the result of a SQL query (that is, a relation, in other words, a collection of instances sharing the same properties: a list of slots of known type, or domain attribute in the relational jargon) to, well, a collection of instances, it's VERY EASY to implement an ORM
<pjb>
But also quite dynamic. And this doesn't help much for update and insertion, when most of thse querys are joints.
<pjb>
But indeed, a better understanding of SQL can only help.
<dim>
if you try to map base tables (that is, considering only the TABLE query rather than all queries with a projection operator, such as SELECT and RETURNING), then it's very hard to implement ORM because what are you going to map the query result to, when you have joins or other operators and the only object definitions you know about are mapped to the base tables...
<dim>
anyway
<pjb>
Also, on the OO side, people concentrate a lot on classes, and much less on relationships between classes.
<pjb>
With the added difficulty that with the various multiplicities, there are different ways to implement them.
<phoe>
dim: in SQL it is trivial to return a table with a different columnset at runtime; in most OO languages, it is troublesome to define new classes at runtime to accomodate the new columnsets
<dim>
pjb: with foo as (delete from t1 where ... returning t1.id), stats as (update history set delete_count = delete_count + (select count(*) from foo)), archive as (insert into t1_history select row_to_json(foo)) select count(*) as deleted from foo;
<dim>
phoe: in theory, we could discuss
<phoe>
and languages where it is easy are shit due to other things (such as complete lack of classes in JS)
<dim>
in practice, I don't see the point, SQL queries are static in the code anyway
meepdeew has joined #lisp
tko has quit [Client Quit]
<dim>
or at the very least, the projection clause of SQL queries in the code are static
<phoe>
dim: I see, you are right
tko has joined #lisp
<phoe>
but then the ORM would need to analyze what exactly is returned and automatically create classes based on that
<dim>
the mapping needs to be done from the projection operator (either select or returning) to a known class in the code, that's static enough
<phoe>
or so it seems to me
<pjb>
dim: I thought that the SQL queries were provided by the user at run-time? :-)
<dim>
phoe: why do you want to make things complex? like the developer doesn't know which kind of object collection he's expecting as the result of sending a SQL query to the database server?!
<dim>
who's writing the SQL query?
<pjb>
Who's writing the COBOL program?
<pjb>
Who's writing the CUCUMBER test?
<pjb>
:-)
<phoe>
dim: I'm thinking from the point of view of an ORM user/writer while also trying to apply what you mentioned up there, about mapping results of SQL queries to a collection of instances
<dim>
so as long as the SQL queries are written by the developers, we don't need any fancy inspection tooling, just associate every query with a specific instance type taht you're going to have a collection of as a result; done
<phoe>
yes, I understand now
<dim>
phoe: here's the trick: don't write an ORM, that's lost time for EVERYBODY involved
<phoe>
(;
<dim>
ORMs have 3 features: 1. managing CRUD for the user, 2. automatic hydratation of the collections of objects so that the poor developer doesn't have to manually loop over the result set and 3. hide SQL behind a worse query language that pretends to look like “normal programming”
<pjb>
That's my conclusion too. Notably, because in OOP, objects encapsulate methods (even in CLOS), but you don't store the code in the SQL databases.
<dim>
ok, so 1. doesn't bring much on the table, you can make without it
goktug_ has joined #lisp
<pjb>
Therefore it's better to have SAVE and LOAD methods to let the objects store themselves, and to have QUERY functions to implement specific SQL queries.
<dim>
2. is easy to implement as an HOF in CL with two arguments, the SQL query and the instance type of the collection you want to build as a result
<dim>
3. just learn SQL and make is so that you have .sql files in your source code, have a look at cl-yesql
<dim>
done.
meepdeew has quit [Remote host closed the connection]
<dim>
CRUD is its own can of worms: the value of a database is not storage, it's concurrent access, and databases are very good at batching operations too; so save and load operators... I frown upon those really
<dim>
CRUD as in admin UI where you operate on a single object at a time, why not, it's very easy to automate. do you actually need that anywhere in your project, for real? you have one form per base table, no joins, no reference/catalog lookups, no computed values, no nothing?
<dim>
anyway, I got started on one of my favorite rants again
<pjb>
dim: granted, but there are two difficulties. The first is that when you use a database, you want to put stored procedures there, and the applications become only user interface (so no need of "fat" objects or objects at all). And the second, which is the same but worse, is the case of mobile applications, where the data is in the backend, and only a local copy is saved (in a sqlite database) for off-line work.
<dim>
just don't use mito or any other ORM, think about what you're trying to solve, refrain from generalizing before you have 3 times the same code copy/pasted, and learn yourself some SQL
<pjb>
So it's just a big mess.
<dim>
hint that you need SQL training: do you know what is and when to use window functions, lateral joins, grouping sets, and correlated sub-queries? if you say no to any one of those, please learn SQL again
<dim>
pjb: it's always a big mess, there's no one-size-fit-all answer, all I'm saying is that in practice I've never seen a case where the ORM was a good answer at any problem thrown at it
<pjb>
Sure. But the more SQL you learn, the more SQL you use to implement your app. Then you don't write CL code or swift or kotlin code anymore. Just UI.
<dim>
yeah of course, and what's the problem?
<dim>
a database solves CONCURRENT ACCESS to a SINGLE SET of data
<dim>
either you need concurrent access, or you don't
<dim>
maybe you don't and you can program everything in CL and be happy
<White_Flame>
well, a database also solves complex querying against filesystem storage, as well as manages rollback-able transactions
<dim>
or maybe you do and you know how to implement concurrency in a correct and fast way in CL yourself, and then do that
<White_Flame>
so even without concurrent access, it still can be very useful
<dim>
White_Flame: transactions/rollback, that's about concurrent access, nothing else
<White_Flame>
no, it's about undoing partially completed work, even without concurrency
<dim>
powerful query language, that's what pjb said, you want to do as much as possible in the database to avoid having to deal with concurrency tricks in the application itself
<White_Flame>
lets you simply "do the work" without worrying about keeping things around for cleanup
meepdeew has joined #lisp
<dim>
you can implement a Write-Ahead Log and ROLLBACK; facility easily in CL, that's not a problem for which you would install a separate service and learn how to manage it in production
<dim>
IIRC manardb does that and more
<Xach>
i added wal/rollback/transactions for a lisp db a few years ago and it was fun & interesting. but it is good to leverage hard work of others in this domain if possible.
<dim>
+1
<dim>
what I'm trying to share is not a universal truth, it's a way to look at things, a way that makes everything simpler and gives sense/meaning to production architecture and where to write code / in which language
<dim>
the goal is to make people think about that piece of software they are using in production and don't really want to know how it works, devs saying it's ops, ops saying it's not their domain and recruiting a dba, etc; that just feels too stupid
meepdeew has quit [Ping timeout: 245 seconds]
<Xach>
i think there's a broader issue of wanting to get something done without learning how something works, anger when something doesn't work according to uninformed intuition, limited ability to deeply understand many tools at once
<pjb>
And yet, if you put a lot of application logic in the database, you need to surdimension the server to handle the millions of users, instead of distributing the processing in their devices.
_leb has joined #lisp
<dim>
pjb: as always, you need to be smart about it
<dim>
Xach: yeah I've seen that so many times, where the devs have the impression that the database needs to be there whether you want it or not, so they don't question the necessity to have one, and then they don't OWN the database being part of the architecture, and then they develop without it as much as possible
<dim>
what a waste.
goktug_ has left #lisp ["ERC (IRC client for Emacs 26.2)"]
kajo has quit [Ping timeout: 244 seconds]
<Xach>
i love postgres but i have not had a ton of opportunity to learn all the new wonderful things - it's a mix of needing to do something quickly and needing to learn the best way to do it (which takes time)
<dim>
indeed it's complex, that's why I wrote a book about it ;-)
<Xach>
but some people hate postgres (or java, or common lisp) because they had to do something quickly and it did not act as they felt it should without any experience or informed intuition
meepdeew has joined #lisp
<Xach>
less so with common lisp :)
tko has quit [Read error: Connection reset by peer]
<dim>
for me what's important is to understand what Postgres is good at, what kind of things you can implement in SQL, and then you can have a look at the docs about how to solve specific problems when you have them
<dim>
I've seen MySQL users implement joins as nested loops in their application code because they didn't know you could do it in SQL; there's a world of difference between implementing a join in the app and passing on the grouping sets or the awesome width_bucket() function in Postgres
<dim>
it's all about the attitude that allows to have a look at the docs (and blogs) when you're like “I think it should belong to the SQL query, but I don't know how to make it happen”
<Xach>
I have an org file revelations.org where I keep notes about how to solve problems in new ways because I find it harder to retain that excitement and revelation across many tools.
<Xach>
"i know i found a better way once, what was it again?"
<dim>
building an informed intuition in CL took me ~5 years and I'm not sure I'm there yet ;-)
<dim>
hehe revelation.org that's nice
<dim>
I have fun.lisp myself, things I discovered and was like “wow that's fun, I wish I had a use for it”
<dim>
loop for/and fibonacci in is there, my favorite implementation of fibonacci of all times
<dim>
(loop repeat 10 for a = 1 then b and b = 1 then (+ a b) collect a)
sauvin has quit [Read error: Connection reset by peer]
meepdeew has quit [Ping timeout: 248 seconds]
abarbosa has quit [Remote host closed the connection]
abarbosa has joined #lisp
meepdeew has joined #lisp
saravia has joined #lisp
gxt has joined #lisp
meepdeew has quit [Ping timeout: 272 seconds]
abarbosa has quit [Remote host closed the connection]
saravia has quit [Quit: Leaving]
<sjl_>
or (loop with a = 1 and b = 1 repeat 10 collect (shiftf a b (+ a b)))
saravia has joined #lisp
* sjl_
never has good opportunities for shiftf
<pjb>
sjl_: another good opportunity is when you implement a CL REPL.
<pjb>
randyjohnson86: as good as any other. (you may also try news:comp.lang.lisp)
<dim>
that will tell you a lot
<pjb>
code review should not concentrate on "coding style", since that can be handled automatically by pretty-printers, emacs paredit, etc.
<dim>
there's style and style, I don't think the paper I've referenced deals with any of the formating issues
gareppa has quit [Remote host closed the connection]
<dim>
pjb: if you haven't already I think you would like reading it
<aeth>
dim: That one's a bit dated now. There's also ITA's old style guide that became the Google CL Style Guide. It's kept up to date, but some of them seem specific to that particular codebase. https://google.github.io/styleguide/lispguide.xml
longshi has joined #lisp
<pjb>
I browsed it once. Probably I should read it, but I won't agree with much of it.
<dim>
you're free not to, at least it's good food for though
<dim>
I've found it easy to agree with it, because I read it before having written any CL code or almost, but then I think I failed to follow it in pgloader, which I'm quite sad about
<aeth>
It's absolutely not general style advice, though!
<dim>
anyway, gn!
<randyjohnson86>
aeth: I am fairly new to common lisp so some of the items in your CONTRIBUTING.md have confused me; for example, the note about lambdas
<aeth>
randyjohnson86: #'(lambda (x) (+ 2 x)) and (lambda (x) (+ 2 x)) are both valid ways to write lambdas in modern implementations.
asdf_asdf_asdf has joined #lisp
<aeth>
I actually probably should update my CONTRIBUTING.md to use the limits that magit enforces since magit enforces that stuff now so I might be breaking my own commit message rules all of the time.
<randyjohnson86>
okay, I seem to understand why you would have it that way
<randyjohnson86>
Much more informative, thank you; I think I was associating the #' with lambda as I had normally been using them together with a function like mapcar
<aeth>
I still sometimes get the #' with my lambdas when I refactor a map or mapcar from using a function to using a lambda
<aeth>
since the function will be #'foo
<aeth>
but to be consistent in style, I try to eliminate those
<randyjohnson86>
should I just link pastebin links for a snippet of code?
<aeth>
Hmm... does CL have one of those tools that you run and it makes a graph of your class inheritance hierarchy? I guess it would be a bit trickier in CL because there's defclass, defstruct, and define-condition, and it must be determined from a running CL image because those could be used indirectly via macros.
<pjb>
randyjohnson86: yes, pastebin links are good.
<aeth>
randyjohnson86: Both Github and Gitlab have their own code pastebins, which will probably do a better job than the regular pastebins if you have an account at either.
<pjb>
randyjohnson86: l is not a good name. It looks like 1, and it's uninformative and non-descriptive.
LiamH has quit [Quit: Leaving.]
<aeth>
randyjohnson86: (1) l is sometimes used in Scheme because it's a Lisp-1 and using "list" would conflict with the procedure "list". In Common Lisp, we take advantage of Lisp-2 to always call it "list" unless there are more than one or it has a more specific role.
<pjb>
randyjohnson86: If you called it map-square-list, you could also write it: (mapcar (function square) list) with (defun square (x) (* x x))
<pjb>
randyjohnson86: is it worth (defun map-square-list (list-of-numbers) (mapcar (function square) list-of-number)) ?
leb has joined #lisp
<pjb>
I would say no, but it might be if you need to use this square list function a lot.
<aeth>
randyjohnson86: (2) You should use DESTRUCTURING-BIND if you have lots of repeated calls to CAR/CDR/FIRST/REST/etc. imo unless you are setting (since destructuring-bind will just bind)
<randyjohnson86>
this little snippet is an exercise in recursion in the book I am following; I don't think I have seen destructing-bind yet, although I have heard about it
<pjb>
For example, if you have a list of lists to be squared: (mapcar (function map-square-list) '((1 2 3) (100 200 300) (1/1 1/2 1/3))) #| --> ((1 4 9) (10000 40000 90000) (1 1/4 1/9)) |#
<pjb>
randyjohnson86: as recursion, it's good: it has a base case, and it has a recursive case.
<pjb>
randyjohnson86: but in general it's bad: append makes it O(n^2) instead of O(n).
<aeth>
randyjohnson86: I'd personally replace your cond with this: (destructuring-bind (first &rest rest) list (if rest ... ...))
<aeth>
randyjohnson86: When I use Scheme, that's the #1 thing I miss for recursive functions like this.
<aeth>
You could also do (if (endp rest) ... ...) if you want to keep the flow in the order that it is currently in
<pjb>
randyjohnson86: and in the little detail, it's often better to use the most specific function to do something (this conveys some information both to the compiler and the reader by way of type constraints, error messages, etc). So for example instead of (equal (length l) 1) (which is O(n) and too general, you could write (= 1 (length list)) and better (endp (rest list)) which is O(1).
<pjb>
randyjohnson86: I would add a base case for when the list is empty.
<pjb>
Otherwise you will get a type error on (* nil nil).
<pjb>
Define and use this square function… You can (declaim (inline square)) if you worry about performance.
<pjb>
randyjohnson86: note the use of first rest endp instead of car cdr null, since you're processing proper lists.
<pjb>
randyjohnson86: now, about append, it will be better to use: (list* (square (first list)) (map-square-list (rest list))) (or cons instead of list* in this case, since your prepending only one element). This will avoid copying the 1-element list which append does.
<randyjohnson86>
is there a better way to reference the functions that you folks are talking about? or should I just peruse the hyperspec?
<aeth>
I'd structure it like this: (if (endp list) nil (destructuring-bind (first &rest rest) list (if (endp rest) #| then |# #| else |#)))
<sjl_>
hyperspec is good
<pjb>
(so contrarily what I said above, it's not append that makes it O(n^2), since it copies always a 1-element list, it makes it O(2n), it was the call to (length list) that made it O(n^2).
<sjl_>
aeth: why the extra if there?
<aeth>
In order to build your list properly, you need to create two functions, though. One is your public function, and the other is your recursion helper. You can put the latter in a labels.
<pjb>
the advantage of square-list-acc is that the recursive call is terminal, so it is at least _possible_ that a CL implementation may optimize the terminal call. But it's not mandatory as per the CL standard, so in any case, a recursive function may hit a stack full error long before you reach the end of the list…
<aeth>
looks like pjb beat me to essentially the same thing while I was writing tests, though
sjl_ has quit [Ping timeout: 272 seconds]
<pjb>
aeth: that's the advantage of writing correct code, you don't have to write tests :-)
<aeth>
I chose to make it a higher order function and just embrace the rewriting-mapcar nature of it
<aeth>
and then just used that as a helper
<aeth>
the recursion is still there
Lord_of_Life has quit [Ping timeout: 245 seconds]
Lord_of_Life_ is now known as Lord_of_Life
<aeth>
sz0: and the reason the extra if is there is because it goes away in the next version, that makes it tail recursive
<randyjohnson86>
okay, these are a lot of things to juggle with, so both of your implementations are resolving the problem of ordinary recursion by using tail-recursion
<aeth>
oops sjl left so I tab completed the wrong person
<pjb>
randyjohnson86: but actually, aeth is the best solution: if your teacher or book forbids you to use mapcar, because you've not learned about it yet, then implement it (in recursive mode, since the exercise is to use recursion), and then use your own mapcar to implement the solution. Then you will have written square-list as it should, and both implemented a recursive function.
<aeth>
I really need to verify tab completions better
<aeth>
it works like 95% of the time so I trust it and I shouldn't. sorry.
<sjl>
aeth: I didn't leave, just logged off the other machine
<pjb>
In other terms, you will have drawn circles around your teacher or book :-)
<aeth>
sjl: ah
<aeth>
sjl: well you left to my IRC client :-p
<sjl>
I really need to set up a damn irc bouncer
<pjb>
randyjohnson86: yes, with labels or with a secondary accumulating function.
<randyjohnson86>
okay, I think I'm just getting lost in the lispiness of it all, but I seem to understand what both of you are aiming towards
<randyjohnson86>
endp seems to be a godsend, had no idea that existed
<randyjohnson86>
(endp (rest list)) was absolutely clever, loved that bit
elwood has joined #lisp
<randyjohnson86>
pjb: do you know the O notation value of these functions offhand or does lisp provide a way to explain the technical debt of your programs?
<aeth>
randyjohnson86: there were three versions, weren't there? pjb's sjl's and mine
<sjl>
Mine was just a cleaned up version of the same non-tail-recursive algorithm. pjb converted it to a tail recursive version. and aeth implemented mapcar and then used that to solve the problem trivially.
<randyjohnson86>
this is truly a treasure trove of information in here
<sjl>
There's no way to automatically determine the O(...) values. e.g. something like (defun foo (list) (if (zerop (random 1)) (length list) 'nope)) is O(n) half the time and O(1) the other.
<sjl>
I'm glad it was helpful.
<sjl>
(to be fair, I guess you could call that just O(n) since big O is an upper bound)
specbot has quit [Remote host closed the connection]
<aeth>
sjl: It would be O(n). Worst case.
<sjl>
Yeah. But that's not super useful.
minion has joined #lisp
specbot has joined #lisp
<sjl>
Or something like (defun foo () (eval (read))) could do anything.
<aeth>
sjl: I think you mean (random 2) btw
<sjl>
Ah, yes.
<sjl>
I have my own (randomp) function I use for stuff like that.
<alandipert>
i enjoy reduce for closed form iteration stuff
<aeth>
sjl: My favorite thing about eval is the only non-connection-related time RETRY is useful in SLIME's debugger. (let ((x `(progn (format t "Hello, world!~%") (zerop (random 2 (make-random-state t)))))) (check-type x (satisfies eval)) x)
<alandipert>
i could be wrong but i want to say reduce is the easiest way to make something work for any sequence. e.g. squared-list there can take a vector
<sjl>
(map 'list #'square list-or-vector) would also work
<sjl>
the reduce version won't automatically do that
<sjl>
alandipert: if you define a square function, it can be even prettier: (reduce #'cons list :key #'square :from-end t)
<alandipert>
nice, :key ftw
<sjl>
or use :key (lambda (x) (* x x)) I guess
<sjl>
yeah CL's reduce is pleasantly versatile
<sjl>
I wonder how various impls implement :from-end t on a list though
<aeth>
You can also use LOOP, DO, and DOLIST, of course.
<aeth>
LOOP's just a standard :collect, but DO will look surprisingly like the recursive version
<alandipert>
can't imagine it's worse than manually reversing, and sometimes maybe better?
<randyjohnson86>
aeth: could you walk me through your version? I cannot seem to wrap my head around it
ricekrispie2 has joined #lisp
<aeth>
randyjohnson86: Since the problem, as I interpreted it, was to do this recursively, I first implemented a simplified recursive form of a CL built-in (mapcar) to help with readability for people who are used to the language. Personally, I try to make my functions and macros look like built-ins if I can.
<aeth>
randyjohnson86: The only real difference that makes is that the function uses (funcall function first) instead of (* first first) or (expt first 2)
ricekrispie has quit [Ping timeout: 245 seconds]
<aeth>
randyjohnson86: A tail recursive function usually consists of two functions. The public API and the internal API. This is because the actual recursive function usually has extra information that it needs to pass with each call. You can have a separate DEFUN at the top level, or you can use LABELS (you can't use FLET because that doesn't allow self-recursive functions iirc)
<aeth>
randyjohnson86: If you want to keep it tail recursive, the tail call has to be the function itself, which usually means for list functions you have an old-list that gets smaller and a new-list that gets larger. You could also e.g. have a counter that increments.
<aeth>
I chose to use destructuring-bind to bind (car old-list) or (first old-list) to first and (cdr old-list) or (rest old-list) to rest instead of using either function pair. It's a useful macro for working with lists imo. You can get rid of it and just substitute out those variables in your head, though
<aeth>
randyjohnson86: And the reverse at the end is because when you build lists in a pure tail recursive way you build them in reverse since e.g. your pair of lists are '(1 2 3) nil then '(2 3) '(1) then '(3) '(4 1) then nil '(9 4 1)
<aeth>
Because cons is adding to the start of new-list
<aeth>
Technically, nreverse might be more efficient here and won't have any issues.
<aeth>
nreverse might modify the input list, but it's a fresh list, anyway.
<aeth>
I called the helper function simple-mapcar* because it's sort of like a slight variation of simple-mapcar and naming things is hard. I could have called it %simple-mapcar because it's internal. If I had it as a top-level function instead of in labels, I almost certainly would use the name %simple-mapcar so people don't accidentally use it
wxie has joined #lisp
<randyjohnson86>
I think I'll have to save this for later in my lisp knowledge, lots of unfamiliar keywords to understand
<randyjohnson86>
I understand the general concept of what you're talking about, but unable to make the direct connections as to why and how it played out
<aeth>
This sort of code is very advanced. You might want to look at some Scheme references since it's far more common in that language.
<aeth>
I never took a full course in Scheme, but I would assume that this is the sort of thing an undergraduate university Scheme course would want you to write at the end of the semester, except in Scheme instead of CL, obviously. So it's on par with stuff like red-black trees.
vhost- has quit [Remote host closed the connection]
vhost- has joined #lisp
<aeth>
hmm, actually, I think a Scheme course would probably cover harder recursion.
xantoz has quit [Ping timeout: 252 seconds]
notzmv has quit [Ping timeout: 248 seconds]
lavaflow has joined #lisp
MetaYan has quit [Ping timeout: 246 seconds]
<randyjohnson86>
aeth: thank you for telling me about lisp-1 versus lisp-2, had no idea that was a thing
<randyjohnson86>
I know languages like python throw a fit when you use 'protected' keywords as function arguments
<randyjohnson86>
definitely more readable than just 'l' and as others have said, hard to differentiate between '1' and 'l'
<aeth>
randyjohnson86: In case you don't know, lisp-1 means shared namespace for functions and variables and lisp-2 means two separate namespaces there. There can be other namespaces. e.g. list can be a variable, is a function, and is also type.
<aeth>
You should really take advantage of a language's traits when using it, so in CL that means functions and types and variables should share the same name a lot of the time. e.g. (let ((list (list 1 2 3))) (check-type list list) list)
<aeth>
Of course trivial examples will often be nonsensical, but it can actually be useful in real code.
<aeth>
If you wanted to copy the standard's way of doing things, you could e.g. have two constructor functions, just like there's LIST and MAKE-LIST. Then you could e.g. (some-foo-action (foo 1 2 3)) where some-foo-action is (defun some-foo-action (foo) (check-type foo foo) ...)
<aeth>
A type is often a class, so you might also be able to implement that as (defmethod some-action ((foo foo)) ...)
<randyjohnson86>
very interesting to think about, I recall a chart where it showed a symbol being like 5 possible things at the same time
<aeth>
(Afaik, a type is basically a superset of classes, allowing you to also do things like (integer 0 10) or (or list vector))
notzmv has joined #lisp
<Bike>
all classes are types and there are more types than classes
<Bike>
(or list vector) is probably just sequence, tho
<aeth>
Bike: well, that excludes extensible or implementation-specific sequences, for implementations that have them
<Bike>
"probably"
<aeth>
I almost said that, but that's sort of like a footnote on a footnote
<aeth>
randyjohnson86: It's very hard to tell exactly how many namespaces there are. function/macro, variable, and type/class are the easy ones. I think conditions are their own namespace. Then iirc there are obscure ones like tags for TAGBODY gotos
<aeth>
And technically speaking, even if you count all of the standard and perhaps implementation-extended namespaces, you can always just create your own new ones using e.g. hash tables within a macro
<aeth>
Definitely more than 5, though
<Bike>
condition types are still types.
<aeth>
ah, okay
<aeth>
is it restarts that are their own namespace, then?
<aeth>
"It is a good idea to limit each symbol to only one name space. Common Lisp will not be confused if a symbol is used in multiple ways, but the poor human reader probably will be." exactly the opposite advice as the one I gave earlier
<aeth>
although, to be fair, that advice is for all namespaces and my advice was for 3
<pjb>
Natural languages have a lot of words belonging in multiple namespaces, and nobody is confused about them. Human brains can deal with those situations.
<grewal>
Especially when you use decriptive names
<aeth>
pjb: Treating programming languages like natural languages is how you get Perl
<pjb>
Smith the smith smith the spear.2
<grewal>
And his example is a degenerate case
<pjb>
It seems logical for a smith to be named Smith, and to smith things. Otherwise he wouldn't be a smith.
<pjb>
Indeed. Here it might be a little too much, at least for non native English speaker. But I hear that it's a perfectly gramatical and understandable sentence (at least, if you don't miss the capitalization).
<pjb>
This Smith had a brother, also named Smith, since from the same father, and who was but a lock-smith. Not a real smith. Nonetheless that Smith also helped his brother to smith big and smal irons on occasions.
<no-defun-allowed>
But natural languages take years to learn, and programming languages have specific uses unlike natural language. Is all that effort justifiable for most people?
<grewal>
All what effort?
<pjb>
What?
<no-defun-allowed>
To learn a language with all those namespaces.