libera/commonlisp - IRC Chatlog
Search
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
6:33:14
asarch
If you have: '(:beers 20 :tacos 3), how would you add the property :pizza 4 to the list?: (:beers 20 :tacos 3 :pizza 4)
6:34:21
asarch
(push :pizza *the-list*) (setf (getf *the-list* :pizza) 4) overwrites a property name: (:pizza 4 20 :tacos 3)
6:36:08
mfiano
The problem might be the original question made a literal list as its example to be mutated.
6:36:13
moon-child
this will behave slilghtly differently to beach's suggestion, if pizza happens to already be contained within the property list
6:41:22
beach
asarch: Also, I would start using more interesting food examples, like :SUSHI, :MAGRET-DE-CANARD,
8:32:00
beach
DEFCONSTANT means the compiler can substitute the value for the name when references are compiled.
8:32:01
phoe
with constants, the compiler doesn't need to do variable access, it is allowed to splice literal constant values wherever the constant is used
8:32:33
phoe
and that means no global value lookup and no dynamic binding lookup, which speeds things down, and that the compiler can always figure out the type of the value ahead of time