Feeling in a refactoring mood I decided to bravely tackle some performance bottlenecks that were present in Genie’s codebase. I decided to go for the low hanging fruits and squash The Evil
A quick search revealed 3 big (anti) patterns involving
eval. Some of them are pretty old in my codebase and I googled them many times, without being able to find a definitive (or even recent) solution.
#1. dynamically invoking functions.
Once upon a time there was
invoke. It got kicked in v0.5 and no clear upgrade path was indicated (at least not for me when reading the “what’s new”). Hello
Fortunately, this issue was greatly addressed by @StefanKarpinski in a StackOverflow reply - the way to do it is with
My only comment here is that invoking nested functions (like
A.B.C.foo) gets horribly complex - and to be honest it took me a while to understand why Julia throws an error. The issue is,
getfield has no support for nested fields - it would be awesome if it did, nesting 3-4 levels of
getfield is horrible.
#2. dynamically generating functions (and optionally exporting them)
This is a common design pattern when developing DSLs. For example, Genie provides a templating system which allows writing views in pure Julia while still looking like an HTML structure (think Ruby’s HAML).
It looks something like:
body(:class => "foo", :onload => "...") do h1() do "Welcome!" end p() do # more stuff here end end
All these functions are simple utility methods that delegate to a function like:
function elem(content::Function, html_tag::Symbol)
And I have an array of html tags, like:
[:body, :h1, :p]
What is the right way of generating (and optionally exporting) the utility functions, based on the array, without using
I’m using these a lot in a factory style of design pattern. Basically I have various modules that rely on “adapters”. For example for caching or for sessions. Both cache and sessions can be stored either on the file system, a RDBMS or a NoSQL backend. The adapters expose a common interface and the framework just delegates to the underlying adapter for persisting and retrieving the data.
The adapters are user configurable in a settings file - but as the configuration is loaded very early in the app’s life cycle, the actual modules are not available yet. So the config uses
Symbols to reference the adapter name - which later (when/if needed) has to be actually included and referenced.
What’s the right way of doing it, without using
Sorry for the long write and thanks in advance for your time – if you have other design patterns please add them, I think that having a reference for this in one place will be very useful. I’m happy to aggregate it and turn it into a page we can put into the docs or on Julia’s blog. I think many users make the mistake of reaching for evil
eval, especially when coming from interpreted languages.
Btw, here is Genie’s repo, if you feel so inclined to take a look at the code and help track performance issues I’ll be forever grateful, I’m very good at taking constructive criticism https://github.com/essenciary/Genie.jl