I'm working my way through SICP, using both the Ableson/Sussman
lectures and the Berkeley 61A lectures, which are far more my
speed. I'd like to do some of the Berkeley homework, but need the
definitions for sentence, butfirst, butlast and so forth. It looks like at one time there was a simply scheme language built in to Dr. Scheme, but version 4.1.5, the most recent, doesn't have it. From Planet PLT
I thought I could simply add (require (planet "simply-scheme.ss" ("dyoo"
"simply-scheme" 1 0))) in my definitions window. I get
require: PLaneT
could not find the requested package: Server had no matching package:
No package matched the specified criteria
I tried grabbing the simply.scm file from here
and pasted it into my Dr Scheme definitions window, but it doesn't work:
In Advanced Student mode, I get
read: illegal use of "."
For the line (lambda (string . args) in the following code.
(define whoops
(let ((string? string?)
(string-append string-append)
(error error)
(cons cons)
(map map)
(apply apply))
(define (error-printform x)
(if (string? x)
(string-append "\"" x "\"")
x))
(lambda (string . args)
(apply error (cons string (map error-printform args))))))
In R5RS I get
set!: cannot mutate module-required identifier in: number->string
(line 7 of the following code)
(if (char=? #\+ (string-ref (number->string 1.0) 0))
(let ((old-ns number->string)
(char=? char=?)
(string-ref string-ref)
(substring substring)
(string-length string-length))
(set! number->string
(lambda args
(let ((result (apply old-ns args)))
(if (char=? #\+ (string-ref result 0))
(substring result 1 (string-length result))
result)))))
'no-problem)
Advanced Student will never really work for you unless you're following examples that were designed for it. Most books and examples will assume R5RS or something like it. I would recommend using the Pretty Big language, as that includes both R5RS, as well as the PLT require syntax, and a few other things.
In order to use the Simply Scheme package from PLaneT, you will need to use the new require syntax (you can find this on the package listing page; it looks like the docs for the package haven't been updated):
(require (planet dyoo/simply-scheme:1:2/simply-scheme))
Following up; the Simply Scheme support library for Racket can be found at:
http://planet.plt-scheme.org/display.ss?package=simply-scheme.plt&owner=dyoo
I did some light documentation updates in http://planet.plt-scheme.org/package-source/dyoo/simply-scheme.plt/2/1/planet-docs/manual/index.html.
Related
For some code I was working I've needed to handle 'x inside macro. What is standard way of handling those values?
I have code like this:
(define (quoted-symbol? x)
(and (pair? x) (eq? (car x) 'quote) (symbol? (cadr x)) (null? (cddr x))))
(define-macro (test x)
(if (quoted-symbol? x)
`(begin
(display ',(cadr x))
(newline))))
(test 'hello) ;; 'hello will be expanded into list (quote hello)
Is this how this should be handled, or is just in macro you don't use quoted symbols?
NOTE: I'm not asking about hygienic macros (I'm asking about real lisp macros), so please no answers with hygienic macros.
EDIT:
My macro works correctly in Guile and BiwaScheme and in my own scheme like lisp in JavaScript. Here is better example:
(define-macro (test x)
(if (quoted-symbol? x)
`',(cadr x)))
(define (example arg)
(list arg (test 'world)))
(example 'hello)
the question was not about display, but about (cadr x).
EDIT2: You've asked so here you go, my macro:
(define-macro (--> expr . code)
"Helper macro that simplify calling methods on objects. It work with chaining
usage: (--> ($ \"body\")
(css \"color\" \"red\")
(on \"click\" (lambda () (print \"click\"))))
(--> document (querySelectorAll \"div\"))
(--> (fetch \"https://jcubic.pl\") (text) (match /<title>([^<]+)<\/title>/) 1)
(--> document (querySelectorAll \".cmd-prompt\") 0 \"innerText\")"
(let ((obj (gensym)))
`(let* ((,obj ,(if (and (symbol? expr) (not (null? (match /\./ (symbol->string expr)))))
`(.. ,expr)
`,expr)))
,#(map (lambda (code)
(let ((name (gensym))
(value (gensym)))
`(let* ((,name ,(cond ((quoted-symbol? code) (symbol->string (cadr code)))
((pair? code) (symbol->string (car code)))
(true code)))
(,value (. ,obj ,name)))
,(if (and (pair? code) (not (quoted-symbol? code)))
`(set! ,obj (,value ,#(cdr code)))
`(set! ,obj ,value)))))
code)
,obj)))
;; ---------------------------------------------------------------------------------------
(define (quoted-symbol? x)
"(quoted-symbol? code)
Helper function that test if value is quoted symbol. To be used in macros
that pass literal code that is transformed by parser.
usage:
(define-macro (test x)
(if (quoted-symbol? x)
`',(cadr x)))
(list 'hello (test 'world))"
(and (pair? x) (eq? (car x) 'quote) (symbol? (cadr x)) (null? (cddr x))))
the macro is used in my scheme like lisp in JavaScript, like the doc string suggest:
(--> document (querySelectorAll ".class") 0 "innerText")
I want to support:
(--> document (querySelectorAll ".class") 0 'innerText)
The code can be tested online at: https://jcubic.github.io/lips/ (You need to copy/paste the code since current version allow only method calls).
To get expansion you can use
(pprint (macroexpand (--> document (querySelector "x"))))
if it don't work (don't expand) it mean that macro is broken somehow.
dot is build in function that get property of an object and .. macro:
(define-macro (.. expr)
"(.. foo.bar.baz)
Macro that gets value from nested object where argument is comma separated symbol"
(if (not (symbol? expr))
expr
(let ((parts (split "." (symbol->string expr))))
(if (single parts)
expr
`(. ,(string->symbol (car parts)) ,#(cdr parts))))))
that can be use to get nested property like (.. document.body.innerHTML)
Scheme doesn't have "real lisp macros". Some implementations has something similar, but the forms have different names and uses. They are not portable at all.
The standard way of handling 'x is to handle it like an expression that gets evaluated in the expansion. Eg.
(define var 'x)
(test 'x)
(test var)
The two test forms should amount to the same even though the macro test gets (quote x) in the first and the symbol var in the second. At the time of the expansion var does not exist since the implementation can expand all the macros before starting.
You implementation of test will not work. Eg. the display might be run one or twice and then each time you call a procedure that uses it it will gfail since the expansion is the undefined value and it might not be fit for evaluation. eg.
(define (example arg)
(list arg (test 'w)))
When this is defined you get 'w or (quote w) printed with a newline and then the procedure it tries to store is:
(define (example arg)
(list arg #<undefined>))
Note that what constitutes the undefined value is chosen by the implementaion, but I know for sure that in many implementaions you cannot evaluate #<undefined>.
I'm trying to make a basic journal entry language in racket for a class on DSLs (specifically beautiful racket, the training version). I have a correct parser and reader, but I am having difficulty with my macros. Below is my expander code. I am trying to have my DSL be functional rather than imperative (I know I could have a global ledger value that I update, but it is not in the spirit of this project).
I have included the code for the important functions in this module. A lot of them are based off of this tutorial: https://beautifulracket.com/bf/a-functional-expander.html
Right now, when I run my code on this text:
[1920-02-19<equipment-20000><cash-10000,stock-10000>]
[2020-12-20<insurance-500><cash-500>]
I get this output from my display calls:
(#<date 1920-02-19> debits credits)()(#<date 2020-12-20> debits credits)#<void>#<void>
It looks like my lambda is not returning the updated ledger in my journal-entry macro. My knowledge isn't great when it comes to Racket, so I'm not sure what I'm doing wrong here. Any tips?
(define (fold-funcs apl ac-funcs)
(for/fold ([current-apl apl])
([ac-func (in-list ac-funcs)])
(ac-func current-apl)
(display current-apl)))
(define-macro (ac-line ENTRIES ...)
#'(begin
(define ledger empty)
(set! ledger (fold-funcs ledger (list ENTRIES ...)))
(display ledger)))
(provide ac-line)
(define-macro (journal-entry "[" INFO ... "]")
#'(lambda (ledger)
(define entry (list INFO ...))
(define dt (first entry))
(set! entry (rest entry))
(define d (first entry))
(set! entry (rest entry))
(define c (first entry))
(define e (list dt d c))
(display e)
(set! ledger (cons e ledger))
ledger
))
(provide journal-entry)
This is part of an interpreter I am making. I keep getting this error:
define not allowed in an expression context in: (define ret1 (list->string wl))
I am using DrScheme version 371, language Standard (R5RS).
(define (read-command)
(set! com '( '() ))
(set! wl (read-as-list))
(define ret1 (list->string wl))
(commRead)
ret1
)
similar issue here:
(define repl(
lambda()
(display "\nUofL>")
(define inp (read-command))
(define lengtha (length com)
In you interpreter, it seems that definitions can only appear at the beginning of the function. You should use a let* instead:
(define (read-command)
(let* ((com '('())) ; are you sure you didn't mean '(()) ?
(wl (read-as-list))
(ret1 (list->string wl)))
(commRead ret1)))
For the second problem, try this:
(define repl
(lambda ()
(display "\nUofL>")
(let ((inp (read-command))
(lengtha (length com)))
; return a value here
)))
As a side note, your code appears to be written in a procedural style - with all those set! and function calls executed for the effect. How on earth is ret1 going to be modified if you don't pass it as a parameter to commRead? I'd suggest you read a good book on Scheme programming and start writing code on a more functional style, currently your code isn't idiomatic and you'll get into trouble sooner or later.
I am new on SCHEME programming and after some while I succeeded on writing a couple of scripts to deal with long maths formulas.
The issue is that its execution is pretty slow. After profiling I realized that the execution takes about 35% of the time executing the built-in functions equal? and member.
My question is regarding whether there exist more efficient version of those functions or I should re-factor the code in order to remove its calls?
I'm about to re-write the code so I would really appreciate any piece of advice.
Thanks!
EDIT 1:
I add some code for making the question clearer. The following function takes two parameters, a pair (x,y) and a term, x and y are usually variable names(symbols), but they may be a symbol and a number, a list and a symbol, etc. The term is a list such as
(+ w y z (* (- 1) x))
so after the function execution I would get something like
(+ w z)
The code looks like the following:
(define (simp-by-term_eq t_CC t)
(cond
((and (member (list '* (list '- 1) (car t_CC)) (cdr t))
(member (cadr t_CC) (cdr t)))
(delete-1st (cadr t_CC)
(delete-1st (list '* (list '- 1) (car t_CC)) (cdr t)))
)
((and (member (list '* (car t_CC) (list '- 1)) (cdr t))
(member (cadr t_CC) (cdr t)))
(delete-1st (cadr t_CC)
(delete-1st (list '* (car t_CC) (list '- 1)) (cdr t)))
)
(else t)
)
)
There are several conditions like the previous one on the function.
The equal function calls are mainly used into filter callse such as
(filter (lambda (x) (and (not (equal? (list '* (car t_CC) (list '- 1)) x))
(not (equal? (list '* (list '- 1) (car t_CC)) x)))) t)
equal? needs to test every part of list structure so basically it traverses both operands until it has touched all sequences and compared all atomic values. If you need to test if it's the same you can use eq? which only checks that the pair has the same address. Thus it will evaluate to #f even when the list structure look the same. Sometimes that's ok. eqv? is similar to eq? but it also works for numbers and characters (but not strings).
You can use memq that uses eq? instead of equal? and you can use memqv that uses eqv? which also works for numbers and characters.
I'm not that familiar with Guile but if you have performance issues and are using member perhaps you should consider hash tables instead?
It could happen you need to rethink the algorithm you use too if you really need to use equal?, but without specific code it's difficult to be more specific.
I want to be able to take a procedure and see what it looks like. Is this possible?
For example, let's say I have:
(define (some-func x)
(+ x 1))
What I want to do is apply some amazing function (say, stringify) to some-func and be able to look at its guts.
\> (stringify some-func)
"(lambda (x) (+ x 1))"
I haven't found any Racket libraries that do it. Can it be done?!
In R6RS, there is no sure way to determine if two procedures are equivalent; even an expression like (let ((p (lambda () 42))) (eqv? p p)) is not guaranteed to be true.
R7RS addresses that by using the concept of "location tags", where each lambda expression generates a unique location tag. Then eqv? works for procedures by comparing location tags: thus, (let ((p (lambda () 42))) (eqv? p p)) is true, and (eqv? (lambda () 42) (lambda () 42)) is false.
There is no reliable way to get the source of a procedure (many implementations macro-expand and compile the procedures, discarding the original source), and even if you could, you could not use it to compare if two procedures are "equal", because of closures (and that two procedures could have the same "source" but have their free variables bound to different things). For example, consider the two expressions (let ((x 1)) (lambda () x)) and (let ((x 2)) (lambda () x)). They have the same "source", but nobody in their right mind would claim that they are equivalent in any way.
Note, you could easily implement a define alternative to keep the source around. You don't avoid the lexical issues but, modulo that, you've got something with limited use.
(define name->source-mapping '())
(define (name->source name)
(cond ((assq name name->source-mapping) => cdr)
(else #f)))
(define (name->source-extend name source)
(set! name->source-mapping (cons (cons name source) name->source-mapping))
(define-syntax define-with-source
((_ (name args ...) body1 body2 ...)
(define name
(begin (name->source-mapping-extend 'name '(lambda (args ...) body1 body2 ...))
name->source-mapping))
(lambda (args ...) body1 body2 ...)))))
[Above does not replace (define name value) syntax; consider the above an example only.]