/reftree

Automatic object tree diagrams for immutable data

Primary LanguageScalaGNU General Public License v3.0GPL-3.0

reftree — automatic object tree diagrams for immutable data

Join the chat at https://gitter.im/stanch/reftree

This project aims to provide visualizations for common functional data structures used in Scala. The visualizations are generated automatically from code, which allows to use them in an interactive fashion. To use this library you will need to have GraphViz installed (and have dot on your PATH).

For more examples see the materials for my talk “Unzipping Immutability”.

Examples

The following examples will assume these declarations:

import scala.collection.immutable._
import java.nio.file.Paths
import reftree.Diagram

val diagram = Diagram(
  defaultOptions = Diagram.Options(density = 100),
  defaultDirectory = Paths.get("images", "data")
)

Since all the example code is actually run by tut, you can find the resulting images in the images directory.

Lists

val list1 = List(1, 2, 3, 4, 5)
val list2 = List(-1, -2) ++ list1.drop(2)

diagram.render("lists")(list1, list2)

By default the trees will be labeled with the arguments passed to render (using sourcecode), but you can provide the labels explicitly:

val list1 = List(1, 2, 3, 4, 5)
val list2 = List(-1, -2) ++ list1.drop(2)

diagram.render("lists2")(
  "positive"  list1,
  "negative"  list2
)

Queues

val queue1 = Queue(1, 2) :+ 3 :+ 4
val queue2 = (queue1 :+ 5).tail

diagram.render("queues", tweakOptions = _.copy(verticalSpacing = 1.2))(queue1, queue2)

To reduce visual noise from Cons and Nil, the visualization of lists can be simplified. Note however that this option also hides structural sharing:

val queue1 = Queue(1, 2) :+ 3 :+ 4
val queue2 = (queue1 :+ 5).tail

{
  import reftree.contrib.SimplifiedInstances.list
  diagram.render("queues2")(queue1, queue2)
}

Vectors

 val vector = 1 +: Vector(10 to 42: _*) :+ 50

 diagram.render("vector", tweakOptions = _.copy(verticalSpacing = 2))(vector)

HashSets

val set = HashSet(1L, 2L + 2L * Int.MaxValue, 3L, 4L)

diagram.render("hashset")(set)

TreeSets

val set = TreeSet(1 to 14: _*)

diagram.render("treeset", tweakOptions = _.copy(highlightColor = "coral1"))(set)

import de.sciss.fingertree.{FingerTree, Measure}
import reftree.contrib.FingerTreeInstances._

implicit val measure = Measure.Indexed
val tree = FingerTree(1 to 22: _*)

diagram.render("fingertree", tweakOptions = _.copy(verticalSpacing = 1.2))(tree)

Case classes

Arbitrary case classes are supported automatically via shapeless’ Generic, as long as the types or their fields are supported.

import com.softwaremill.quicklens._

case class Street(name: String, house: Int)
case class Address(street: Street, city: String)
case class Person(address: Address, age: Int)

val person1 = Person(Address(Street("Functional Rd.", 1), "London"), 35)
val person2 = person1.modify(_.address.street.house).using(_ + 2)

diagram.render("case-classes")(
  person1,
  "person next door"  person2
)

Animations

You can generate animations using diagram.renderAnimation. For this you will need Inkscape and ImageMagick installed (and have inkscape and convert on your PATH).

Here is an example:

import reftree.Utils

diagram.renderAnimation(
  "list-prepend",
  tweakOptions = _.copy(diffAccent = true))(
  Utils.iterate(List(1))(2 :: _, 3 :: _, 4 :: _)
)

diagram.renderAnimation(
  "list-append",
  tweakOptions = _.copy(onionSkinLayers = 3))(
  Utils.iterate(List(1))(_ :+ 2, _ :+ 3, _ :+ 4)
)

If you prefer to navigate between the animation frames interactively, take a look at renderSequence, which will render each frame into its own file.

Usage

This project is intended for educational purposes and therefore is licensed under GPL 3.0.

To try it interactively:

$ sbt demo
@ render(List(1, 2, 3))
// display diagram.png with your favorite image viewer

You can depend on the library by adding these lines to your build.sbt (the latest version can be found here: Download ):

resolvers ++= Seq(
  Resolver.bintrayRepo("stanch", "maven"),
  Resolver.bintrayRepo("drdozer", "maven")
)

libraryDependencies += "org.stanch" %% "reftree" % "latest-version"