Skip to main content
14 of 16
added 31 characters in body
Jules Manson
  • 2.5k
  • 12
  • 20

Separate Styles and Options from Functional Code

Almost nothing was said about Styles and Options so I think I will do that now.

Basis

In all modern languages it is a best practice to separate presentation and behavior of interface elements from content. In web development presentation is controlled with cascading stylesheets (CSS) and behavior with JavaScript (JS) which were early on expressed inline or embedded throughout web pages (HTML) making them appear extremely cluttered and clumsy to work with. This is analogous to how Mathematica currently embeds styles and options throughout functional code. As web pages became more dynamic and grew much larger it became essential to separate presentation and behavior from content by intelligently placing CSS and JS in separate files and linking them to HTML files with a few simple commands at the top. We can't do that in Mathematica (we can but we shouldn't) however we can do the next best thing -- place all styles and options in cells apart from the rest of the code by defining hooks and handlers linking them. This accomplishes three main things...

  • It makes the dependent code more readable and styles and options easier to find when debugging or adjustments are needed.
  • It helps in keeping style directives and options consistent.
  • It practices DRY resulting in writing less code and lighter files.

This is probably not a great strategy for smaller projects which are about a full page or less as using hooks and handlers may cause some bloat. However it is still great practice for becoming proficient at this and for setting up smaller projects that are over time expected to grow or refactored.

Implementing a Strategy

1. Don't Be Clever

Before I suggest a solution I wish to stress just because you can do something it doesn't mean that you should. Do not change the default set options of native functions especially when other developers with various backgrounds or levels of experiences may need to access your code. Too much can go wrong by just forgetting you did that. There is a better way as shall be discussed shortly. Options can also be borrowed between functions but such code usually appears entangled (violates KISS) making it very difficult to read or debug. So I don't recommend this either even though it practices DRY.

2. Separate Presentation and Behavior from Content

The following uses Grid as an example.

My solution is to specify all options using variables then they can be inserted inside a function in place of the actual options.

gridops = Sequence[op1->spec1, op2->spec2, ...] Grid[{row1, row2,...},gridops] 

Sometimes you will have many output structures that have common option specs but with minor differences. You can do one of two things. Trail unique option specs (op3 and op4) after placement of the options variable. This should be the only exception to the rule of separating presentation from content.

gridops = Sequence[op1->spec1, op2->spec2, ...] Grid[{row1, row2,...},gridops,op3->spec3,op4->spec4] 

Or you can create a function to handle unique option specs as parameters. Just one caveat. You may need to wrap the options function in an Evaluate when placed in your dependent function as is necessary with Grid and others like Plot.

gridOps[spec1_,spec2_]:=Sequence[op1->spec1, op2->spec2,...common ops here] Grid[{row1, row2,...},Evaluate[gridOps[spec1,spec2]]] 

Similarly can be done wherever Style is used. For my grid headers I might wrap text in the top row of cells with Style and place common style specs in a variable. If you have unique styles you can create a function to handle them or trail them as was done with Options.

headerstyle = Sequence[12,"Helvetica", Bold] Style["string",headerstyle,...trailing unique styles] 

3. Collect All Option and Style Handlers in One Place

Group all Styles directives and Option specs handlers into a single cell at the top of file placed so that they load before dependencies. This is analogous to how CSS and JavaScript are loaded in web pages. If there are an overabundance of handlers use several cells keeping related styles and option definitions together. Of course there is no need to reminder user that it might be a good idea to set those cells as Initialization Cells.

4. Leverage Up Your Coding Style With Theming

Most projects demand setting only a few options or style directives that are consistently sprinkled in a lot of places. Instead of repeating the same specs define those specs in alias variables and insert aliases everywhere they occur including the aforementioned styles and options handlers made in points 2 and 3. These would then be collected in a cell higher up from any dependent code. Doing this would enable making quick and consistent changes to behavior or theme from just one place in your file. Your hierarchy might look like below. As before it might be a good idea to set cells to Initialization Cell.

(* TOP CELL *) (* theme aliases goes here *) bgcolor=Darker[Blue]; fontcolor=White; fontfam="Helvetica"; fontsize=12; font=Italic; spaces={4,4}; align=Center; frame=All; output=Traditional; method=Automatic; (* NEXT CELL *) (* handlers from points 2 and 3 *) (* ALL OTHER CELLS *) (* all other code *) (* injected with hooks from points 2 and 3 *) 

Your Opinion Matters

What are your thoughts on this? Do you already do something similar? What are your best practices?

Jules Manson
  • 2.5k
  • 12
  • 20