Visual Font for Diagramming Elixir

Hello all, and especially Elixir teachers and authors. I’d like to present to you the beginnings of a Visual Font for Elixir Diagramming.


Motivation

In learning Elixir, I find that there is a certain disconnect between what the machine reports and a helpful mental model. For example, a map from IEx looks superficially similar to a tuple to the beginner:

Whereas they are both “containers” but otherwise behave very differently. A more helpful representation may be:

where we clearly see that while all of maps, structs, lists, and tuples are collections, map and struct are much more similar to one another.

Drawing these diagrams, however, requires some familiarity with graphics & design, and is not so straight-forward.

Solution

I drew a set of SVGs, and embedded them into a font. This means that anyone could “draw” the images simply by typing its name. This is hard to explain, so I’ve recorded a 45 sec video to show you what this does.

Advantages

  • As a good ol’ font, this is (in principle) application-agnostic; in the video I show its use in macOS Keynote and Illustrator, and it would also work in Sketch’s macOS app.
  • A wide-range of symbols are accessible provided that you know its name.
  • All of these symbols can be expanded / further manipulated in vector software.

Limitations

  • In practice, there is some kinda font war going on, and support of this SVG-in-OT standard is not standard. In general :white_check_mark: {Apple, Adobe, Linux with FreeType 2.12+} :no_entry_sign: {Microsoft, Google, Serif (Affinity)}.
  • You need to know the name of the object
  • The symbols are hand-drawn and is thus finite (see request later)
  • Fonts are… complicated. We have a 85% working font-building pipeline, but it turns out the remaining 15% just can’t be done in the way we were doing, so we’re doing something of a rip-and-rebuild. One of the current limitations is that we do not support spaces in the names, and that is directly limiting this “DSL” that can be constructed.

Instructions / Let Me Play!

If you are on macOS and want to try, the installation & usage is super-simple.

  1. Download the proof-of-concept font from: https://drive.google.com/file/d/1pY65dieJTQH5VJZ-r5ZROxTXb5RyoQ2N/view?usp=sharing
  2. Double-click to install, and yikes, dismiss the “minor error”. You would then find the font in everywhere else that you can choose a system font.

  1. Suggest: Keynote.app for the first try. Add a new slide, draw a new text-box, type map, with a font-size of 60, and change the font to VF elixir-diagram 02. You should see the map diagram.

DSL / names / scope

As a proof-of-concept, there is a limited set of components, all focusing on data types. The following will walk you through the possibilities:

Basic types

atom, string, integer

Each of these support “arguments” of key, small, tiny, and value (these are for composing with the Collection types). The following shows atom, atom(:key), atom(:value), atom(:small), atom(:tiny)

String additionally supports :explicit, which may be useful if you want to illustrate String.graphemes() or something similar. Strings can also be accessed by just "" (and thus ":key" and so on.)

image

Collections

At the moment there’s only map, struct, list, and tuple:

These can alternatively be accessed by %{n}, %x{n}, [n], and {n}.

Collections with 0…5 elements can be generated by list(0)list(5) (or [][5]):

They also support :tiny and :value. So struct(:tiny) and {value} becomes

List variant

Lists have an entirely different variant, accessible from list_horizontal, which accepts the same arguments. Thus… list_horizontal(0), list_horizontal(3), list_horizontal(5) gives:

Composing diagrams

Provided the font-sizes are the same, the objects composes seamlessly. For example, our IEx example of a map from above can be drawn by typing %{2}, atom(key), "value", integer(value):

…and just moved together. Add some normal textbox and you then get:

The :tiny variant fits inside the horizontal list boxes.

What’s next?

My immediate goal is a set of components including functions / pipelines, all the regular data-types, and perhaps pictures for concepts like module, token, and macro. I hope this would help visualize the language part of Elixir.

A little further away — and this depends on us successfully rip-and-rebuild the font-construction — is an exact copy of the above, but in isometric. This would then enable visualizing things that occur / interact at different layers. (You’d specify something in isometric by selecting a font-variant, like how you’d toggle Italics and bold.)

Further further out are OTP concepts, and expanding the vocabulary to be able to talk about database / Ecto / Ash, and Phoenix / LV. The problem here is that I’m very much a newbie tinkerer, with an incomplete view of the landscape. Your feedback would be much welcomed.

