In a class I am taking we are using the old R5RS standard of Scheme to solve SICP assignments. I like to do test first development, so I figured a unit testing framework would be nice, and I chose SchemeUnit for writing the small tests.
This has worked fine so far, just testing primitives in the output (strings, numbers, ...), but I hit a road block when trying to test lists. It has probably something to do with differences in the Scheme dialect used to run the tests:
foo.scm: (define a-list (list 2))
foo-tests.scm: (check-equal? a-list (list 2))
Result when running the tests:
Unnamed test
FAILURE
name: check-equal?
location: tester.scm:22:3
actual: {2}
expected: (2)
To make the test suite run, I have to add "#lang scheme/base to the top of foo-tests.scm and require the schemeunit package. In foo.scm I need to have #lang r5rs and (#%provide (all-defined)) at the top.
I guess lists are somehow differently implemented in R5RS and "scheme/base". Any way to get them to work together? And why does it fail ({} vs ())?
Yes, as you've noticed, lists are implemented differently in #lang r5rs vs #lang scheme/base. If it's possible to write the tests in your foo-tests.scm in r5rs, that would help to eliminate the possible confusion.
You should be able to do this by having this at the top of your foo-tests.scm file.
#lang r5rs
(#%require schemeunit)
(#%require "foo.scm")
;; Now you can add your tests here:
(check-equal? a-list (list 1 2 3))
If the test suite is written in the same language, then the constructs --- and in particular, the representation for lists --- should match up. The test above should hopefully pass.
To elaborate on the difference between the r5rs lists and the one in #lang scheme (and #lang racket): Racket uses immutable cons pairs to represent lists. Immutable cons pairs do not support the set-car! and set-cdr! functions of r5rs, so it wouldn't be faithful to the standard for the #lang r5rs language to use the built-in immutable pairs. To support the r5rs standard, Racket includes a separate mutable pairs data type, and uses it consistently within r5rs. But it means that the standard pairs in Racket and the mutable pairs do not compare equally.
Related
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
Improve this question
A claim that I recall being repeated in the Clojure for Lisp Programmers videos is that a great weakness of the earlier Lisps, particularly Common Lisp, is that too much is married to the list structure of Lisps, particularly cons cells. You can find one occurrence of this claim just at the 25 minute mark in the linked video, but I'm sure that I can remember hearing it elsewhere in the series. Importantly, at that same point in the video, we see this slide, showing us that Clojure has many other data structures than just the old school Lispy lists:
This troubles me. My knowledge of Lisp is quite limited, but I've always been told that a key element of its legendary metaprogrmmability is that everything - yes, everything - is a list and that this is what prevents the sort of errors that we get when trying to metaprogram other languages. Does this suggest that by adding new first-class data structures, Clojure has reduces its homoiconicity, thus making it more difficult to metaprogram than other Lisps, such as Common Lisp?
is that everything - yes, everything - is a list
that has never been true for Lisp
CL-USER 1 > (defun what-is-it? (thing)
(format t "~%~s is of type ~a.~%" thing (type-of thing))
(format t "It is ~:[not ~;~]a list.~%" (listp thing))
(values))
WHAT-IS-IT?
CL-USER 2 > (what-is-it? "hello world")
"hello world" is of type SIMPLE-TEXT-STRING.
It is not a list.
CL-USER 3 > (what-is-it? #2a((0 1) (2 3)))
#2A((0 1) (2 3)) is of type (SIMPLE-ARRAY T (2 2)).
It is not a list.
CL-USER 4 > (defstruct foo bar baz)
FOO
CL-USER 5 > (what-is-it? #S(foo :bar oops :baz zoom))
#S(FOO :BAR OOPS :BAZ ZOOM) is of type FOO.
It is not a list.
CL-USER 6 > (what-is-it? 23749287349723/840283423)
23749287349723/840283423 is of type RATIO.
It is not a list.
and because Lisp is a programmable programming language, we can add external representations for non-list data types:
Add a primitive notation for a FRAME class.
CL-USER 10 > (defclass frame () (slots))
#<STANDARD-CLASS FRAME 4210359BEB>
The printer:
CL-USER 11 > (defmethod print-object ((o frame) stream)
(format stream "[~{~A~^ ~}]"
(when (and (slot-boundp o 'slots)
(slot-value o 'slots))
(slot-value o 'slots))))
#<STANDARD-METHOD PRINT-OBJECT NIL (FRAME T) 40200011C3>
The reader:
CL-USER 12 > (set-macro-character
#\[
(lambda (stream char)
(let ((slots (read-delimited-list #\] stream))
(o (make-instance 'frame)))
(when slots
(setf (slot-value o 'slots) slots))
o)))
T
CL-USER 13 > (set-syntax-from-char #\] #\))
T
Now we can read/print these objects:
CL-USER 14 > [a b]
[A B]
CL-USER 15 > (what-is-it? [a b])
[A B] is of type FRAME.
It is not a list.
Let's break the word "homoiconicity" apart.
homo-, meaning "same"
iconic, here meaning something like "represented by"
-ity, meaning "something having such a property"
The quality that makes macros possible (or at least much easier) is that the language itself is represented with the same data structures that you use to represent other objects. Note that the word is not "monolistism", or "everything is a list". Since Clojure uses the same data structures to describe its syntax as it does to describe other values at runtime, there's no excuse to claim it is not homoiconic.
Funnily enough, I might say that one of the ways that Clojure is least homoiconic is a feature that it actually shares with older lisps: it uses symbols to represent source code. In other lisps, this is very natural, because symbols are used for lots of other things. Clojure has keywords, though, which are used much more often to give names to data at runtime. Symbols are quite rarely used; I would go so far as to say that their primary usage is for representing source code elements! But all of the other features Clojure uses to represent source code elements are frequently used to represent other things as well: sequences, vectors, maps, strings, numbers, keywords, occasionally sets, and probably some other niche stuff I'm not thinking of now.
In Clojure, the only one of those extra data structures that is required for some language constructs, besides lists, are vectors, and those are in well-known places such as around sequences of arguments to a function, or in symbol/expression pairs of a let. All of them can be used for data literals, but data literals are less often something you want to involve when writing a Clojure macro, as compared to function calls and macro invocations, which are always in lists.
I am not aware of anything in Clojure that makes writing macros more difficult than in Common Lisp, and there are a few features there distinct to Clojure that can make it a little bit easier, such as the behavior where Clojure backquoted expressions by default will namespace-qualify symbols, which is often what you want to prevent 'capturing' a name accidentally.
As previous answerers pointed out, "homoiconicity" means nothing else than that the famous "code is data": That the code of a programming language consists 100% of data structures of that language, thus is easily build-able and manipulateable with the functions in the language usually used to manipulate the data structures.
Since Clojure data structures include lists, vectors, maps ... (those you listed) and since Clojure code is made of lists, vectors, maps ... Clojure code is homoiconic.
The same is true for Common Lisp and other lisps.
Common Lisp contains also lists, vectors, hash-tables etc. .
However, the code of Common Lisp sticks more rigidly to lists than Clojure does (function arguments are in a argument list and not in a vector like in Clojure).
So you can more consistently use functions to manipulate lists for metaprogramming purposes (macros).
Whether this is "more" homoiconic than Clojure is a philosophical question.
But definitely you need a smaller set of functions for manipulation of the code than in Clojure.
I'm writing a program in which i have to reuse code from one of my professors. My program is written in Racket and the code i want to reuse is written in r6rs.
When I want to test my program it always fails.
This is because I call a procedure with as argument a list (racket list), but that procedure is in the R6RS file. In the R6RS file there is (assert (list? argument)) , this is always false...
Here a simple example :
Racket code :
#lang racket
(require "test2.ss")
(define a (list 1 2 3))
(b a)
R6RS code :
#!r6rs
(library
(test)
(export b)
(import (rnrs base (6))
(rnrs control (6))
(rnrs lists (6))
(rnrs io simple (6)))
(define (b a)
(display "a is : ") (display a) (newline)
(display "list? : ") (display (list? a)) (newline)))
The list? test in the R6RS file is always false... even if I pass as argument a newly created list like in the above example.
How can I do the same as in the example above, so that the list? tests results true.
Thanks for your help!
EDIT : I could not find a r6rs test that results in true on a immutable list, but I found another way to resolve my problem (by passing a mutable list to the procedure).
Racket pairs are different from Scheme pairs since Racket pairs are immutable while Scheme pairs are not.
As far as I know, there is no way to check for Racket's immutable lists in pure RnRS Scheme. However, it is possible to use Scheme's mutable lists in Racket (though of course that isn't really recommended).
#lang racket
(require compatibility/mlist
"test2.ss")
(define a (mlist 1 2 3))
(b a)
Here's an excerpt from the documentation for compatibility/mlist:
This compatibility/mlist library provides support for mutable lists. Support is provided primarily to help porting Lisp/Scheme code to Racket.
Use of mutable lists for modern Racket code is strongly discouraged. Instead, consider using lists.
Still, if you need to interact with Scheme code, that's probably your only reasonable option.
This is just an addendum to Alexis King's answer (code examples can't be in comments). Since the r6rs language (as implemented in Racket) uses mutable lists, and all racket libraries expect immutable lists, you can't reuse the r6rs code as-is. The fastest way to reuse the code is to port it to the #lang racket language.
Change the language, remove the import statement, and then fix each error one at a time.
#lang racket
(define (b a)
(display "a is : ") (display a) (newline)
(display "list? : ") (display (list? a)) (newline)))
When you say your code is written in Racket. Do you mean Racket, the software, or #!racket, one of the multiple compatible languages that Racket (the software) supports?
Since your library is written in #!r6rs, you either need to port it to a #!racket module or you main program can be written in #!r6rs and you can use the library as is. A third option is to make mutable lists to pass to the library function and convert back but or ban lists all togerther, but I find this option somewhat suboptimal.
To do full #!r6rs you need to install your library like this:
plt-r6rs --force --install ./test.sls
I assume test.sls is in the current directory. You'll get a confirmation. you need not restart DrRacket. (Force is not needed, but it will overwrite an earlier version.) Then you just change your code to be Scheme code:
#!r6rs
(import (rnrs)
(test))
(define a (list 1 2 3))
(b a) ; #<void> (and prints stuff to stdout)
Hit [Run] in DrRacket and see the magic!
In Common Lisp, the let uses a list for a bindings, i.e:
(let ((var1 1)
(var2 2))
...)
While Clojure uses a vector instead:
(let [a 1
b 2]
...)
Is there any specific reason, other than readability, for Clojure to use a vector?
You can find Rich Hickey's argument at Simple Made Easy - slide 14, about 26 minutes in:
Rich's line on this was as follows
"Since we were talking about syntax, let’s look at
classic Lisp. It seems to be the simplest of syntax, everything is a
parenthesized list of symbols, numbers, and a few other things. What
could be simpler? But in reality, it is not the simplest, since to
achieve that uniformity, there has to be substantial overloading of
the meaning of lists. They might be function calls, grouping
constructs, or data literals, etc. And determining which requires
using context, increasing the cognitive load when scanning code to
assess its meaning. Clojure adds a couple more composite data literals
to lists, and uses them for syntax. In doing so, it means that lists
are almost always call-like things, and vectors are used for grouping,
and maps have their own literals. Moving from one data structure to
three reduces the cognitive load substantially."
One of the things he believes was overloaded in the standard syntax was access time. So vector syntax in arguments is related to the constant access time when you used them. He said:
Seems odd though as it only is valid for that one form...as soon as it is stored in a variable or passed in any way the information is 'lost'. For example...
(defn test [a]
(nth a 0)) ;;<- what is the access time of and element of a?
I personally prefer harsh syntax changes like brackets to be reserved for when the programmer has to switch mental models e.g. for embedded languages.
;; Example showing a possible syntax for an embedded prolog.
{size [],0}
{size([H|T],N) :- size(T,N1), N is N1+1}
(size '(1 2 3 4) 'n) ;; here we are back to lisp code
Such a concept is syntactically constant. You don't 'pass around' structure at runtime. Before runtime (read/macro/compile time) is another matter though so where possible it is often better to keep things as lists.
[edit]
The original source seems to have gone, but here is another record of the interview: https://gist.github.com/rduplain/c474a80d173e6ae78980b91bc92f43d1#file-code-quarterly-rich-hickey-2011-md
Clojure has a number of libraries for generative testing such as test.check, test.generative or data.generators.
It is possible to use higher order functions to create random data generators that are composable such as:
(defn gen [create-fn content-fn lazy]
(fn [] (reduce #(create-fn %1 %2) (for [a lazy] (content-fn)))))
(def a (gen str #(rand-nth [\a \b \c]) (range 10)))
(a)
(def b (gen vector #(rand-int 10) (range 2)))
(b)
(def c (gen hash-set b (range (rand-int 10))))
(c)
This is just an example and could be modified with different parameters, filters, partials, etc to create data generating functions which are quite flexible.
Is there something that any of the generative libraries can do that isn't also just as (or more) succinctly achievable by composing some higher order functions?
As a side note to the stackoverflow gods: I don't believe this question is subjective. I'm not asking for an opinion on which library is better. I want to know what specific feature(s) or technique(s) of any/all data generative libraries differentiate them from composing vanilla higher order functions. An example answer should illustrate generating random data using any of the libraries with an explanation as to why this would be more complex to do by composing HOFs in the way I have illustrated above.
test.check does this way better. Most notably, suppose you generate a random list of 100 elements, and your test fails: something about the way you handled that list is wrong. What now? How do you find the basic bug? It surely doesn't depend on exactly those 100 inputs; you could probably reproduce it with a list of just a few elements, or even an empty list if something is wrong with your base case.
The feature that makes all this actually useful isn't the random generators, it is the "shrinking" of those generators. Once test.check finds an input that breaks your tests, it tries to simplify the input as much as possible while still making your tests break. For a list of integers, the shrinks are simple enough you could maybe do them yourself: remove any element, or decrease any element. Even that may not be true: choosing the order to do shrinks in is probably a harder problem than I realize. And for larger inputs, like a list of maps from vectors to a 3-tuple of [string, int, keyword], you'll find it totally unmanageable, whereas test.check has done all the hard work already.
I want to judge if two values are of same type, but I found that the type of an empty list is clojure.lang.PersistentList$EmptyList rather than clojure.lang.PersistentList.
user=> (def la '())
#'user/la
user=> (def lb '(1 2))
#'user/lb
user=> (def t (map type [la lb]))
#'user/t
user=> t
(clojure.lang.PersistentList$EmptyList clojure.lang.PersistentList)
user=> (apply = t)
false
user=>
So, I'm wondering why is the type of an empty list different from that of non-empty lists and what's the correct way to tell if two things are of same type?
Don't rely on the concrete types of Clojure data structures. They are undocumented implementation details, and you have no guarantee that they won't change in future versions of Clojure.
It is much safer to rely on the abstractions (e.g. as defined by the IPersistentList or ISeq interfaces). These are much less likely to change in ways that might break your code (my understanding is that Rich Hickey is very big on backwards compatibility when it comes to abstractions. If you depend on a concrete implementation, I believe he would say it's your own fault if things break)
But even better, you should use functions in clojure.core such as seq? or list?, depending on exactly what it is you want to detect. Not only are these likely to maintain backwards compatibility for a long time, they also have a chance of working correctly on non-JVM versions of Clojure (e.g. ClojureScript).