freenode/#lisp - IRC Chatlog
Search
6:30:53
aeth
iqubic: Here, let me help you. Closures are what we're talking about. Another name for "closure" is "lexical closure".
6:31:20
aeth
The alternative name might help explain why foobar works as expected. It's using a lexical variable.
6:32:33
aeth
If you really want to have fun, let's play with this version: (let ((foo 42)) (defun foobar () foo) (defun foobar+1 () (incf foo)))
6:32:57
aeth
This was a major selling point of Lisp for a long time until everyone started copying it.
6:33:57
aeth
Note, though, that closures require the lexical variables to work. The normal ones, without the earmuffs.
6:35:17
aeth
It remembers the environment in which the function was created. This works for all functions, even anonymous (lambda) ones.
6:36:00
aeth
Both refer to the foo in that environment because there's no foo in the local environment of either function.
6:37:08
aeth
Lots of languages have this, but I never truly understood scope until I programmed in Lisp.
6:39:36
iqubic
I was trying to have a varible foo defined inside the closure, and outside the closure too.
6:42:06
aeth
Everything is nice and contained, and you shouldn't have any surprises when working with lexical variables.
6:42:29
aeth
I mean, it might be a bit surprising that foo increments, but not too surprising if you look at the whole environment
6:43:17
iqubic
yeah. Calling foobar+1 a couple times, then calling foobar shows a number bigger than 42.
6:48:58
aeth
If you're correct, that should similarly stack overflow, instead of producing fifty quintillion forty-two
6:50:32
aeth
You can do this if you want to verify that it will hold in our other case: (let ((foo 42)) (defun foobar () foo) (defun foobar-inc (x) (incf foo x))) (foobar-inc (* 50 (expt 10 18))) (foobar)
6:52:04
aeth
In Common Lisp, integers are the mathematical integer. The efficient representation is called a fixnum and the inefficient representation is called a bignum. You will be able to go as large as you have memory.
6:52:35
aeth
If the compiler knows that it will stay a fixnum, it will be just as efficient as C. If you forget to do this, at least you still have an integer of some sort.
6:53:09
aeth
In the foobar example, you probably want an integer and not a fixnum. So that's what you get.
6:54:14
aeth
Most cool uses of closures use anonymous functions, called lambda. That way, you don't have to do everything globally.
6:56:23
aeth
Let's make a foo factory: (defun foo (&optional (foo 42)) (values (lambda () foo) (lambda (&optional (x 1)) (incf foo x))))
6:57:25
aeth
You can make it more robust like this: (defun foo (&optional (foo 42)) (check-type foo integer) (values (lambda () foo) (lambda (&optional (x 1)) (incf foo x))))
6:58:52
aeth
You can bind both functions with multiple-value-bind. You can call these functions with funcall. You can do something like this now: (multiple-value-bind (foobar foobar-inc) (foo 42) (funcall foobar-inc) (funcall foobar))
6:59:25
aeth
I wish we still had lisp paste because line breaks probably would help here now that things are getting more complicated.
7:00:17
aeth
The first defun is the same as the second, except the second also checks that foo is an integer, so the user doesn't give it a bogus value
7:00:39
aeth
The defun creates an environment with foo, just like our let, except it lets you define it
7:01:44
aeth
Common Lisp has multiple namespaces. That's why both the function and the variable can use the name foo. That means, though, that to call the anonymous functions you just created, you need to use funcall to tell it to treat a variable namespaced thing like a function
7:02:50
aeth
That's what the multiple-value-bind is for. There are two return values because there are two functions. It's just like let, except it defines it from multiple return values, e.g. this foo will start at -1: (multiple-value-bind (foobar foobar-inc) (foo -1) (funcall foobar-inc) (funcall foobar))
7:03:41
aeth
If you don't like multiple return values you can always put them in a list, vector, standard-object, struct, hash-table, etc. of course.
7:04:34
aeth
The foo environment is stored away in some place that foobar and foobar-inc (formerly anonymous functions, now named) can access
7:06:33
iqubic
but once I make a foo thing, I can no longer seem to call the functions related to foo.
7:07:58
aeth
One is multiple-value-bind. Another is multiple-value-list, e.g. (multiple-value-list (foo))
7:15:39
aeth
(multiple-value-bind (foobar foobar-inc) (foo -1) (setf (symbol-function 'foobar) foobar) (setf (symbol-function 'foobar-inc) foobar-inc))
7:16:08
aeth
You can use symbol-function to define them globally, and then you can use (foobar) and (foobar-inc) as if they were always there
7:20:29
aeth
It's probably handled specially, but in some Schemes, let might be defined as a macro over lambda.
7:22:09
iqubic
For built in stuff I don't care whether a thing is a function, macro, or special form, because they all work mostly the same.
7:22:39
aeth
iqubic: Roughly: (funcall (lambda (x) (format t "~D~%" x)) 42) <=> (let ((x 42)) (format t "~D~%" x))
7:22:58
aeth
iqubic: In some Schemes, the Scheme equivalent of that might be exactly true. I'm sure someone here can tell me why that's not exactly true in CL.
7:25:01
aeth
iqubic: multiple-value-bind actually uses a very similar principle in its implementation, except with multiple-value-call instead of funcall.
7:29:45
aeth
iqubic: A practical example of a let over a lambda is in the utility library alexandria. curry. https://gitlab.common-lisp.net/alexandria/alexandria/blob/e5c54bc30b0887c237bde2827036d17315f88737/functions.lisp#L114-155
7:30:39
aeth
It lets you do this to do partial function application: (mapcar (alexandria:curry #'+ 10) (list 1 2 3 4 5)) => (11 12 13 14 15)
7:31:49
aeth
It lets you do this to do partial function application: (mapcar (lambda (x) (+ x 10)) (list 1 2 3 4 5)) => (11 12 13 14 15)
7:32:22
aeth
curry just creates the lambda for you and makes it a lot more concise (especially if you import curry so you don't see the prefix)
7:37:13
aeth
The worst part about CL is the names. CL chose to be compatible with much older Lisps rather than break compatibility (Scheme broke compatibility). But, hey, I'll take ugly names over many other flaws a language could have.
7:38:29
aeth
Whenever there was a design decision CL chose the uglier, more practical decision. It's designed for industrial use.
7:39:46
aeth
Most of the names are fine, but some older things (including mapcar) don't really follow the language's idiomatic naming style because they predate it.
8:03:16
aeth
Shinmera: Wait, are you telling me that the point of Common Lisp is to be the common Lisp?
10:06:37
beach
How can I implement the standard method combination if I have no evaluator (no compiler, no interpreter)?
10:07:27
beach
You can assume that the methods themselves exist and have been loaded as binary code from (say) a file.
10:19:53
specbot
Standard Method Combination: http://www.lispworks.com/reference/HyperSpec/Body/07_ffb.htm
10:21:01
beach
And you would turn the applicable methods into an effective method which is a function that needs to be created at runtime.
10:21:26
phoe
Your CALL-NEXT-METHOD may simply invoke a method that is found via some dynamic variable.
10:23:11
phoe
If you know that you will want to have a CALL-NEXT-METHOD local function, then you can take a step back and, whenever you compile the methods, simply force them to have an ignorable local function CALL-NEXT-METHOD that refers to some dynamic variable and expects it to be bound.
10:24:36
beach
Related question: Suppose I have a (possibly empty) list of :BEFORE methods, a (possibly empty) list of :AFTER methods, a (possibly empty) list of :AROUND methods, and a (possibly empty) list of primary method. Can I, without any other information, always know what to do when call-next-method is called?
10:24:47
phoe
If it's for bootstrapping, the end user will not be able to notice that they have a function that is global but should have been local.
10:25:47
beach
2. If the list of :AROUND methods is empty, but the list of :BEFORE methods is not, then call all the :BEFORE methods in that order, and then set the list to '().
10:26:17
beach
3. If the list of :BEFORE methods is also empty, but the list of primary methods is not, then pop the first primary method off and call it.
10:26:40
phoe
The algorithm in CLHS seems well-defined. Your code should be able to always guess what to do next.
10:27:16
beach
4. If the list of primary methods is also empty, but the list of :AFTER methods is not, then call the methods on the list in that order.
10:29:00
phoe
Let's assume that you have *PRIMARY-METHODS* that holds the list of all primary methods for a function.
10:29:23
phoe
grab the CAR of this list, rebind the variable to the CDR, call the method-function of the CAR.
10:29:41
phoe
This way, CALL-NEXT-METHOD may refer to the next method by grabbing the CAR of *PRIMARY-METHODS*.
10:31:22
phoe
CALL-NEXT-METHOD can check on *AROUND-METHODS* if the CAR of this list "matches" the CAR of *PRIMARY-METHODS*.
10:33:38
beach
some of which are primary, some of which are auxiliary. No :AROUND method is attached to another method.
10:36:04
beach
Given four lists *AROUND-METHODS* *BEFORE-METHODS* *PRIMARY-METHODS* and *AFTER-METHODS*...
10:36:40
beach
To invoke the effective method, I actually call the global function CALL-NEXT-METHOD, and that one does the following...
10:38:06
beach
2. If *AROUND-METHODS* is empty, it calls all the methods in *BEFORE-METHODS*, then sets that list to the empty list, pops the first primary method off and calls it.
10:38:35
Shinmera
beach: You have to check whether there's a primary method at all first, and if not, error.
10:39:18
beach
3. If both *AROUND-METHODS* and *BEFORE-METHODS* are empty, then if *PRIMARY-METHODS* is empty, then error.
10:39:52
beach
4. If *PRIMARY-METHODS* has a single entry on it, then pop it off, call it, and then call all the methods in *AFTER-METHODS*.
10:40:12
phoe
But if there is no primary method when there are after/or before methods, then it is an error.
10:40:49
beach
Since this is bootstrapping, we can assume that the code is correct, so that CALL-NEXT-METHOD is not called unless it is allowed.
10:42:53
beach
Correct, but if it has a single entry, it is the last one, so the after methods should be called.
10:43:35
phoe
You only call the first one - it is the method itself that may call next methods if it wants to.
10:44:17
phoe
If the method wants to call more, then it must invoke CALL-NEXT-METHOD inside its body.
10:50:31
pjb
aeth: the best thing in CL are the names! If you're not happy with them, you can define your own in your own package! (:use "AETH-LISP") !
10:52:28
aeth
pjb: thanks to specialization-store we now have a (semi-portable) way to easily define type-generic functions on numbers and sequences that behave like #'+ and #'map
10:53:12
aeth
Which means arithmetic functions could be defined to support inline sequence functions and hopefully have no performance penalty with the existing arithemtic functions!
10:56:03
pjb
Well arithmetic with more than two arguments has the inconvenient that the dispatch has to be done by analysing the whole argument list types.
10:56:58
aeth
I think another problem is that arithmetic with vectors/matrices would cons heavily unless there was something fancy going on.
10:57:04
pjb
Well, AFAIK the standard would allow (reduce op arguments), but you'll get more precise results by sorting out the arguments.
10:58:26
aeth
(Also, matrices would have to be represented with 2D arrays, not sequences, or else there's no way to tell them apart from vectors, at least as I just described things.)
11:08:17
aeth
pjb: I think you just pointed out why this wouldn't work, though. Argument lists can be as close to infinite as 64-bits allows (probably the most positive 63-bit fixnum value)
14:10:39
flip214
using CLON for argument parsing I came upon this problem: a STROPT (say, "-l") has value NIL if the cmdline argument has a "-" in front.
14:11:16
flip214
Is that on purpose? Can I tell CLON that the string "option" isn't optional but required?
14:11:38
flip214
My use case is getting an integer from the command line - but negative values don't work so well, as you can see.
14:36:09
flip214
jackdaniel: no, the problem is that passing "-l -1" on the command line looks like the two options "-l" and "-1" to CLON
14:39:08
jackdaniel
https://www.lrde.epita.fr/~didier/software/lisp/clon/enduser/Valued-Options.html#Valued-Options
15:14:17
phoe
KZiemian: I think it refers to data stored in a tree structure, where you have nodes that can point to other nodes without cycles, and leaves are the nodes that do not point to anything.
15:36:33
pagnol
there's a library I would like to use (https://github.com/heegaiximephoomeeghahyaiseekh/lisp-binary) but for some reason ql:quickload gets stuck during compilation
15:46:03
zazzerino
pagnol: I'm also on debian stable, and would recommend https://github.com/roswell/roswell for this task
15:56:00
phoe
yep. AFAIK there are workarounds, look on the github page of the project, in the issues.
15:57:59
zazzerino
Looks like an interesting library. I tried loading it in quicklisp and have the exact same issue (quickload hanging while loading [package lisp-binary]. Sorry, I don't have a solution, but it's not just your machine!
15:58:34
zazzerino
Also, looks like the main dev opened an issue about a month ago with the title "Dependencies are fucked" lol
15:59:26
phoe
zazzerino: it's not QL hanging. It's SBCL running out of heap space while compiling it.
16:00:55
zazzerino
phoe: yeah, I think you're right. I just meant "ql:quickload" was the function I was running to load the library (which was in my local-projects dir)
16:15:00
zazzerino
maybe try ccl? if you've installed roswell it should be as easy as "ros install ccl" and I think "ros use ccl"
16:26:42
beach
ebzzry: When it looked like the xxx-if-not family was going to be removed (because of being deprecated) complement would have been more useful. But then, most people now agree that they are undeprecated.
16:41:03
Xach
An implementation may choose not to signal this error for performance reasons, but implementations are forbidden from defining the failure to signal an error as a useful behavior.
17:00:17
pjb
ebzzry: you have to distinguish two cases: #'foo where foo is a symbol exported from CL and #'foo where foo is not exported from CL.
17:04:59
pjb
11.1.2.1.2 points 2 and 3 (the exceptions deal only with flet/labels), mean that the implementation is free to fbind symbols exported from CL.
17:06:45
pagnol
phoe, zazzerino, jackdaniel on #sbcl suggested I run sbcl with --dynamic-space-size=8192, and it worked
17:14:18
pagnol
being new to lisp I had to google to find out what this refers too and am reading http://wiki.c2.com/?SmugLispWeenie
17:27:52
drmeister
I'm writing allocation profiling code for Clasp - is it better to keep track of the total memory allocated or the number of allocations?
17:28:56
drmeister
It seems to me the number of allocations is more valuable - there's overhead associated with each allocation.
17:33:50
Colleen
Bike: drmeister said 10 hours, 42 minutes ago: bclasp seems rock solid now - no more exception handling problems that I can see.
17:33:50
Colleen
Bike: drmeister said 10 hours, 40 minutes ago: It was a very, very longstanding problem in the WITH-TRY macro when an exception bubbles up the stack.
18:19:19
oisjdfoasidf
https://pastebin.com/61SjuyYV I get "*** - CONCATENATE: #\a is not a SEQUENCE", help?
18:20:22
oisjdfoasidf
I am brand new to lisp (coming from years of Python experience), and finding it very hard to get used to lisp. trying to write basic hangman game..
18:21:35
beach
oisjdfoasidf: IF in Common Lisp is a form with a value, so you can write: (setq result (concatenate 'string result (if (find c guesses) x y)))
18:21:53
pjb
oisjdfoasidf: but more probably, you'd want to use a functional expression, not a procedural one!
18:22:17
beach
oisjdfoasidf: Only in languages where IF is a statement must you duplicate the (setq result (concatenate 'string result ....))
18:22:19
pjb
oisjdfoasidf: also, your function doesn't do I/O, so don't include "PRINT-" in its name!
18:23:19
pjb
(defun current-matches (word guess) (map 'string (lambda (word-char guess-char) (if (char= word-char guess-char) word-char #\-)) word guess)) (current-matches "apple" "ajkle") #| --> "a--le" |#
18:25:12
pjb
(defun revealed-word (word guesses) (map 'string (lambda (word-char) (if (find word-char guesses) word-char #\-)) word)) (revealed-word "apple" "ajklexyz") #| --> "a--le" |#