17 Likes

Have you looked at LiveBook?

It’s intended to be used as a lot of things, including teaching.

It also helps with visualisation, using Vega Lite, Mermaid js for diagrams.





Perhaps your visual font can be part of it as a Smart Cell, so people can have a nice visual output for the internal data structures and concepts!

4 Likes

Wow that looks great. Did not know that this is even possible.

Is it possible to use it on Linux?

1 Like

Some quick thoughts in mostly random order:

  • a more common confusion (in my experience) for novices is Map vs Keyword - the only difference in their inspect output is [ ] vs %{ }. Continuing the pattern above, a kwlist would look like a cross between the symbol for %{} and [] but I’m not sure if that stands out enough :thinking:

  • an important part of tuples is the fixed size, especially versus lists. What about using the “interlocking protrusion” like on “key” and “value” to join together tuple elements instead?

  • some list algorithms will match more than one element in a head:

      def some_complicated_list_thing([first_thing, second_thing | moar_things]) do
    

    so a variant of list that distinguishes multiple elements before the dashed vertical bar could be handy

1 Like

@derpycoder Yes, LiveBook smart-cells also came to mind when I was drawing the components. It’s beyond my skill-set at the moment, and at the design stage, I iterate faster drawing in Illustrator than wrangling SVG in code.

(The problem working programmatically, however, is that we really would expect to be able to represent any structure… and I’m not sure that nesting of more than one layer is possible to show meaningfully.)

For now I’ll be focusing on the visual language, but going forward will drop the folder of raw SVG so other ppl can experiment with smart-cells. When the visual language is fully stabilized, we can slowly go back through the SVGs and annotate the elements with class and id, which would then greatly help targeting the innards.

@al2o3cr Thanks. We’ve been experimenting for a few months. It’s been far less straight-forward than I could imagine. Hopefully we can get our first product, a set of geographical maps wrapped in a font, out by Dec. There’s a whole lot other plans, like “type a chemical name and get its structure,” but we need to get a more robust build process working first.

SVG-in-OpenType is supported since FreeType 2.12, released 7 months ago (2022 Apr). At that time, different distros had a timeline of about half a year for its inclusion — but I’m not sure yet. When we have our first commercial product we’ll need to do another big round of compatibility testing.

@al2o3cr

  1. The Map vs Keyword is an excellent showcase for how the visuals can help with constructing the appropriate mental model. Keyword was not part of the collection because it is composable from List and Tuple (but really, it should be part of the collection given how widespread it is). Let’s see this in action with an example from elixir-lang:

What I don’t know how to represent is when a keyword list is itself occupying a slot. Need to play to find out.

  1. It’s prob too small in the prev screenshots, but tuples have a “lock”:

It’s my subtle way of reminding folks about the fixed size issues.

  1. :thinking: wat. I’ve never seen / thought of that. Do you have an example in hexdoc that I can poke at? I’m not sure I understand how it behaves.
5 Likes

There’s an example in the language spec - short short version, matching [a, b | rest] is shorthand for matching [a | [b | rest]] and does what you’d expect:

  • binds the first two elements of the list to a and b respectively, and the remainder in `rest
  • fails to match if given a list with less than 2 elements

I brought it up because the list graphic calls out head and tail; sometimes the relevant part of the list is more than just the head.

Nesting is definitely a concern; there’s a reason why get_in is in the standard library :stuck_out_tongue:

Drawing a list as a single logical “chunk” with a box around it also raises two concerns:

  • lists aren’t stored in a contiguous block, but the drawing looks like they are. IMO it’s important to call out that difference because many other languages actually store lists as auto-expanding contiguous arrays

  • building on the above, the “chunk” also obscures the important details of structural sharing between lists when prepended vs appended. For instance, the second keyword list example allocates 3 new cons cells, versus only one for the third example (the last two elements are shared with list).

I’m unsure if the above is overthinking things because I don’t fully grasp the target use-cases for this, but I’ve found the the traditional box-and-arrow cons-cell diagram useful for helping new Elixir devs write code with better performance.

1 Like

Great project! Please keep us posted :023:

Awesome, thank you! I am sure a lot of people will find them useful :smiley:

1 Like