<?xml version="1.0"?>
<?xml-stylesheet href="/transform" type="text/xsl"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:bibo="http://purl.org/ontology/bibo/" xmlns:bs="http://purl.org/ontology/bibo/status/" xmlns:ci="https://vocab.methodandstructure.com/content-inventory#" xmlns:dct="http://purl.org/dc/terms/" xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:xhv="http://www.w3.org/1999/xhtml/vocab#" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" lang="en" prefix="bibo: http://purl.org/ontology/bibo/ bs: http://purl.org/ontology/bibo/status/ ci: https://vocab.methodandstructure.com/content-inventory# dct: http://purl.org/dc/terms/ foaf: http://xmlns.com/foaf/0.1/ rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# xhv: http://www.w3.org/1999/xhtml/vocab# xsd: http://www.w3.org/2001/XMLSchema#" vocab="http://www.w3.org/1999/xhtml/vocab#" xml:lang="en">
  <head>
    <title property="dct:title">IBIS Gets a Tune-Up</title>
    <base href="https://doriantaylor.com/ibis-gets-a-tune-up"/>
    <link href="508e58b4-0b67-4ce0-8acb-bb08797beddc" rev="ci:document"/>
    <link href="elsewhere" rel="alternate bookmark" title="Elsewhere"/>
    <link href="this-site" rel="alternate index" title="This Site"/>
    <link href="http://purl.org/ontology/bibo/status/published" rel="bibo:status"/>
    <link href="" rel="ci:canonical" title="IBIS Gets a Tune-Up"/>
    <link href="software-developer" rel="dct:audience" title="software developer"/>
    <link href="person/dorian-taylor#me" rel="dct:creator" title="Dorian Taylor"/>
    <link href="//vocab.methodandstructure.com/graph-tool" rel="dct:references"/>
    <link href="person/dorian-taylor" rel="meta" title="Who I Am"/>
    <link about="./" href="3f36c30c-6096-454a-8a22-c062100ae41f" rel="alternate" type="application/atom+xml"/>
    <link about="./" href="this-site" rel="alternate"/>
    <link about="./" href="elsewhere" rel="alternate"/>
    <link about="./" href="e341ca62-0387-4cea-b69a-cdabc7656871" rel="alternate" type="application/atom+xml"/>
    <link about="./" href="f07f5044-01bc-472d-9079-9b07771b731c" rel="alternate" type="application/atom+xml"/>
    <link about="verso/" href="3f36c30c-6096-454a-8a22-c062100ae41f" rel="alternate" type="application/atom+xml"/>
    <link about="verso/" href="this-site" rel="alternate"/>
    <link about="verso/" href="elsewhere" rel="alternate"/>
    <meta content="I thought I would wrap up Intertwingler by the end of 2023, but I got diverted by a request to do something that ultimately turned out to be equally important." name="description" property="dct:abstract"/>
    <meta content="2024-01-18T22:29:50.479374+00:00" datatype="xsd:dateTime" property="dct:created"/>
    <meta content="2024-01-19T04:32:32+00:00" datatype="xsd:dateTime" property="dct:modified"/>
    <meta about="person/dorian-taylor#me" content="Dorian Taylor" name="author" property="foaf:name"/>
    <meta content="summary" name="twitter:card"/>
    <meta content="@doriantaylor" name="twitter:site"/>
    <meta content="IBIS Gets a Tune-Up" name="twitter:title"/>
    <meta content="I thought I would wrap up Intertwingler by the end of 2023, but I got diverted by a request to do something that ultimately turned out to be equally important." name="twitter:description"/>
    <object>
      <nav>
        <ul>
          <li>
            <a href="//dorian.substack.com/p/so-long-farewell-auf-wiedersehen" rev="dct:references" typeof="bibo:Article">
              <span property="dct:title">So Long, Farewell, Auf Wiedersehen, Goodbye</span>
            </a>
          </li>
          <li>
            <a href="why-we-care-about-plagiarism" rev="dct:references" typeof="bibo:Article">
              <span property="dct:title">Why We Care About Plagiarism</span>
            </a>
          </li>
          <li>
            <a href="508e58b4-0b67-4ce0-8acb-bb08797beddc" rev="ci:document" typeof="qb:Observation">
              <span>urn:uuid:508e58b4-0b67-4ce0-8acb-bb08797beddc</span>
            </a>
          </li>
        </ul>
      </nav>
    </object>
  </head>
  <body about="" id="EdAE_s9oXqbZBt8yJRmQII" typeof="bibo:Article">
    <p>Early last month a client approached me just as I was getting ready to put together the final piece of <a href="https://intertwingler.net/" rel="dct:references">Intertwingler</a>, my flagship project of 2023. He wanted me to carve off a piece of one of Intertwingler's predecessors as a standalone app&#x2014;a thing called <a href="https://github.com/doriantaylor/p5-app-ibis" rel="dct:references">App::IBIS</a>, that concerned the construction of <a href="https://www.w3.org/TR/skos-primer/" rel="dct:references">concept schemes</a>. This was something I had haphazardly grafted onto the main <abbr>IBIS</abbr> tool, itself a ragged proof of concept I had knocked together over the span of a couple weeks, over a decade ago.</p>
    <p><a href="https://twitter.com/pinboard/status/761656824202276864" rel="dct:references">To quote one Maciej Ceg&#x142;owski</a>: <q>We do these things not because they are easy, but because we <em>thought</em> they would be easy.</q> I had initially believed I could just spend half a day carving off the <abbr>SKOS</abbr> bit or otherwise introducing a configuration parameter that would hide the <abbr>IBIS</abbr> part. This would have been a fine strategy as I don't really care about what happens to this software at this point, since a shippable Intertwingler will replace it.</p>
    <aside role="note">
      <p>Another option I briefly considered was to cut a branch with the necessary subset of functionality. I promptly nixed it because my goal is to get <em>rid</em> of this software (and replace it with Intertwingler), not give myself yet another thing to maintain.</p>
    </aside>
    <p>The proximate problem with the <abbr>SKOS</abbr> interface is that it never really was fit for purpose. Moreover, it was sufficiently different from its <abbr>IBIS</abbr> counterpart that the front-end code couldn't be adapted without a rewrite. Said front-end code was written on top of JQuery, because I wrote it ten years ago, prior to certain guarantees of cross-browser compatibility that exist today. So now I'm looking at rewriting the JavaScript for the whole thing. <em>But</em>, the <abbr>UI</abbr> for the <abbr>IBIS</abbr> part sucks <em>too</em>, so if I'm going to have to rewrite a bunch of JavaScript, I'd rather do it for a less-bad <abbr>UI</abbr>.</p>
    <p>What made the <abbr>UI</abbr> suck in particular was this fixed region at the bottom of the screen reserved for data entry. After a decade of using this tool (after all, it was surprisingly useful despite being so rough), I found this paradigm terrifically cumbersome. It boiled down to having to click around the screen a bunch every time you wanted to add a thing, which was antithetical to my original goal of making data entry <em>quick</em>. There was a set of checkboxes for one or more types of semantic relation (which actually didn't make much sense in practice), an overzealous <abbr>OS</abbr>-supplied autocomplete dutifully offering you stale or redundant entries, and an unwieldy dropdown for connecting existing items, which you had to explicitly toggle into view. The <abbr>SKOS</abbr> side was a little simpler, due to there being only one type of entity and three kinds of semantic relation (versus three and twelve for <abbr>IBIS</abbr>), though I did not feel good about putting lipstick on that pig.</p>
    <aside role="note">
      <p>The overarching issue of the <abbr>IBIS</abbr> tool is there's almost nothing to it, so changing <em>anything</em> entails changing <em>everything</em>.</p>
    </aside>
    <p>So if I was going to put effort into fixing the <abbr>SKOS</abbr> <abbr>UI</abbr>, I wanted it to be a <em>decent</em> <abbr>UI</abbr>. If I was fixing the <abbr>SKOS</abbr> <abbr>UI</abbr>, it would implicitly fix the <abbr>IBIS</abbr> <abbr>UI</abbr> because they're basically the same thing. To fix the <abbr>UI</abbr>, I would have to fix the markup, and to fix the markup, I would have to fix the server-side code that <em>generates</em> the markup. And if I'm going to do <em>that</em>, then I'm going to need to fix the way the markup is <em>generated</em>, because I will no doubt have to go back and tweak it several times.</p>
    <aside role="note">
      <p>And then, of course, I'm going to have to fix the visual styling, because it pairs with the markup I'm about to destroy.</p>
    </aside>
    <p>I started by cleaving out the code that generated the pages for type-specific entities and putting them into their own modules. There are only two of these, and they were a little over 300 lines (of Perl&#x2014;this is an antique, after all) apiece, plus a 450-line common ancestor. All these 1100-some lines of code do, more or less, is belch out some markup&#x2014;and very similar markup at that. I wrote this tool initially around the time I was experimenting with bucking the <abbr>MVC</abbr> paradigm, but had yet to perfect the alternative (which I wouldn't do until about five years later, in 2018 or so). What this meant was that there was too much detail happening in the markup generation, which made it cumbersome to change iteratively. As such, I ultimately decided to blow all of this away and create a universal stub function with no real structure to speak of, just a page with a list of literal data members and links to adjacent resources, along with embedded semantic metadata.</p>
    <aside role="note">
      <p>This is almost exactly how Intertwingler works, to the extent that everything I will be describing hereinafter can be picked up and plunked down on top of it with little to no modification. Indeed, it was this decision that transformed this little project from a dead-end task of rehabilitating some sclerotic old software with no chance of better than a mediocre outcome, into a much more valuable, albeit much more involved one.</p>
    </aside>
    <p>The <abbr>IBIS</abbr> tool can be understood as a special-case precursor to the generic piece of infrastructure that is Intertwingler. As I have mentioned many times in past missives, the <abbr>IBIS</abbr> tool is only <em>incidentally</em> useful. I wrote it for the express purpose of testing <a href="rdf-kv" rel="dct:references" title="RDF-KV">a protocol I designed</a>, to radically simplify the development of <abbr>RDF</abbr>-based Web applications by embedding graph manipulation operations directly into ordinary <abbr>HTML</abbr> form fields. <em>That</em> turned out to be easy. What turned out to be <em>hard</em>&#x2014;and ultimately took another decade, mostly <em>not</em> working on the problem&#x2014;was displaying that information <em>back</em>. This is because dispatching the right layout depends on a standard inferencing mechanism that has only been implemented in a handful of programming languages (not the one I was using) and would either demand a <em>huge</em> sunk cost to either write the missing piece (into a language that was past its prime), or rewrite all the <em>other</em> pieces into one of the languages that had it (which is what I ultimately did).</p>
    <p>This is ultimately why the <abbr>IBIS</abbr> tool was a dead end: I had to <em>fake</em> the inferencing code by writing out a bunch of mapping tables by hand. If I wanted to add another kind of entity or relation, this would explode combinatorially in the amount of mappings I would have to write. It was totally unsustainable, but if we can accept that the <abbr>IBIS</abbr> tool has all but served its purpose, we can retrofit it for one final tour before retirement.</p>
    <p>So that's what I did: I wrote up a few key routines that superficially resembled analogues in Intertwingler, and created something of a <em>cleavage plane</em> that would enable all subsequent work to be portable (or at least <em>near</em>-portable) between the <abbr>IBIS</abbr> tool and Intertwingler.</p>
    <p>The upgraded interface itself is actually a lot simpler than what it replaces. I had designed the original data entry interface with a JavaScript-free fallback but very quickly abandoned that as too quixotic. The replacement is actually something I dreamed up a long time ago but never implemented, again because prior to Intertwingler, it would have been a dead end.</p>
    <p>The original data entry <abbr>UI</abbr> was focused on adjacent <em>nodes</em>, as I had anticipated one would be connecting them together by more than one semantic relation at once, so I wanted to bundle that part together. From experience, it turns out that this doesn't actually happen all that often, so I unbundled the data entry <abbr>UI</abbr> to the parts of the screen that represent the semantic relations themselves.</p>
    <aside role="note">
      <p>This is one of the minority situations I would advocate prototyping over user research, because there was no precedent, and so nothing to interview users about. Not that I had any scope or budget to do such a thing.</p>
    </aside>
    <p>The next significant change was to get rid of the explicit toggle between <q>create new</q> and <q>connect existing</q> entities, which alternated between a text input and a (potentially gigantic) dropdown. I replaced it, provisionally, with the <code>&lt;datalist&gt;</code> construct that didn't exist when I originally wrote the tool. This meant that I only needed a single text input that would populate its value from the list if it matched, or otherwise afford the entry of a new entity.</p>
    <aside role="note">
      <p>I still don't totally love this solution because of the inconsistent way it represents in different browsers. I also had to get clever with the JavaScript, because the standardized behaviour is to always auto-insert the option <em>value</em> into the form field, even if what you matched was the <em>label</em>. This makes sense, I suppose, but to get it to behave consistently, I had to write some code that detected the autofill event (which was different across browsers!), tucked the matching node's <abbr>URL</abbr> into a hidden field, and replaced it with the label.</p>
    </aside>
    <p>That was surprisingly it for the <abbr>UI</abbr> upgrade. <a href="rdf-kv" rel="dct:references" title="RDF-KV">The <abbr>RDF-KV</abbr> protocol</a> did its job and made it <em>embarrassingly</em> easy to communicate with the server. I <em>did</em> have to rewrite the <a href="https://sass-lang.com/" rel="dct:references">Sass</a>, because it was easier than trying to decipher what was already there, but that wasn't that big a deal. There were a couple other entailments too, namely around the fact that I invoked my bastard trademark of using browser-side <abbr>XSLT</abbr> to transform <abbr>(X)</abbr><abbr>HTML</abbr> into itself. What I like about it&#x2014;and why I've been a resolute user of <abbr>XSLT</abbr> since I first picked it up in 2001, is that it is a high-performing, platform-agnostic, standard, declarative language that <em>only</em> operates over the information you give it, and it is incapable of outputting a syntax error. I extended this bastard toolkit, in 2016 and 2018 respectively, with <a href="https://github.com/doriantaylor/xslt-rdfa" rel="dct:references">a module for querying embedded <abbr>RDFa</abbr></a>, and <a href="https://github.com/doriantaylor/xslt-transclusion" rel="dct:references">another for seamlessly transcluding page components</a>. I like working this way, because it helps define what I need to make on the server side to feed into the template, and what kinds of entities and relations need to be represented in the system.</p>
    <aside role="note">
      <p>Lamentably, <abbr>XSLT</abbr> is something of an abandoned standard, despite being updated to a third version relatively recently. The browsers still, almost surprisingly, support the original 1.0 spec from 1999, which is considerably less powerful, but plenty powerful enough for most ordinary markup schlepping. I suspect this stagnation has to do with the loathing expressed by the Web development community for all things <abbr>XML</abbr>, and because, in all honesty, <abbr>XSLT</abbr> is a comically bulky language. The template I just wrote for the updated <abbr>IBIS</abbr> tool is over 2500 lines, which I guesstimate would be about a fifth of that length in any other language, because certain common constructs (like parameters, conditions) take up a ridiculous amount of syntactical real estate. Even <em>I</em> find this annoying. It's so damn useful though that I have been mulling designing a compact syntax that will transpile into the standard <abbr>XML</abbr> notation, similar to the one for <abbr>RelaxNG</abbr>.</p>
    </aside>
    <p>To reiterate, what the client wanted was a way to operate over <em>just</em> <abbr>SKOS</abbr> concepts. Even though at this point I had a <abbr>UI</abbr> that wasn't a total disgrace, I still needed to figure out an approach to deliver this that wasn't totally stupid. In the original cut of the tool, there is an implicit resource at the root address that is generated, and not part of the underlying graph. This happened to be an entity of class <code>ibis:Network</code> (which, incidentally, is a subclass of <code>skos:ConceptScheme</code>), a fact that was hard-coded into the markup-generating function for that location (and nowhere else). This, in addition to being a total hack that tied the address of the installation site to a particular object&#x2014;and by extension, a particular <em>representation</em> of that object&#x2014;it also wouldn't resolve in graph queries. This had to get resolved if I was going to give my client what he wanted.</p>
    <p>Here I get into a situation that I'm still not completely sure how to navigate, because until Intertwingler, I didn't have a <em>substrate</em> through which I could move quickly enough for feedback to be meaningful, so I have yet to establish a set of best practices. You can think of the <abbr>IBIS</abbr> tool as an early attempt on my part to marry <abbr>REST</abbr> (as Fielding originally conceived it) to <abbr>RDF</abbr>. What this implies is that every page is more than <em>just</em> a page, it's also a structured data object. Per <q>The Hypermedia Constraint</q> (erstwhile <dfn>Hypermedia as the Engine of Application State</dfn> or <abbr>HATEOAS</abbr>), the current representational state&#x2014;that is, whatever is at the location displayed at the top of your browser&#x2014;contains instructions for accessing subsequent states. The role <abbr>RDF</abbr> plays is in providing an extensible mechanism for expressing what those subsequent states <em>mean</em>. So the question you ask yourself when making an app or website in this style is less <q>what pages do we need?</q> and more <q>what <em>primitives</em> do we need?</q></p>
    <p>The mechanism that puts together the presentation the user ultimately <em>sees</em>, has all the information it needs to perform complex logic and fetch additional resources. If the resource in context is an instance of a particular class, then we can expect it to have certain members, whose values will <em>themselves</em> be instances of certain classes, which will imply certain members, and so on. Practical applications of this state of affairs include dispatching appropriate visual designs, conditional processing of interface elements, and the all-important reuse of content.</p>
    <p>The problem that I alluded to when you're trying to come up with primitives is you get <em>lacunae</em>: gaps of uncertainty between parts you're otherwise reasonably confident about. In <abbr>RDF</abbr>, classes and properties are registered in <dfn>schemas</dfn> and/or <dfn>ontologies</dfn>, a hair-splitting distinction smoothed over by the term <dfn>vocabulary</dfn>. Like anything else in the world of software, you either <em>find</em> these artifacts, or you <em>make</em> them. My own tendency is, if I can't find a a vocabulary in time that a) contains a useful-looking class or property and b) doesn't look abandoned, I rough in a provisional one with the entities I need, and gradually supplant them with more established ones if they start to look redundant. In this case, I made a <a href="https://vocab.methodandstructure.com/graph-tool#">Collaborative Graph Tool Ontology</a> (which I had actually started a year ago), intended to encode <em>only</em> the largely-presentational metadata associated with the tool <em>itself</em>, separate from its instance data.</p>
    <p>The strategy here was to change the root address of the tool to an instance of <code>cgto:Space</code>, which would have a special functional property called <code>cgto:focus</code> which would identify the main thing you were working on. (This relation, of course, can be overwritten at will.) Through this mechanism I can say: if the focus is a <code>skos:ConceptScheme</code>, only show the <abbr>UI</abbr> for <abbr>SKOS</abbr>; if it's an <code>ibis:Network</code>, then show the <abbr>IBIS</abbr> <abbr>UI</abbr> which extends it.</p>
    <p>Here is where I think I may have made a mistake, which I attribute to travelling over the holidays, so what may have been reasoned out in a day or two got smeared out over two weeks. For some reason my brain insisted that an <code>ibis:Network</code> had to have a <em>separate</em> <code>skos:ConceptScheme</code>, so if a concept scheme was in focus, there needed to be a way to unambiguously resolve the <q>canonical</q> <abbr>IBIS</abbr> network. I created a property <code>ibis:concepts</code> and functional subproperty <code>ibis:main-concepts</code> to articulate this, but even this was insufficient because there could still be more than one <code>ibis:Network</code> that nominated that particular <code>skos:ConceptScheme</code> as its main concepts. After wrestling with this for several days, it occurred to me finally that it was totally legal for a concept to belong to more than one concept scheme, and since <code>ibis:Network</code> is a subclass of <code>skos:ConceptScheme</code>, it would be sensible enough to add an <q>upgrade</q> button to your concept scheme in focus, which would turn it into an <code>ibis:Network</code>, and reveal the <abbr>IBIS</abbr> <abbr>UI</abbr>. </p>
    
  </body>
</html>
