Dear Julians, I have a major dilemma. I’ve been going back and forth between how to handle this and I was wondering if there’s any best practice?
There is a Html.div function which creates a HTML <div>...</div> element. This is part of Genie’s HTML rendering API, together with many others, corresponding to the rest of HTML elements. These are exported to be used for writing expressive HTML UI code:
section() do
h1("Hello")
p("Lots of text")
end
The issue is that Html.div conflicts with Base.div which is automatically exported by Julia. This makes Html.div an exception in the Html API in that it needs to be written as Html.div(...) or explicitly imported. This is ugly and verbose and can trip people new to Julia. So from a usability/principle-of-least-surprise perspective I’m all for specialising Base.div.
However, my left brain hemisphere screams that from a semantic standpoint Base.div and Html.div are totally different, and that it’s not specialisation as Html.div does not introduce a new division method.
In the end I’m partisan to doing what’s more natural for the users (least surprise is a big deal for me and a user of the Html API expects div to work like any other methods) but I was wondering what’s the community recommendation when dealing with such name conflicts, if any.
I would not extend Base.div, that is for division.
Regardless of the existence of Base.div, I think that using Html.div, Html.p etc are much more clear anyway, so personally I would just not export any of these.
In the last few months I tried various approaches (and it’s true, this is an edge case in that the Html API is very large, which won’t be the case with most name conflicts with definitions from Base). My observations where:
when one has a 20-30-50 lines of HTML UI code, prefixing adds a lot of noise and extra typing. Also, it’s very clear from the context that we’re dealing with an HTML div so prefixing does not add value. This is more of an issue of a DSL really. @fredrikekre
in the beginning I didn’t export but I noticed that when rendering a reasonably complex HTML UI, explicitly listing all the used tags was a tedious process (imagine having to manually import some 20-30 tags). Also it was annoying as I would always forget to declare them, error, argghhh. @fredrikekre
@Mason I’ve seen this used in some other package’s APIs, but for consistency it means all the elements would have to be prefixed, so we’d end up with html_p, html_h1, etc. And in this case we’re better off using Julia’s “natural” namespaces, the modules, e.g. Html.div.
A “think outside the box” idea I just got would be to build a DSL out of constants. Then all the elements would be defined as SPAN, DIV, H1, P (uppercase tags would feel so 90s :)) ). But it seems very uncomfortable to type that many uppercase words… Although the idea might have some potential.
If you don’t have to define div don’t do it and instead, let the users define their custom tags.
In AcuteML, I made HTLM/XML tag names independent of the Julia types. This allows high level of flexibility without unnecessary code bloat. I can pass any Julia type (String, Number, Array, etc) to a field without being worried about its name.
If your library is only used for HTML5, then it makes sense to define a type and call it div. However, for a general library like AcuteML that supports any arbitrary XML/HTML code with any tag names, this approach is not suitable.
using AcuteML
@aml mutable struct Body "~"
group::Vector{String}, "div"
heading, "h1"
heading2, "h1-nonstandard"
foo, "mytag"
end
AcuteML is like a Julia version of JSX. In the above, I defined Body as a component. Now, I can use it as a Julia type with any tag name!
@aml mutable struct Page doc"html"
body::Body, "~"
end
I am pro-extending functions. Methods can have different “definitions” based on the context / arguments. That’s the point of multi-dispatch. div(::AbstractString) however would fall into type piracy though which is the only reason why I would not favor it.
An other way of implementing the same kind of idea would be to have
@html section() do
h1("Hello $(div(5,4))")
p("Lots of text")
div("boo")
end
expand to something like
@html section() do
# Local function that dispatches to Base.div or Html.div
div(args...) = Base.div(args...)
div() = Html.div()
div(x::AbstractString) = Html.div(x)
h1("Hello $(div(5, 4))") # -> calls Base.div(5, 4)
p("Lots of text")
div("boo") # -> calls Html.div
end
I disagree with the viewpoint “div does division so it should never be used for another purpose”.
This leads to people handicapping themselves, and then trying to find dubious justification for
“creative” use of div. For me a method:
Base.div(x::foo)=...
is not pirating if the type foo is specific to your package. I do not see in which way this could
introduce a conflict with Base or any other package. Thus a definition
Base.div(x::AbstractString)=...
is pirating (because if two packages do that there will be a conflict) but a definition
Maybe a new language feature would be useful: a new import variant that allows for hiding several symbols. Haskell has that and I think it is quite handy:
import Base hiding (div)
In Julia it could maybe look something like this:
hiding Base: div
Though, this would need a baremodule or so, and it wouldn’t help users of older versions of Julia. I’ve opened an issue on GitHub.
You are of course free to use div to mean whatever you want in your own package but if you extend Base.div it should mean the same. Otherwise, you have no idea what generic code means because you don’t have concrete types available at that point. Maybe you forgot Function name conflict: ADL / function merging? where this was explained in detail.
Base.div is always binary numeric arguments (or with third optional argument), while for HTML always (one?) string. Maybe I have “no idea”, but does it not save us if div for HTML is always a unary function? I’m thinking of the practical implications of specializing. I looked at your link to a much longer discussion than I have time for now… and from there a link to:
I just know Julia for web programming, is also very important, so I hate to have this dilemma, and would like the most natural div (without needing macros or prefixing). Is this about mostly about the help text for div, that can’t be(?) extended. Can we allow for some impurity here?Would it make something slower?
SInce ÷ points to div (also for the help text), you could do very confusing…:
julia> ÷("text for div")
text for div
About the help text, should it be “x ÷ y” rather than current “÷(x, y)”?