libera/commonlisp - IRC Chatlog
Search
17:30:06
mzan
neominimum: I'm a newbie too. In CL recursive functions are not used as much as in Haskell. It favours explicit loop/iterate.
17:30:46
mzan
The CL standard does not guarantee tail call optimizations, so there can be problems for some compilers, if you recurse on a big list.
17:31:38
beach
More precisely, iteration is preferable on linear structures, because it is more efficient, and with recursion on linear structures, there is a risk to blow the stack.
17:31:42
mzan
I'm using the "iterate" macro, and I like it a lot. https://common-lisp.net/~loliveira/tmp/iterate-manual/iterate.html#Control-Flow
17:32:20
beach
Recursion is fine on tree-like structures when the depth is limited to (say) logarithmic.
17:33:20
beach
mzan: I would not recommend an external library over a standard operator to a newbie. It is fine if that's your choice, but I would be more careful with recommending it to others.
17:35:39
mzan
neominimum: you are using recursion. Use Scheme because it supports tail call recursion directly in the standard :-)
17:46:45
mzan
beach: in any case I disagree. LOOP is a macro, so it must be studied apart. "Iterate" is a very similar macro, but better. "Iterate" is used a lot, so it is not standard, but for sure not esoteric. It is nearly standard. In case he had to read legacy code, if he knows "iterate" macro, he can understood LOOP macro.
17:51:48
_death
some thoughts I had about iterate a while ago https://old.reddit.com/r/adventofcode/comments/e92jm2/2019_day_11_solutions/faht58g/?context=3 ... since then the breaking change was committed btw
17:56:59
mzan
_death: ah ah, funny enough I discovered that if I try to optimize the code with "iterate", SBCL complains because variables like "x1", "x2" in your example can be "nil" and not only "fixnum" or something of similar. But probably there are sane workarounds, and it is also my fault in specifying typing constraints.
17:58:35
_death
according to its source code iterate existed since 1989, and perhaps made into a library in 2003.. so when a breaking change is introduced in 2021 I think it's a good argument for LOOP
17:59:46
mzan
And to be fair, if you "iterate" on an empty data-structure, then the returned "x1" can be nil, and the code must take in account this. So SBCL is right.
18:07:30
_death
probably beach's point had a different rationale, btw.. a CL newbie should learn to read and write CL proper well before getting too comfortable with (reliant on) external libraries purporting to replace or improve upon some CL counterpart
18:08:10
mzan
_death: I read better now. Yes. Probably in a perfect world, one should change the version of "iterate" and other macro, every time there is a breaking change. So one can import something like "iterate_v2", and this will not break code using "iterate_v1".
18:09:11
_death
in a perfect world one would not need to create new versions, since everything would be perfect already ;)
18:09:18
mzan
LOOP for sure is more stable, because it is part of the standard. And then there are also "mapcar" & C. that are more standards and more direct.
18:09:53
mzan
Probably a newbie should first exsercise with mapcar & C. Then study LOOP and/or "iterate".
18:11:57
Alfr
The problem with loop is, imho, that it has non-obvious rules regarding clause placement.
18:12:09
didi
Puzzle: How to print "X A\nX B\nX C\n" from (X A B C) using only format? i.e., without transforming (X A B C) into something more suitable. I think, if at all possible, it might involve ~? and ~*.
18:12:15
edgar-rft
the only reason why there is no edgar-v2 is because I'm already perfect? probably no...
18:14:03
mzan
_death: "probably beach's point had a different rationale, btw.. a CL newbie should learn to read and write CL proper". Yes. If one is serious, it should start from the bases, and then build up.
18:14:26
Alfr
Bike, also many things are considered harmful, and I guess manually writing tagbodies may actually be unhealthy.
18:14:30
mzan
BTW I studied a little of CL basic (but not too much), and then I started playing with macro and other powerful features, for discovering the funny/powerful parts of CL.
18:18:23
Bike
not because dijkstra decreed it, but rather because his reasons make sense. better to use higher level constructs in almost all cases.
18:18:59
ck_
doesn't PCL use it as an example for how awesome a direct translation of one of Knuths algorithms can be done using tagbody, as a first step for refacoring it
18:20:19
_death
to get better context for Dijkstra, imagine (tagbody <your whole program here, using go to jump around>)
18:20:26
Bike
the context of dijkstra's paper was that people were regularly using goto when they could have used better control constructs. nowadays that's not really a problem with tagbody
18:21:16
Bike
people rarely use tagbody, and when they do use it it's for some weird shit that's hard or impossible to express with higher level constructs. like in eclector off the top of my head
18:22:47
Bike
dijkstra was also writing when "structured programming" was new, instead of now where it's so basic people don't remember it had to be invented
18:29:16
yottabyte
I'm not too knowledgeable about namespaces, but when I quickload a library, how do I add it to my namespace so I don't have to call functions from it with the name? like instead of library:function just call function. I guess this can become a problem if the library contains a function that's already defined in your namespace? but yeah.
18:29:52
didi
Oh, well, I can't solve my format puzzle. I'll transform the argument. /me likes format "puzzles"
18:30:37
_death
Bike: aside from tagbody being useful for state machines and such, it may also be useful to expand to in macros..
18:33:20
_death
yottabyte: (defpackage :yottabyte (:use :cl :arrow-macros)) (in-package :yottabyte) (-> "Hello" write-line)
18:35:49
_death
in a real program, consider using (:import-from :arrow-macros :->) instead of :use, to explicitly import the symbols you care about
18:37:11
didi
phoe: Sorry, but what's the name of the library which has literal tabs inside strings again?
20:35:22
pjb
yottabyte: you must read the documentation of the system, to know what packages it defines, and from what package it exports the names of the functions you want to use.
20:36:00
pjb
yottabyte: note: systems are named by lower case strings such as "foo"; while packages are usually named by uppercase string like "FOO".
21:35:53
_death
Josh_2: I actually stopped pulling changes from iterate because some library that used it broke, iirc
0:30:11
neominimum
mzan: I'm a perpetual newbie even though I've been casually using CL for a few years, my style suck-- I mean, my style is unconventional due my solitary practice. I'm trying now to be more conventional, so it's been great to get some specific feedback. Thanks for your suggestions. I wonder what is the reason why TCO needs to be supported by the standard, I mistakenly thought it was an implementation detail. I think I get it though... If the
0:30:11
neominimum
standard doesn't require it then implementations may not implement it. Does that make recursive code non-portable? Although my understanding is the standard doesn't require compilers to be optimising, professional users would not consider using one that lacked that facility. Like wise, I am interested to know if there is anyone that would consider a compiler that lacks support for TCO to be "Not Sufficiently Smart (tm)". I'm genuinely
0:33:45
jasom
first you need to define "what is tail position" (defun foo () (let ((*bar* 3)) (baz)) ; <-- the call to baz here will likely *not* be tail optimized on any CL compiler due to the dynamic binding of *bar*
0:35:01
jasom
Then once you've done that, you need to ensure that the function is compiled, as most interpreters will not eliminate the stack frame for tail calls
0:36:02
jasom
Then once you've done that, you need to ensure that the "optimize" declaration is set sufficiently high (in either the absolute or relative sense; some compilers may use the relative values of optimize v. debug, for example)
0:38:01
jasom
The one place I like to rely on TCO is when implementing state-machines; in that case the non-tail call approach is less readable IMO and will involve passing either closures or a list of function and arguments, both of which are more unweildy than a tail-call
0:39:20
jasom
I should also point out that non-tail-recursive code is *definitely* non-portable; the maximum recursion depth may vary not just from implementation to implementation, but from machine to machine even when using the same implementation
0:46:10
neominimum
Ok, so it's definitely not feasible in some situations due to environmental factors and the use of some language features.
0:50:36
neominimum
I do like recursion, more because I find it challenging. To me it definitely looks better than some iterative implementations sometimes, but as they say "When in Rome..."
0:51:32
jasom
Oh, there are times when recursion is more readable than iteration, but, in my experience, few of those cases are tail-recursive
0:52:08
jasom
The most obvious example is any sort of tree walker; the recursive form is almost trivial, the iterative form involves an explicit stack.
0:54:26
jasom
The naïve fibonacci is the most readable way to do fibonacci; I think the iterative vs. tail-recursive are of similar readability.
0:56:36
EdLangley[m]
Clojure's LOOP/RECUR is an example of an abstraction that makes this a bit nicer.
1:03:34
EdLangley[m]
If you (psetf arg1 (step arg1) arg2 (step2 arg2)) (go start) you can "pass arguments"
1:04:40
jasom
EdLangley[m]: right, but that's far less clear and sm2n was asking "why not use tagbody"
1:05:19
neominimum
If I understand correctly now. In the iterative form, walking a non-linear data structure, requires the use of an explicit stack. Compared to the recursive form utilising the implicit call stack.
1:05:24
jasom
also if you break in the middle of the state-machine, you more quickly get the information you want with most implementations it will show the current function
1:06:50
jasom
neominimum: right, my comment was that most of the times that recursion is more clear than iteration, it's due to the implicit call-stack, which means it's not a tail-call.
1:07:18
EdLangley[m]
Hmm, expanding to (defun foo (...) (macrolet ((foo () `(go start))) (tagbody start ...))) would mean you could just turn recursive calls into GOs
1:08:20
jasom
EdLangley[m]: at that point I'd rather just do a loop/apply/funcall and return a function/args list from each state
1:09:19
EdLangley[m]
I've used it in PHP before when the recursive version of a function was significantly easier to write than the iterative one.
1:09:43
jasom
I've done both and I find the tagbody gets unweidly quickly for complicated state machines