freenode/#lisp - IRC Chatlog
Search
4:23:55
pragmaticmonkey
For example, the tests are a simple cons cell ( to-be-test exception ) and doesn't need the check macro.
4:32:11
Bike
(defun transfer (x) (if (plusp x) x 0)) (defun neuron (weights inputs) (transfer (apply #'+ (first weights) (mapcar #'* (rest weights) inputs))))
5:02:23
Pixel_Outlaw
iqubic, for what it's worth there is #lispgames where a bunch of weirdos hang out.
5:15:08
aeth
Anything that's abstract, non-realtime 2D can be written many, many ways. Efficiency isn't really an issue
5:18:37
aeth
And if you don't really have physics, that's a major plus. That makes things much easier.
5:35:58
aeth
Think of game design like a graph. The x axis is similarity to an MMOFPS. The y axis is similarity to Dwarf Fortress. Difficulty to implement is roughly how far that game is from 0. 2048 is pretty close to 0 on that graph.
5:36:53
aeth
(And DF-style complexity would be much easier to write in CL than MMOFPS-style complexity.)
6:04:43
aeth
Lisp's local variables are (by default) lexically scoped. They behave like you expect in most programming languages. Lisp's global variables are dynamically scoped (unless constant or implementation-specific). This is rare in languages these days.
6:05:55
aeth
Dynamic scoping basically means that the variable refers to the most recent binding. So if you do (let ((*standard-output* some-new-stream)) (format t "Hello, world!~%")) it will print to some-new-stream even though you told format to use t (i.e. *standard-output*)
6:06:30
aeth
That's because you don't have to pass the variable in, it checks the most recent binding
6:07:26
aeth
Here's the exact definition of dynamic scoping: https://en.wikipedia.org/wiki/Scope_(computer_science)#Dynamic_scoping
6:09:08
aeth
The advantages of dynamic scoping are: (1) it's easy to implement, (2) you don't have to pass the variable deep down the call stack (so you could redefine *standard-output* at the start of your program and it will go to the right spot every time you say format t)
6:09:47
aeth
The disadvantages are: (1) it's slower (the compiler has less information because it's dynamic), (2) it often doesn't do what you want and it makes things harder to understand (you never actually know for sure where format t will print, especially in a multi-threaded application)
6:11:57
aeth
You essentially go backwards up a list of environments, looking for the first one that defines foo
6:12:35
aeth
here's an example of lexical scoping: (let ((foo 42)) (let ((foo 7)) (print foo)) (print foo))
6:13:27
aeth
here's another example of lexical scoping: (let ((foo 42)) (let ((bar 7)) (print foo)) (print foo))
6:13:45
aeth
it doesn't see foo in the bar environment, so it uses foo in the outer environment this time
6:14:13
aeth
Actually, in this example, the same thing would happen with lexical and dynamic scoping
6:15:55
aeth
This creates a closure. Because the only function retrieves the value and no function sets the value, the compiler can turn this into a function that constantly returns 42.
6:17:33
aeth
Let's do the same thing with a local special variable (even though special variables in CL are normally global, and you'll see why): (let ((*foo* 42)) (declare (special *foo*)) (defun barfoo () *foo*))
6:19:35
aeth
(The *foo* convention is just so we know that they're special, btw. Your compiler might warn you if special variables do not have *s on either side.)
6:24:35
aeth
It bound *foo* to 42 but *foo* only existed within that dynamic scope and no longer exists
6:24:50
aeth
That's one reason why dynamic/special variables are probably not what you want most of the time.
6:25:54
aeth
Let's get rid of the error with a global: (defparameter *foo* 7) (let ((*foo* 42)) (declare (special *foo*)) (print *foo*) (defun barfoo () *foo*)) ; I added a side effect print statement so you can see that *foo* is 42 inside the let
6:26:26
aeth
iqubic: By convention (and your compiler might warn if it violates the convention) special variables (the CL term for dynamic variables) always have the earmuffs
6:29:28
aeth
the print statement is within the let. If you did a (print (barfoo)) after you define barfoo, it will also be 42.
6:29:55
aeth
But once you're outside of the let, (barfoo) is looking at the global *foo*, it doesn't remember the environment it was defined in. It looks at a dynamic environment.
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"