I am working on a DSL for creating web pages. Something like this (Julia like pseudo-code):
module Html
function table(f::Function; args...)
# actual implementation here
end
function tr(f::Function; args...)
# actual implementation here
end
function td(f::Function; args...)
# actual implementation here
end
end
A user would produce code as:
using Html
table(border=0) do
tr(style="border: 1px solid red") do
td()
td()
end
end
The Html
module provides an API for all HTML5 elements, which is a well defined set (so function are created for each standard element). This allows other packages to extend the Html
API, for instance to provide more advanced table
APIs:
module HtmlX
using Html
function Html.table(rows::Vector, columns::Vector)
# actual implementation here
end
function Html.table(df::DataFrames.DataFrame)
# actual implementation here
end
end
So all goes well for APIs for working with the standard HTML5 elements. However, the goal is to use web components
, which extend the HTML5 standard with random elements. Like say a paginated_table
. The problem is that Html
can not define a paginated_table
function in advance.
So I tried something like this:
module Html
function register_element(elem)
Core.eval(@__MODULE__, Meta.parse("function $elem end"))
end
end
But when running:
module HtmlX
Html.register_element("paginated_table")
end
Julia complains that I’m evaling in a closed module.
The objective is to have a rich ecosystem of 3rd party packages where users install and use a variety of such external elements. I don’t like the idea of having the elements defined in say 10 modules within a project, as this would be very confusing for the users. An alternative would be for the packages to export their functions but this would lead to an inconsistent API as Html
does not export the functions.
Any ideas or best practices? Thanks!