3

I am re-typesetting a picture (Figure 3.9) from "Structure and Interpretation of Computer Programs":

Original image

The result of my TikZ musings is the following:

TikZ image

Please, ignore the white-ish main colour, I want to make the picture usable with both dark and light backgrounds.

This is the code for the picture:

#+name: remark-environments-3-9
#+header: :imagemagick yes :iminoptions -density 300 :imoutoptions -geometry 800
#+header: :fit yes :headers '("\\usepackage{tikz} \\usetikzlibrary{positioning,fit,petri,arrows}")
#+header: :buffer on
#+begin_src latex :results raw file :exports both :file figure-3-9.png
\begin{tikzpicture}[inner sep=0mm,>=stealth',very thick,color=black!50]
\begin{scope} [node distance=4mm]
\node (make withdraw) {make-withdraw: ...};
\node (fake1) [right=of make withdraw,xshift=40mm] {};
\node (W1) [below=of make withdraw.west,anchor=west] {W1:};
\end{scope}

\node (g env) [draw,rectangle,inner sep=2mm, fit=(make withdraw) (W1) (fake1)]
{ };

\node (g env name) [left={of g env},text width=1cm]
{global env} edge[->,very thick] (g env);

\begin{scope}[node distance=0mm]
\node [draw,minimum size=5mm,circle,tokens=1,below=of W1.east, yshift=-20mm] (w1fun-left) {};
\node [draw,minimum size=5mm,circle,tokens=1,right=of w1fun-left, xshift=0mm]
(w1fun-right) {};

\node [rectangle, fit=(w1fun-left) (w1fun-right)] (w1fun)
{} edge [<-, to path={|- (\tikztotarget)}] (W1.east);
\end{scope}

\node [below=of w1fun-left, align=left] {parameters: amount \\ body:...}
edge [<-] (w1fun-left.center); 


\node (E1-env) [below=of g env,yshift=5mm,draw,inner sep=2mm] {balance: amount}
edge [->] (g env);
\node [left=of E1-env,xshift=5mm] {E1} edge [->] (E1-env);

\path (w1fun-right.center) edge[->,to path={-| (\tikztotarget)}] (E1-env.south);

\end{tikzpicture}


#+end_src

This looks more or less matching the original, even looking better, but I wonder what are the most obvious (and less obvious) ways to make the code more idiomatic.

In particular I don't like:

  1. "two dot" scope
  2. "tokens" are still black and thus invisible
  3. manual xshifts and yshifts
  4. I'd like some macro, or something like that to define for later usage, if I want to make bigger environment diagrams, to make something like \environment or \closure.
  5. Any other ways to make this look better.
4

As for your points:

  1. You can make this scope a pic. This addresses also point 4.
  2. You can use colored tokens={black!50}.
  3. The manual shifts are unnecessary if you add the distance in the positioning syntax, e.g. right=44mm of make withdraw.
  4. One very convenient way to repeat things is to use pics. And after you pointed your finger on it: the arrows pointing to the shapes are just pins. Another obvious thing is to use styles. I added a box style for the repeated rectangles that get drawn. One can add parameters to these styles, and default values that get applied if one does not set the parameter. In the box example the parameter is the inner sep, and its default 2mm.
  5. It is hard to make things nicer when it is not clear what the target output is. For the time being I added font=\sffamily.

Here is a complete example, which also uses the backgrounds library to add the black background. If you explain a bit more what you expect under point 5, I will be happy to reiterate.

\documentclass[tikz,border=3mm]{standalone}
\usetikzlibrary{positioning,arrows,fit,petri,backgrounds}
\begin{document}
\begin{tikzpicture}[inner sep=0mm,>=stealth',very thick,color=black!50,
    background rectangle/.style={fill=black},show background rectangle,
    font=\sffamily,pics/two dots/.style={code={
     \node [draw,minimum size=5mm,circle,colored tokens={black!50}] 
     (#1-left) {};
    \node [draw,minimum size=5mm,circle,colored tokens={black!50},
    right=0pt of #1-left]
        (#1-right) {};
    \node [rectangle, fit=(#1-left) (#1-right)] (#1){};
    }},
    every pin edge/.style={<-,very thick},
    box/.style={draw,rectangle,inner sep=#1},box/.default=2mm]
  % 
  \node (make withdraw) {make-withdraw: \dots};
  \node (fake1) [right=44mm of make withdraw] {};
  \node (W1) [below=4mm of make withdraw.west,anchor=west] {W1:};
  %
  \node (g env) [box, fit=(make withdraw) (W1) (fake1),
  pin={[text width=1cm,pin distance=10mm]left:global env}]
  { };
  %
  \path ([yshift=-20mm]W1.east)pic{two dots=w1fun}
    (w1fun) edge [<-, to path={|- (\tikztotarget)}] (W1.east);
  %
  \node [below=of w1fun-left, align=left] {parameters: amount \\ body: \dots}
    edge [<-] (w1fun-left.center); 
  \node (E1-env) [below=5mm of g env,box,
    pin={[pin distance=5mm]left:E1}] {balance: amount}
    edge [->] (g env);
  \path (w1fun-right.center) edge[->,to path={-| (\tikztotarget)}] 
    (E1-env.south);
\end{tikzpicture}
\end{document}

enter image description here

  • Can I use "pic" to combine an environment and its name into a single entity? I mean that this is also a repeating pattern in environment diagrams. Here we have (g env) and (g env name), which essentially are a single thing; and also (E1-env) and the node on the left of it are sort of a single entity. – Vladimir Nikishkin Nov 12 at 2:12
  • @VladimirNikishkin Yes. However, looking at this now: this is just a pin. You do not even have to invent anything for that. I'll add that ASAP to the answer. – Schrödinger's cat Nov 12 at 2:14
  • @VladimirNikishkin I added the pins and made some more minor changes. – Schrödinger's cat Nov 12 at 2:44
  • Accepted. I guess, this can be called an answer to the very loosely defined question that I asked. Thank you. – Vladimir Nikishkin Nov 15 at 8:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.