In case you haven’t seen, Scribble does have a <https://docs.racket-lang.org/scribble/lp.html?q=lp2|literate programming library>.
If datum->syntax
doesn’t work (it should though), you can also try syntax-local-introduce
That is a very nice document to read. So much useful information normally not revealed by the creator of a program to its users or maintainers.
It would certainly be possible to make a scribble generated document look like that
You can take a look at scribble/acmart for an example of using a different set of latex styles
Thanks @rokitna! Your solution works. I didn’t know how to get that to work syntactically. The ,#’require-magic-name part is what I was missing.
I also had to mess around with paths, I ended up putting a symlink to my magic/ directory in src/ so that it could be referenced with “magic” instead of "../magic". That fixed an issue when running raco make from outside of src/. In general, is there a way to get the path of the file being compiled during macro expansion?
I’m working through eopl, but want to implement everything using match
instead of using the eopl
package. I’m stuck at the cps interpreter - specifically, how would I write https://github.com/iambrj/eopl/blob/master/src/cps-letrec/cps-letrec.rkt#L54\|this tail recursively? I could always use a closure, but I’m curious if I can just reuse Racket’s lambda
in some clever way.
What kind of output is expected to be the result of (cps some-expression)
?
I want to rewrite (cps-letrec expr env)
into (cps-letrec expr env k)
where k
is the control context
I would love to see a small Racket program (as an example) made literate as an example like the one above. (Yeah I know, “So go ahead and do it!”) I looked at the docs and realized first I would have to reacquaint myself with what Knuth intended when he created Literate Programming. It’s been a while. It occurs to me others may have a handle on that already.
Yes - but how are expressions represented in the output? (I don’t have my eopl book here).
so for instance, if I were to do (cps-letrec '(+ 1 2) init-env identity)
would evaluate to 3
here is what the current interpreter does "letrec.rkt"> (cps-letrec '(+ 1 2) init-env)
3
So you are evaluating and cps’ing at the same time?
Okay - I found a pdf. The grammar for the output is:
> So you are evaluating and cps’ing at the same time? Umm I’m not sure I understand, are they supposed to be distinct? My understanding of cps is to make the control context explicit
CPS is a compilation. It’s supposed to produce another program.
Rewriting a program P into cps form P’ doesn’t evaluate the program. It simply rewrites the program into a new program (using a restricted set of constructs). That program can then be evaluated.
So for example, when you rewrite if: [(list 'if condition then-clause else-clause)
(if (cps-letrec condition env)
(cps-letrec then-clause env)
(cps-letrec else-clause env))]
The idea is to produce an instance of cps-if-exp
in the grammar.
That document is in racket, though?
so this is not a valid cps rewrite? [(list 'if condition then-clause else-clause)
(cps-letrec condition env
(lambda (b)
(if b
(cps-letrec then-clause env k)
(cps-letrec else-clause env k))))]
No, you will need to produce an if-expression (are EOPL using s-expression or records to represent expressions)?
@anything Have you seen the blog posts Jay wrote (some years ago)
I believe eopl uses records and leaves rewriting them using procedures as exercises
> you will need to produce an if-expression what would the expression give me when evaluated? Would it work as a scheme if
?
> are EOPL using s-expression or records to represent expressions I don’t know what “record” is, but looking at your code, it seems that you are matching against S-expression, no?
yes
(I guess record is struct
in Scheme)
@soegaard2 I am still in chapter 5 where they rewrite the letrec interpreter by making the control context explicit. I haven’t looked at chapter 6 yet.
the book transforms the letrec interpreter so that all clauses are tail recursive and control context grows only in the third parameter k
I would like to do the same with my interpreter, but I can’t figure out how to rewrite this clause [(list 'lambda (list args ...) body)
(lambda host-args
(cps-letrec body
(foldr ext-env
env
args
host-args)))]
without using a closure
Okay - I think I get the context now. You are not doing a cps-transform on a program. You are writing an interpreter that has an explicit representation of continuations.
yes yes, sorry for framing my question so terribly :sweat_smile:
That’s ok!
So wrt [(list 'lambda (list args ...) body)
(lambda host-args
(cps-letrec body
(foldr ext-env
env
args
host-args)))]
You need to ask yourself what apply-procedure
will need to do.
I exptect that apply-procedure
will be called as (apply-procedure proc val cont)
.
So if proc
is represented as a Racket closure, you will need to pass the continuation cont
along to the procedure proc
.
You could for example have the convention that the first argument of all procedures are the continuation.
[(list 'lambda (list args ...) body)
(lambda (cont . host-args)
(value-of/k body
(foldr ext-env
env
args
host-args)))
cont)]
Here value-of/k
is your cps-letrec
(I think).
Note that value-of/k
expects three arguments:
yes this would work, but is it tail recursive? Since value-of/k
is called inside a lambda
, its not tail recursive correct?
but if you look at <https://github.com/mwand/eopl3/blob/master/chapter5/letrec-lang/interp.scm#L36|eopl’s >value-of/k
, it is indeed tail recursive
so my question is, can I use Racket’s lambda
for representing procedures, but still not lose out on tail recursion?
I hope at least my question makes sense now :sweat_smile:
I’m pretty sure the answer is yes
because all somethink like (define (f x) x)
expands to using lambda
The last expression in a lambda expression is in tail position, so it is not a problem to call value-of/k
inside lambda
.
whoaa I didn’t know this about tail calls! May I ask where you quoted the above from?
Thank you for putting up with my pestering :smile:
It just happens to be where I remember it is described. I think it is in the Racket docs too somewhere.
I think this is the spot you are refering to: https://docs.racket-lang.org/guide/Lists__Iteration__and_Recursion.html?q=tail%20recrusion#%28part._tail-recursion%29
@arthertz has joined the channel
Thank you @soegaard2! I don’t remember if I saw that before, but it certainly fits the bill. And @samth yes you’re right.
Thanks all of you! I hadn’t seen this post by Jay. Great stuff, specially to me because I enjoy the Hanoi problem at a lot and had been meaning to illustrate it like that. There couldn’t be a better example to me right now. (@samth, yes, that example.pdf is a program in Racket, but written in noweb, not with Scribble.) So I think my intuition was right — perhaps what I want is just to have Scribble produce a LaTeX document like noweb does, with the cross-references and the same layout. The layout itself is just a LaTeX article, so perhaps it is “only” the chunk layout and the cross-referencing that’s need. I don’t know if I would be able to figure something like that out, but if I’m not to far from the required knowledge, I would surely have the motivation to do it. The end result would in fact be a lot better than noweb. Scribble is limitless, after all. Great stuff. Thanks so much!
@anything Check Jay’s archive. There are quite a few of his blog posts in this style.
I bet define-runtime-path
is at least part of the solution you’re looking for there.
I’ve run into the issue that, although I can wrap match
to produce immutable bindings, it only works in the case that I can extract all of the bound variables from a pattern. Since match expanders can extend patterns arbitrarily, I can’t hard code such extraction for a few patterns. I’ve found that the match modules have a private procedure to extract bound variables from a pattern; would it be possible to add something similar to the public API?
Perhaps it’d be best to file a feature request on GitHub?
To close this chapter. After looking at Jay’s Hanoi post (and not being able to compile it locally), I went to look at scribble/lp2 (the newer version of scribble/lp). The documentation gave a nice introduction, so I do understand how it works more or less. I built some examples, looked at the latex source code. The language scribble/acmart worked nicely too in the basic examples. So I seems I understand — to produce an output like the one done by noweb, I could fork scribble/acmart and get the job done. But the job seems a bit over my head. In any case, I can easily see that Racket is by far the best tool there is around to produce things like documents. To write a document without a programming language as beautiful as Racket is a major crime. Word-like users, pure LaTeX users (such as myself) are really missing out on the nice life of having a powerful extensible language to produce beautiful documents. (It’s just not easy.)
I wonder if pollen is a more gentle solution to the objective of producing a LaTeX document in the literate programming style, but I guess not because scribble/lp2 comes ready with the chunk-feature, which is the essence of the solution. So it seems that the solution is really forking scribble/acmart (say), adjust it and worrying about the LaTeX design as a second phase.
I only pointed to scribble/acmart
as an example — I think adjusting the LaTeX preamble used by scribble would be much easier than that.
Yes, an issue for that would be great.
Done!