Announcing Shapes, a functional drawing language

Merry Christmas!

@nonstroking:[rgb 0.9 0.1 0.1] & @TeX_bleed:2bp | [rotate 20°]*[scale y:2] [] (TeX `Merry Christmas, $\lambda$!´)
   >> \ obj → @width:3bp | [stroke [bbox obj 'bleed]] & obj


message: @nonstroking:[rgb 0.9 0.1 0.1] & @TeX_bleed:2bp
   | (TeX ("Merry Christmas, $\lambda$!"))
tf: [rotate 20^^]*[scale y:2]
obj: [tf message]
#page << @width:3bp | [stroke [bbox obj 'bleed]]
#page << obj

This post is an announcement of the functional drawing language Shapes. (Both block quotes above are complete Shapes programs, although the first is admittedly not the easiest program to read, and the second is slightly obscured by ASCII fallbacks.) There are several reasons for the announcement:

  • Comments and feedback on the design would be most welcome.
  • It would be fun if more people would like to join on the implementation-side of the project.
  • The project is in great need of testers.
  • Someone might actually find Shapes a useful tool!

While Shapes is a domain specific language, there exist other drawing languages for the same type of applications, so the raison d'être lies in how the end result is obtained — that is, what programming in the language is like. The primary audience for Shapes is the community of people with a taste for functional languages.

Then, given that a DSL is being developed, there is an opportunity to dwell upon the general purpose aspects of the language. This is an important motivation behind the development.

So, if you didn't follow the link at the top of the post, you might be wondering what Shapes is really like. Here is a short list of key properties:

  • Syntax is tailored for the application at hand (path construction and LaTeX strings are prioritized use cases).
  • Pure functions have no side effects.
  • Pure functions may be implemented using states.
  • Both lexical and dynamic binding.
  • Named arguments.
  • Dynamically typed.
  • Lazy evaluation.
  • CPS (currently only used for escape continuations).

Error messages are often helpful, there is a fair amount of documentation at the project web site, there is a friendly shapes-mode for Emacs, and I will personally be more than happy to help out in all ways. Please have a look at the online documentation and consider trying it out hands on!

Shapes is developed under the GPL, and implemented in C++.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

The concept is fine...

... but the syntax makes my eyes bleed.

Thanks for the comment!

The syntax of Shapes has been subject for discussions since early days. However, with the most recent version, 0.4.3, the major long-lived issues have been addressed.

Let me take the opportunity to give some details, that can be the subjects of more precise criticisms:

  • Shapes enforces a kind of Hungarian notation, preventing lexical bindings (aka variables), dynamic variables, and states, to be confused. A lexical binding has no particular prefix at all, a dynamic variable has the prefix @, and a state is prefixed with # or •. The reason for the two alternatives for states is that the bullet is thought to make the code easier to read — in Shapes, it it very important to be able to detect where states appear in expressions, as this makes the difference between pure expressions and non-pure expressions (aka statements).

  • Shapes provides three ways to enter string literals. The two shown in the examples above have no escape characters, to maximize ease of use with LaTeX source code. Basically, ("$\lambda$") can be regarded the ASCII fallback for `$\lambda$´. The two-character delimiter of the ASCII fallback greatly reduces the need for escape sequences, but reduces readability due to its bulkiness compared to the very light one-character delimiters.

  • The token 'bleed is a symbol , just like in Scheme.

  • The code snippets above use three forms of function application:

    1. [rgb 0.9 0.1 0.1] is the basic syntax, where rgb is the callee.

    2. tf [] (TeX `$\lambda$!´) uses the left associative [] to apply the unary tf to the following argument. When applicable, this form greatly enhances the readability of expressions that would otherwise suffer from the nesting parentheses phenomenon found in Scheme.

    3. (TeX `$\lambda$!´) >> tf uses the left associative >> to send the left hand side through the unary tf. This operator is inspired by the // found in Mathematica, and also reduces nesting of parentheses. Repeated use of this operator in one expression agrees matches the order in which callees are applied with the order they appear when reading the source from left to right, thereby enhancing readability.

    The operator [] is primarily provided to support curry-style programming, but it also enhances readability compared to >> when the callee is a composition of several functions (here, [rotate 20°]*[scale y:2]). The operator >> is — in my experience — much more useful than [] in keeping down nesting square brackets.

    Note that (TeX `$\lambda$!´) is not a function application (for historical reasons), making it easy to reduce the number of time-consuming calls to pdfLaTeX.

  • A dynamic binding is (basically) a value associated with a dynamic variable, for instance @TeX_bleed:2bp. Several dynamic bindings may be combined using the & operator, and the result is simply called dynamic bindings. (A single dynamic binding is a special case of dynamic bindings.)

  • Dynamic bindings are put into scope using the | operator. This is probably the single most confusing part for the Shapes syntax, so let me write a few more words on this. The dynamic bindings are first class values, and may be passed around arbitrarily without affecting the dynamic environment. The dynamic environment can only be changed using the | operator, which puts the dynamic bindings to the left in scope for the expression to the right.

  • The expression \ obj → @width:3bp | [stroke [bbox obj 'bleed]] & obj constructs a function, of the one argument obj. The ASCII fallback is to replace → with ->. The \ cannot be replaced by λ, since λ is considered an ordinary letter, and using single ordinary letters as keywords is a bad idea. Note that the constructed function will be a pure function, meaning that it has no interaction with states in future calling environments. With just a few exceptions, all functions in the Shapes core are pure.

  • States such as •page (or #page, as written in the example), may be modified using the insertion sequence syntax which reminds of C++. (The state •page has a special meaning in the determination of the program output.)

There is plenty of syntax which doesn't show in the two small examples above, but the items discussed here should cover the spikes that make your eyes bleed, and I think it would be wise to focus on criticism on these to start with.