<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Conrad Barski on Medium]]></title>
        <description><![CDATA[Stories by Conrad Barski on Medium]]></description>
        <link>https://medium.com/@conrad_9565?source=rss-da75e3799d70------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*ELmT7OK9SQeBEgjX-2bC8A.png</url>
            <title>Stories by Conrad Barski on Medium</title>
            <link>https://medium.com/@conrad_9565?source=rss-da75e3799d70------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 15:32:34 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@conrad_9565/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[The HORSE The Band Smart Contract is Live!]]></title>
            <link>https://medium.com/@conrad_9565/the-horse-the-band-production-contract-is-live-6fa8b74fdc80?source=rss-da75e3799d70------2</link>
            <guid isPermaLink="false">https://medium.com/p/6fa8b74fdc80</guid>
            <category><![CDATA[blockchain]]></category>
            <category><![CDATA[music-business]]></category>
            <category><![CDATA[cryptocurrency]]></category>
            <category><![CDATA[smart-contracts]]></category>
            <category><![CDATA[ethereum]]></category>
            <dc:creator><![CDATA[Conrad Barski]]></dc:creator>
            <pubDate>Fri, 23 Feb 2018 18:56:53 GMT</pubDate>
            <atom:updated>2018-02-23T19:24:05.789Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vjaw821hLC8aU0JKp9o2bw.png" /></figure><blockquote>Read about the all design details of this contract in <a href="https://medium.com/@conrad_9565/changing-the-music-industry-with-smart-contracts-in-the-small-dccaac4a6ab8">my previous post</a>.</blockquote><p><strong>The contract has officially been deployed into on the public ethereum blockchain. </strong>I have put in my full donation to support their new EP: <a href="https://etherscan.io/address/0x2e95978e1addf44d57467069e4fd947244b9dd5c">https://etherscan.io/address/0x2e95978e1addf44d57467069e4fd947244b9dd5c</a> (source on <a href="https://github.com/drcode/htb-multisig/blob/master/htb_multisig.sol">github</a>)</p><p>The four signatories for the production contract are <a href="https://www.linkedin.com/in/terrence-rossi-161677b2/">Terrence Rossi</a>, Disruption Joe from <a href="http://chicagoblockchainproject.com/">chicagoblockchainproject</a>, <a href="https://twitter.com/craigludington?lang=en">Craig Ludington</a>, and <a href="http://twitter.com/lisperati">Myself</a>.</p><p>When HTB’s next EP is mixed, the four of us have agreed to release 1/3rd of the contract’s funds (only 2 out of the four signatories are required). The remaining funds will be sent to the band members when this EP is officially released.</p><p><em>Note: The contract is already well funded, but if anyone wishes to additionally support this experiment and/or is a fan of HORSE The Band, feel free to send funds to </em><a href="https://etherscan.io/address/0x2e95978e1addf44d57467069e4fd947244b9dd5c">0x2e95978e1addf44d57467069e4fd947244b9dd5c</a> and your funds will automatically become part of this little experiment!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6fa8b74fdc80" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Making Procedural Animation Friendlier for Humans]]></title>
            <link>https://medium.com/@conrad_9565/making-procedural-animation-friendlier-for-humans-a986ea46f587?source=rss-da75e3799d70------2</link>
            <guid isPermaLink="false">https://medium.com/p/a986ea46f587</guid>
            <category><![CDATA[procedural]]></category>
            <category><![CDATA[clojurescript]]></category>
            <category><![CDATA[animation]]></category>
            <category><![CDATA[qlkit]]></category>
            <category><![CDATA[demo]]></category>
            <dc:creator><![CDATA[Conrad Barski]]></dc:creator>
            <pubDate>Sat, 17 Feb 2018 03:28:08 GMT</pubDate>
            <atom:updated>2018-02-17T04:08:39.203Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/480/1*96ojI7Y1KADeJmHIRYwxTQ.gif" /><figcaption>An experimental tool for animating objects with projectile physics, which you can<a href="http://lisperati.com/bounce/"> play with here</a></figcaption></figure><p>I’ve been thinking a lot about animation and have recently been wondering about how animation tools could be engineered to make the process easier. And, in my free time, I’ve been working on an animation framework (in clojurescript, using <a href="https://medium.com/@conrad_9565/lets-build-a-ui-with-qlkit-and-graph-queries-79b7b118ddac">qlkit</a>) inspired by the principles of functional programming — Watch our intro video at <a href="http://forwardblockchain.com/">forwardblockchain.com</a> and you can see some early animations I have created with my tools.</p><p><strong>I’ll be writing more in the future about how we might approach animation differently</strong> with the goal of exploring how animation can be made easier and also make it more natural to introduce precision into the animation process: I believe there would be a lot of benefit in creating new tools that can simplify the creation of animated scientific illustrations.</p><p>In this short post I want to share <a href="http://lisperati.com/bounce/">one extremely simple experiment</a> that you can play around with and which is one of many experiments I’ve built to help me think about the process of animation.</p><h3>Thinking In Snapshots</h3><p>The first step most animators will take when creating a new animation is to generate storyboards, like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vPWS-CZL4xLYeKJ9_qi9Jg.jpeg" /><figcaption>A storyboard for <em>The Radio Adventures of Dr. Floyd</em> episode #408 — <a href="https://en.wikipedia.org/wiki/Storyboard">via Wikipedia</a></figcaption></figure><p>Thinking in snapshots about an animation, as we see embodied in such storyboards, is a very natural way for humans to approach animation. And, because of this, most modern animation apps try to offer tools to translate storyboards into animation through keyframing, <a href="https://en.wikipedia.org/wiki/Key_frame">a technique that wikipedia describes as follows</a>:</p><blockquote>In software packages that support animation there are many parameters that can be changed for any one object. One example of such an object is a light. Lights have many parameters including light intensity, beam size, light color, and the texture cast by the light. Supposing that an animator wants the beam size of the light to change smoothly from one value to another within a predefined period of time, that could be achieved by using key frames. At the start of the animation, a beam size value is set. Another value is set for the end of the animation. Thus, the software program automatically interpolates the two values, creating a smooth transition.</blockquote><p>On the one hand, keyframes are great in that they complement the human conception of “snapshots” in time. On the other hand, keyframes (as they are usually implemented in modern animation tools) lack two properties that, in my view, still makes them a poor substitute for human notions of motion and time.</p><h4>Problem #1: Keyframes Break the Laws of Physics</h4><p>Suppose a human creates a storyboard of a basketball game with images of a ball passed between players:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*C4q-L_T6sJ2D711-2LOGGQ.png" /></figure><p>It is natural for a human creating a story to specify that the ball has to be passed between two players — But the animator (and their animation tools) are going to have to figure out the exact logistics whereby the ball moves from one player to the other. Typically, the animator will do this by specifying an animation path for the ball, along with keyframes and easing. If the animator wants the motion to appear natural, they will need to put a lot of effort into how the ball behaves at the end of the throwing arc, so that it appears to have a proper “weight” to it. In essence, the animator needs to painstakingly play the role of a “physics engine” and use complex keyframes to make the motion appear “real”.</p><p>The other alternative would be for the animator to eschew keyframes and rely directly on a physics engine to simulate the motion of the ball — However, it is <em>extremely</em> difficult, using a physics engine, to get an object to move in a predictable way… unfortunately, raw physics simulation can be very difficult to meld with typical human goals of storytelling, which requires objects to move in a predictable way from A to B.</p><h4>Problem #2: Keyframes Are Not Composable</h4><p>In computer programming, we call tools “composable” if they can be connected together in many different ways to produce interesting new results, and also can be disconnected at a future time without breaking individual components (i.e. they behave like “lego blocks”)</p><p>Keyframes, however, do not usually have this behavior. For instance, if our animator has created keyframes for the motion of the basketball, it will often be very difficult for the animator to make invasive changes to the animation after the fact- For instance, if the director says “replace the basketball with a bowling ball” or “make the players stand twice as far apart” the animator would likely need to throw away their keyframe animation and create a new one from scratch to create a new animation that remains convincing.</p><p>The lack of “composability” for keyframe animation also means that this technique forces the creation of a lot of “incidental mutable state” which is frowned upon by programmers who favor the functional programming style.</p><h3>One Approach to a Solution: Replacing Keyframes with “<em>Future Handles</em>”</h3><p>In the <a href="http://lisperati.com/bounce/">simple experiment I am sharing with this post</a>, I’ve implemented one possible solution to the problems described above — I’ve replaced the keyframes with something I’ll call <em>future handles</em>. Whereas a keyframe always exists at a specific point in time, <strong>future handles happen at any point in the future</strong>. The animation engine will guarantee that each of the handles will be reached — However, it is up to the animation tool (and indirectly, the animator) to decide what the rules are whereby the object reaches this future position, and how long it will take to get there.</p><p>In this way, it is possible to introduce realistic physical motion directly into an animation, while at the same time creating predictable destinations for objects. <strong>Essentially what my animation engine does is that it invents appropriate walls and floors for the ball to bounce off of in order to meet the conditions imposed by these handles, while maintaining correct physical properties of the ball whenever possible</strong><em>.</em> If, perchance, the engine guesses a wrong location for a wall, the animator can fix this by simply interposing an additional handle to give more guidance to the engine. As we’ve discussed, predictable destinations for objects makes it possible for animators to use their natural intuitions around “mental snapshots” and can potentially reduce the <em>cognitive load</em> on the animator as they attempt to tell a story in an animation.</p><p><em>It is important however to realize that issues around composability, precision, and “human-friendliness” in animation tools is a complex problem and in this post we have looked at only one tiny piece of a much larger set of solutions that would be necessary in resolving these issues on a larger scale. </em><strong><em>I hope to share many more ideas around the animation framework that I’m developing in the future- Stay tuned!</em></strong></p><p>Conrad Barski, CEO of <a href="http://forwardblockchain.com/">ForwardBlockchain</a><br>twitter: <a href="http://twitter.com/lisperati">@lisperati</a></p><p>Source code for this demo: <a href="https://github.com/drcode/bounce">https://github.com/drcode/bounce</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a986ea46f587" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Changing the Music Industry with Smart Contracts… In The Small]]></title>
            <link>https://medium.com/@conrad_9565/changing-the-music-industry-with-smart-contracts-in-the-small-dccaac4a6ab8?source=rss-da75e3799d70------2</link>
            <guid isPermaLink="false">https://medium.com/p/dccaac4a6ab8</guid>
            <category><![CDATA[blockchain]]></category>
            <category><![CDATA[smart-contracts]]></category>
            <category><![CDATA[ethereum]]></category>
            <category><![CDATA[wallet]]></category>
            <category><![CDATA[horse-the-band]]></category>
            <dc:creator><![CDATA[Conrad Barski]]></dc:creator>
            <pubDate>Wed, 07 Feb 2018 12:41:31 GMT</pubDate>
            <atom:updated>2018-02-23T21:12:21.388Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NsRmGoLQ9pFxQOc0RrCo0Q.png" /></figure><p>Update 2/23: <a href="https://medium.com/@conrad_9565/the-horse-the-band-production-contract-is-live-6fa8b74fdc80">The contract is now live</a>!</p><p>So one of my all time favorite bands is this band named <em>HORSE the band</em>; they’re kind of a cerebral thrash metal band with chiptune influences or…I don’t know, you decide what they are:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fw.soundcloud.com%2Fplayer%2F%3Furl%3Dhttp%253A%252F%252Fapi.soundcloud.com%252Ftracks%252F12937040%26show_artwork%3Dtrue&amp;url=https%3A%2F%2Fsoundcloud.com%2Fhorsetheband%2Fmurder&amp;image=http%3A%2F%2Fi1.sndcdn.com%2Fartworks-000006049205-os5a6h-t500x500.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=soundcloud" width="800" height="166" frameborder="0" scrolling="no"><a href="https://medium.com/media/751ee468da814fc1127d28f8740d43ef/href">https://medium.com/media/751ee468da814fc1127d28f8740d43ef/href</a></iframe><p><a href="https://soundcloud.com/horsetheband"><em>Lots more fantastic HTB on Soundcloud…</em></a></p><p>Perhaps not everyone’s cup of tea, but I love them! And hey, this is supposed to be the promise of the <em>information era</em>: People with obscure “long tail” interests are enabled to work together and expand the catalog of media in a multitude of different dimensions, be it literature, film, or music — even if we don’t all have the same tastes, in the aggregate we will all be better off if we can establish a universal environment open to experimentation and exploration.</p><p>Anyway, HTB hadn’t put out any material in a while — so I sent the band an email to let them know I wanted to donate some funds, no strings attached, but with the hope that <em>maybe, possibly</em>, we’d see some new HTB material one day. I told them I could send the donation in dollars or, if they were familiar with cryptocurrencies, I could also send them the funds in ether.</p><p>Here was their response:</p><blockquote>Wow. This is very nice of you. Thank you. :)</blockquote><blockquote>Dave and I discussed and we think the best thing to do would be for you to <strong>make a smart contract for us in ethereum</strong> that gets released when we release the 3 songs we’re in the middle of recording (and have been for the past year). That way me [Erik] and Nathan have an incentive to finish our parts.</blockquote><blockquote>Is that possible?</blockquote><blockquote>Thanks again man. What an awesome email to get!</blockquote><blockquote>Erik</blockquote><p>Much to my surprise, not only are they in the process right now of releasing new material for an EP, but they are also savvy about cryptocurrency… <em>and have already come up with a fantastic use case for Ethereum!!!</em></p><h3>Designing The Contract</h3><p>Alright, so at this point I had to design a contract with the desired capabilities requested by the band. For safety, I decided to base the contract on Christian Lundkvist’s security-first multisig contract, which he described in detail here:</p><p><a href="https://medium.com/@ChrisLundkvist/exploring-simpler-ethereum-multisig-contracts-b71020c19037">Exploring Simpler Ethereum Multisig Contracts</a></p><p>Using this multisig contract, I could designate 4 people as signatories for the funds, and enable a <em>2 out of 4</em> multisig process for performing actions in the contract. These signers will be tasked with attesting when the new album is fully mixed and also when it has been released.</p><p>Also, I had to perform two additional modifications to the contract to enable important new functionality.</p><h4>Modification #1: Payout in 3 Phases</h4><p>I decided that payouts to the contract would happen in three phases:</p><ol><li>The first phase will be a minimal amount of funds (&lt; 0.1 ETH) and will enable us to perform a live test of the signing process and the band member’s ethereum wallets before the bulk of the funds have been committed to the contract. When this phase completes successfully, I will submit my full donation into the contract.</li><li>After the album has been fully mixed, the signatories can unlock up to 1/3rd of the funds for the first milestone of the new album. This milestone will be unlocked when the band sends emails to the signatories notifying them that the mix is complete (i.e., we’re validating whether the mix is complete based on the honor system.)</li><li>After the album has been publicly released, the signatories will unlock the remaining funds.</li></ol><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4c462c63cfd37281fce7a78cfcb299e6/href">https://medium.com/media/4c462c63cfd37281fce7a78cfcb299e6/href</a></iframe><h4>Modification #2: Payout is Split Between Band Members with Hardcoded Destination Addresses</h4><p>Besides being able to release the funds based on milestones, smart contracts allow us to provide many other compelling benefits, especially if we design the final contract to only send payouts to hardcoded destination addresses. By combining multisig with hardcoded destination addresses (e.g., two specific addresses belonging to two of the band members) we give the band some important additional guarantees:</p><ul><li>There is a guarantee that the donation cannot be withdrawn due to a change of heart of the donating person.</li><li>It is impossible for the signatories to abscond with any funds.</li><li>It is also difficult for any signatories to extort the band for a portion of the funds. This is because the multisig process would require collusion of at least 3 of the 4 signatories to prevent a valid signature set from being supplied.</li></ul><p>Additionally, by having automatic splitting of the payout between multiple band member accounts, we can provide greater assurance to the band members that the funds will be distributed fairly; and, of course, the transparency of transactions on the public blockchain makes it easy for all band members to get accurate accounting of all funds.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/baa306127fb04f531c30659c3c4dc095/href">https://medium.com/media/baa306127fb04f531c30659c3c4dc095/href</a></iframe><h4>The Completed Contract</h4><p>The most recent version of the smart contract can now be <a href="https://github.com/drcode/htb-multisig/blob/master/htb_multisig.sol">viewed on github</a>, with all the planned modifications in place. <em>I would encourage any experienced smart contract developers to take a peek and let me know if they see any potential issues with the final code — I’ll wait at least a week before deploying this contract to take into account all feedback that is received from this medium post.</em></p><h3>Conclusion</h3><p>There are many opportunities for improving the music industry with ethereum smart contracts — it is an industry rife with inefficiencies and coordination problems. Hopefully large-scale ethereum projects, such as the promising <a href="https://ujomusic.com/">ujo project</a> and others, will be able to help solve these problems.</p><p>However, the promise of blockchain tech is that<strong> it is decentralized and that nobody needs to wait for “someone else” to solve all the big problems </strong>— Instead, each of us is already free to start using this tech at any point and in permission-less ways, to immediately solve small and highly personal problems. But mostly, it’s just super fun to try out new ideas!</p><p><em>Final Note: If you have interest in supporting this experiment, and simultaneously supporting HORSE the band’s new EP, please follow my </em><a href="http://twitter.com/lisperati"><em>twitter feed</em></a><em> where I will publish the address of the final, official contract once it is live. Any ether send to this contract will automatically augment the donation and will be similarly distributed to the band members based on the rules of the contract. At that point, I will also publicly announce the 4 official signatories.</em></p><p><strong>I am confident the contract will be live on the ethereum mainnet within the next week or two- Stay Tuned!</strong></p><p>Conrad Barski<br>CEO, <a href="http://forwardblockchain.com/">ForwardBlockchain.com</a><br>twitter: <a href="http://twitter.com/lisperati">@lisperati</a><br>email: lisperati@gmail.com</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=dccaac4a6ab8" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Let’s Build a UI with Qlkit and Graph Queries!]]></title>
            <link>https://medium.com/@conrad_9565/lets-build-a-ui-with-qlkit-and-graph-queries-79b7b118ddac?source=rss-da75e3799d70------2</link>
            <guid isPermaLink="false">https://medium.com/p/79b7b118ddac</guid>
            <category><![CDATA[graphql]]></category>
            <category><![CDATA[clojurescript]]></category>
            <category><![CDATA[clojure]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[Conrad Barski]]></dc:creator>
            <pubDate>Fri, 26 Jan 2018 12:07:09 GMT</pubDate>
            <atom:updated>2018-02-08T04:10:56.104Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-4JETz47ZvAxj4r-aesZNw.png" /></figure><p><em>by Conrad Barski, author of “Land of Lisp” &amp; CEO of </em><a href="http://forwardblockchain.com/"><em>ForwardBlockchain.com</em></a></p><p><a href="https://github.com/forward-blockchain/qlkit">Qlkit Github Page</a></p><blockquote><strong>Graph query language</strong> n.<em> A language for querying servers (and other types of remote entities, like databases) that has the shape of a graph. Examples include Facebook’s </em><a href="http://graphql.org/"><em>GraphQL</em></a><em>, Netflix’s </em><a href="https://netflix.github.io/falcor/"><em>Falcor</em></a><em>, and Cognitect’s </em><a href="http://docs.datomic.com/pull.html"><em>Datomic pull syntax</em></a><em>.</em></blockquote><p>In the last few years many large companies have started replacing RESTful server APIs with new types of APIs based on graph queries. However, graph queries can also be applied to other common development tasks, such as UI development.</p><blockquote>Note: Graph queries are often confused with graph databases, though these are two completely unrelated ideas: With a “graph database,” the <strong>data</strong> has the shape of a graph. With a “graph query language,” it’s just the <strong>query</strong> that has the shape of a graph — the data itself still retains the traditional, flat relational model that you are most likely very familiar with, both in the client browser and the server.</blockquote><p>The most well-known existing projects that have applied graph queries directly to UI development are Facebook’s <a href="https://facebook.github.io/relay/">Relay framework</a> and the <a href="https://github.com/omcljs/om/wiki/Quick-Start-%28om.next%29">Om Next framework</a>. Today our <a href="http://forwardblockchain.com/">company</a> released <a href="https://github.com/forward-blockchain/qlkit">Qlkit</a>, a ClojureScript web development framework with similar abilities that is heavily influenced by the design of both Om Next and Relay.</p><blockquote><a href="https://github.com/clojure/clojurescript">ClojureScript</a> is a dialect of Clojure that compiles into JavaScript and can be used in lieu of JavaScript for writing UI browser code.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2f0P9H1JHpr3MNQ3SKEWCQ.png" /></figure><p>Where our framework differs from Relay and Om Next is that qlkit is meant to be, above everything, lightweight, unsurprising, and minimalistic. We have made an effort to maintain all critical features of these other frameworks (and improved on some of the features) while keeping the size of the library as small as possible: Qlkit consists of around 300 lines of source code, while offering everything that is needed for a modern UI app: a transactional client state model, as well server-side functions for query resolution —Additionally, rich component rendering can be added as well with a small accessory library.</p><p>Qlkit attempts to meet the “simplicity” guidelines from the seminal talk <a href="https://www.infoq.com/presentations/Simple-Made-Easy">Simple Made Easy</a> in being a single-purpose tool that can be easily composed, understood, and modified.</p><p>In this post, I will walk through a browser-based app that has a UI and see <em>how simple code can be if all UI management tasks and all client-server communication are handled with a single uniform graph query language.</em></p><blockquote>While this post walks through an example written in the qlkit query language, the ideas in this post can also be applied to applications built on Om Next, Relay or Falcor.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Tr_NZSwDMlS2UzSdtoUoyw.png" /></figure><h3>A Basic Todo-List App</h3><p>For this example, we’re going to walk through the entire code for a basic todo list app, <a href="http://forwardblockchain.com/qlkit-todomvc/">available to try out here</a>. This app has the following features:</p><ul><li>It allows you to add new todo list items</li><li>It allows you to delete todo list items</li><li>It persists the items on a server</li><li>It implements basic multiuser safety <em>(i.e., items created on different browser clients are assigned temporary IDs that are replaced by server-generated IDs once persistence has completed)</em></li><li>It leverages modern UI components for a modern appearance <em>(in our case, we will leverage the </em><a href="http://www.material-ui.com/"><em>Material-UI</em></a><em> component library)</em></li></ul><h3>Self-Contained Components Through Graph Query Declarations</h3><p>Our todo list app consists of two qlkit components, named TodoItem and TodoList. Here is the code that implements the TodoItem component:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/45e534e8702b4151831ad48f95b138ec/href">https://medium.com/media/45e534e8702b4151831ad48f95b138ec/href</a></iframe><p>To declare a component in qlkit, we need to (at a minimum) declare its query ➊ and also a React-like render function ➋ that outputs a description of the UI for the component.</p><p>A TodoItem component in this example app will need to know two different things about a todo item: its text, and its ID, which is why the component requests these two pieces of data as “inputs” into the component ➊.</p><p>The render function receives two important arguments:</p><ol><li>The attributes of the component, which will be populated with the data the component requested. In this case, the attributes are the text of the component and the component’s ID.</li><li>The component-local state, which is a sort of “scratch pad” available to each component for writing the temporary state. We will discuss how to handle other, more permanent forms of state shortly.</li></ol><p>The render function simply returns an <a href="https://learnxinyminutes.com/docs/edn/">EDN</a> data structure that describes the HTML for the contents of the component. In this case, the component just returns a standard HTML li item ➌. However, you may notice that this list item also has some nonstandard attributes, such as :primary-text and :right-icon, which are made possible by extending qlkit through the qlkit-material-ui library, which transparently overrides the default li item with the more sophisticated <a href="http://www.material-ui.com/#/components/list">Material-UI ListItem component</a>.</p><p><em>(Note to advanced readers: The opinionated rendering algorithm and “DOM element sugar” demonstrated in this todo list app are completely optional and reside in separate packages named </em><em>qlikit-renderer and </em><em>qlkit-material-ui. Please take a look at this </em><a href="https://github.com/forward-blockchain/qlkit-todo-demo-raw"><em>alternative example</em></a><em> and you will see it is equally straightforward to build an app without this “fancy stuff” and which just uses default React rendering and </em><em>sablono.)</em></p><p>Because data is supplied to this component in a standardized way (through the query declaration) and data is output from this component in a standard way (the EDN data structure returned via the render function), it is very straightforward to unit test qlkit components: You simply need to compare inputs with outputs in each test. The strong isolation and testability of qlkit components persist even as components are nested.</p><p><em>(Another noteworthy piece of code in the declaration of the </em>TodoItem<em> component is the declaration of the </em>on-click<em> event </em>➍ <em>that is triggered when the user deletes the item. Ignore this code for now; we will discuss event handling in qlkit later.)</em></p><h3>The TodoList Component</h3><p>Here is the component that manages the entire TodoItem list and also lets users create new items:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0533b2e71269e06cd0b8a49296788ed1/href">https://medium.com/media/0533b2e71269e06cd0b8a49296788ed1/href</a></iframe><p>Most of this code (again, besides the events declarations) should be straightforward for anyone familiar with React. The main novel aspect of this code is that it implements nested components (i.e., a single TodoList can contain many TodoItem child components). Three lines in this component have logic in them that connects the TodoItems to the TodoList:</p><ul><li>In the query declaration it embeds the query of the child component ➊ — after all, a todo list component shouldn’t have to “know” what fields each todo item needs; that’s the job of the component that manages individual items. This means that the child component’s data needs are addressed in a way that’s invisible to the parent component.</li><li>In the todo list render function, the attributes passed in will contain not only the data for the parent component, but also the data that the child component requested: The qlkit-todo/todos value we’re extracting here ➋ contains both the data for the todo list and for all the todo items.</li><li>When the todo list returns its HTML elements, it can simply refer to the child component TodoItem by name, and qlkit will appropriately insert the child component ➌. All the parent component is responsible for is forwarding the attributes to the child (i.e., taking the data collected here ➋ and passing it on to the child component here ➌ via the todo variable.)</li></ul><p>By design, graph query languages are nestable: The final query at the top ➊ of this todo list component will look as follows:</p><pre> [[:qlkit-todo/todos {} [:db/id] [:todo/text]]]]</pre><p>As you can see, this final query combines the data needs of both the todo list and all the todo list items into a single query that essentially says “I need the whole todo list, and for every item in the todo list, I need to know its <em>ID</em> and its <em>text.</em>” The empty map {} indicates that there are no parameters in the query for todo items (i.e., that we want <strong>all</strong> todo items). <em>(A detailed description and full specification of the query language are included in our GitHub repo.)</em></p><h3>Adding a New Item to the Todo List</h3><p>OK, we’ve seen how a UI is generated with qlkit. Next, let’s take a look at how events are handled. To create a new todo item, we type the text into the input field, then store it as a permanent item when [enter] is pressed. Let’s review the code for TodoList to see how this happens:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0360bd3965e31bb7e36453e02360572e/href">https://medium.com/media/0360bd3965e31bb7e36453e02360572e/href</a></iframe><p>In the on-key-down event for the input field ➊, we check if the key code is 13 ➋ (the [enter] key), and in that case, we need to perform two actions:</p><ul><li>We trigger an application-wide transact! event ➌, which will (in subsequent steps) place the new item into the application-wide client state, as well as into server storage. The arguments to the transact! event are similarly in the form of a graph query: The query performs a mutation called :todo/new! (the exclamation mark indicates that it’s a mutation), which qlkit will forward to query parsing functions, as we will see shortly.</li><li>Every time a new item is created, we need to clear out the input box again to ready it for another item. We accomplish this by erasing the text from component-local state here ➍. This will automatically clear out the component, based on <a href="https://reactjs.org/docs/forms.html#controlled-components">React’s “controlled component” mechanism</a>.</li></ul><p>Here is what the graph query will look like for the part of the transaction that generates the new todo item:</p><pre>[:todo/new! {:db/id     &quot;ff81aa5b-9cde-4399-a2fd-d648e8ef9503&quot;<br>             :todo/text &quot;Iron the curtains&quot;}]</pre><p>In this query, the query command of :todo/new! is followed by a map of parameters that describes things about the new todo item we’re adding. To add a new todo item, we need to first have a temporary unique identifier for the todo item, which is stored under the key :db/id here. (This ID will be replaced by a friendlier long-term identifier once we’ve managed to synchronize with the server). Additionally, we pass in the text for the todo as :todo/text.</p><p>So… what happens to this transaction once it’s submitted? To find out, let’s look at how a qlkit component (in this case, our TodoList component) gets mounted to the DOM tree:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ee3c0f57a25e6795cf56d097d2c35e53/href">https://medium.com/media/ee3c0f57a25e6795cf56d097d2c35e53/href</a></iframe><p>As we mount the component to the DOM tree, we have the ability to set various options for how this component is connected to the outside world. First of all, we link it to the application-wide state atom that will represent the main client-side application state, in this case a Clojure atom named app-state ➊. Also, we assign a remote handler function ➋ — this is a function that we will see later that is responsible for forwarding graph queries to the remote server, the final storage destination for our todo items.</p><p>The last option we set as we mount our component is to link it to a bunch of parsing functions that we’ve written ➌. These parsing functions parse all the graph queries that originate from our components — this includes queries generated by the TodoList, the TodoItems, and whatever other components our app requires. Qlkit allows you to create four different types of parser functions on the client side:</p><ul><li>read parsers: These handle queries that read information out of the client-side application state.</li><li>mutate parsers: These handle queries that modify the client-side application state.</li><li>remote parsers: These parse queries to determine whether they require information (or involve mutations) that need to be communicated to the remote server.</li><li>sync parsers: Once a server has engaged with a query, these parsers subsequently update the client-side application state based on the results received from the server.</li></ul><h3>Writing a Mutate Parser</h3><p>Here again is the query we’re currently working to satisfy to create a new todo item:</p><pre>[:todo/new! {:db/id     &quot;ff81aa5b-9cde-4399-a2fd-d648e8ef9503&quot;<br>             :todo/text &quot;Iron the curtains&quot;}]</pre><p>We’ll want to update the client-side application state to add this todo item, even before the server has processed the new todo item. This is good practice, since it will give the user rapid feedback, regardless of what happens to the item on the server, which we will deal with later. (Updating the UI before a transaction has been fully processed is commonly referred to as an “optimistic UI update.”)</p><p>As we discussed, updating the client state based on a mutation query is accomplished by defining a mutate parser:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/42dab7b137cc12c81ad50df79ae09908/href">https://medium.com/media/42dab7b137cc12c81ad50df79ae09908/href</a></iframe><p>This parser receives three arguments: The query, an environment variable, and a reference to the state atom ➊. Right away we extract the parameters (i.e., theparams variable) from the query term, which will be found in the second slot. Next, we pull the todo item’s ID out of the parameters ➋. Finally, we update the application’s state by inserting the new todo item into it ➌.</p><p>With qlkit it is recommended that you keep your client application state flat and normalized, just as if it were a traditional database. Here is what the structure of the app state in our sample app would look like <em>before</em> we add the new item:</p><pre>{:todo/by-id {<strong>0</strong> {:todo/text &quot;Walk the dog&quot;<br>                 :db/id     0}<br>              <strong>1</strong> {:todo/text &quot;Pay the bills&quot;<br>                 :db/id     1}}}</pre><p>As you can see, the state contains a single item called :todo/by-id, which holds the collection of todo items, keyed by their ID. After the mutate parser executes, the state will look like this:</p><pre>{:todo/by-id {<strong>0</strong> {:db/id     0<br>                 :todo/text &quot;Walk the dog&quot;}<br>              <strong>1</strong> {:db/id     1<br>                 :todo/text &quot;Pay the bills&quot;}}<br>              <strong>&quot;ff81aa5b-9cde-4399-a2fd-d648e8ef9503&quot;</strong><br>                {:db/id     &quot;ff81aa5b-9cde-4399-a2fd-d648e8ef9503&quot;<br>                 :todo/text &quot;Iron the curtains&quot;}}</pre><p>The new todo is now in the local state as we wanted, though it still uses a lengthy, collision-resistant temporary ID.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uKNHvDJpcbrAxyQh9JXafA.png" /></figure><h3>Rerendering the UI</h3><p>After executing this parser function, qlkit will trigger a rerender of the entire component tree. (This is one significant difference between qlkit and Om Next, since Om Next goes to considerable effort to optimize for only partial rerenders of the component tree — However, React will still optimize the rerendering in qlkit in the usual way, so qlkit apps still have performance comparable to vanilla React apps.) To do this, qlkit will refetch the data for the components from the local client state, which, if you remember, is defined as the following query:</p><pre>[[:qlkit-todo/todos {} [:db/id] [:todo/text]]]]</pre><p>In processing this query, it will trigger the following read parser, which as we discussed is responsible for resolving reads of the local state:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b9e88dde584cca8e750787d7a0f1dcbe/href">https://medium.com/media/b9e88dde584cca8e750787d7a0f1dcbe/href</a></iframe><p>Like components, parsers can also be nested: Just as we don’t want the TodoList component to have to worry about the “dirty details” of individual todo components, we don’t want the :qlkit-todo/todos parser to have to worry about the “dirty details” of parsing the query for an individual todo item.</p><p>As you can see, read parser arguments ➊ look the same as mutate parser arguments, with the sole exception that the state is passed in as a <em>value</em>, not a <em>reference</em> to the state atom: This ensures that a read parser doesn’t accidentally mutate the state.</p><p>Just as with the mutate parsers, read parsers support parameters in the query. In this case, the parser for :qlkit-todo/todos is written to handle both default <em>and</em> parameterized versions of the query:</p><pre>;;default query</pre><pre>[[:qlkit-todo/todos <strong>{}</strong> ...]]]</pre><pre>;;parameterized query</pre><pre>[[:qlkit-todo/todos <strong>{:todo-id 123}</strong> ...]]]</pre><p>In other words, this parser can address the query “Give me all the todo items” as well as the query “Give me the todo item with ID=123.” We will see later why it’s beneficial to handle both cases. To see if the :todo-id was provided, we check to see if it was defined in the parameters ➋ ➌. If the todo-id was specified, we hand over control to other parsers that know how to supply data for this individual todo item. This is accomplished by calling the qlkit function parse-children , which triggers all the query terms that are children of the current query ➍. Otherwise, we want to return all todo items and therefore start looping over the items ➎. For each item, we again hand over control to other parsers ➏.</p><p>Of course, the parsers for the child queries can function only if they are given enough context to do their job: It’s impossible to answer a vague question like “Give me the text of the todo item.” Instead we need to give more context, i.e. “Give me the text of the todo item with ID=123.” Therefore, before the parser initiates parsing of the inside of the query, it attaches the current :todo-id to the parsing environment ➏, which the child parsers will be able to reference.</p><p>Each term in a query has a parser defined for it. Here is the parser that handles the :todo/text term:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/55d1432f33051008eb0764ca86dabb5e/href">https://medium.com/media/55d1432f33051008eb0764ca86dabb5e/href</a></iframe><p>It’s a simple parser: It just grabs the todo-id out of the environment ➊ (the one we attached in the earlier step) and then gets the text for the todo item with this ID out of the application state ➋.</p><p>Our todo app now has all the information needed to refresh the UI, causing the new todo item to appear on the screen!</p><h3>Persisting the New Todo Item on the Server</h3><p>Often, the data we’re reading (or modifying) lives in the client-side application state of our application. However, other times it is on the server. In order to indicate what part of a query needs to be sent to the server (such as the todo items that are persisted on the server) we define a remote parser. Here is the remote parser that is triggered by the :todo/new! query term in our transaction:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e70c7407faed09a59a2db9628006bdbe/href">https://medium.com/media/e70c7407faed09a59a2db9628006bdbe/href</a></iframe><p>Every remote parser takes a query term and the state as a parameter and then simply returns a new query, consisting of the version of the query that needs to be sent to the server. In the case of :todo/new!, we simply forward the existing, unmodified query to the server. In essence, our app is saying “Every time a new item is added to the todo list, let the server know about it.”</p><p>As we will see later, it is also common for a query to be trimmed before sending it to the server (when we want to say “These parts of the query can be satisfied by the client, but these other parts need to be sent to the server”). Other times it makes sense to add additional information to the query before sending it to the server (for instance, if we want to say something like “Before sending the todo item to the server, add a parameter to the query to indicate that the item was added by user bobsmith29, so the server can take this fact into account.”)</p><h3>Communicating Between the Client and Server</h3><p>Graph queries are a replacement for the more traditional RESTful web services methodology. With graph queries, the client app has richer mechanisms for describing the types of reads and mutations that need to be performed on the server, and multiple nested reads and mutations can be bundled in a single server round trip.</p><p>When you declare a graph query API for your server in qlkit, you only set up a single server endpoint, which is very different from a traditional RESTful API, which typically has many endpoints. <em>(Note: If you need to connect to a RESTful service for legacy reasons, qlkit can support that as well — just use your preferred client-server logic and make sure the client state atom is updated appropriately.)</em></p><p>Here is the entirety of the browser-side ClojureScript code in our app for communicating with the server:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9a976a52e06d40051575dba28981c906/href">https://medium.com/media/9a976a52e06d40051575dba28981c906/href</a></iframe><p>This function was assigned to our root qlkit component when it was mounted to the DOM tree. The function is called every time qlkit determines that a remote query needs to be sent to the server, which is accomplished by POSTing the query to a single server endpoint named “endpoint” ➊. <em>(Note that this code is wrapped in a </em><em>go block supplied by </em><em>cljs.core.async, a library that implements Golang-style channels in ClojureScript. This is how asynchronous requests are usually handled in ClojureScript, in lieu of the “promises” that may be more familiar to the JavaScripters among you.)</em></p><p>When the server has completed processing the request, we first check that the request was handled successfully ➋ and if so, we read the result stored in the response and send it back to qlkit via a callback ➌, so that the data from the server can be incorporated in the client-side application state.</p><p>Here, on the flip side, is the entirety of our app’s server code for handling client requests:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0cd8ed1d906f260e78abeaa294f7d9a7/href">https://medium.com/media/0cd8ed1d906f260e78abeaa294f7d9a7/href</a></iframe><p>All this request handler function does is read the graph query out of the request ➊, attempt to parse the query ➋, and then return the result of the parsing back to the client ➌.</p><p>In fact, the parsing of qlkit queries on the server makes use of the <em>same</em> library code as on the client, since Clojure and ClojureScript can share code: The same qlkit functions run on both the client and the server. On the server we therefore again use read parsers and mutate parsers to parse and satisfy queries. Here is the :todo/new! parser function on the server:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6dfffd63911286ead06f1b344ddd9b20/href">https://medium.com/media/6dfffd63911286ead06f1b344ddd9b20/href</a></iframe><p>To keep this example app simple, the server simply stores the master list of todo items in a server-side state atom — in a “real” app, these items would of course be stored in some sort of database, instead.</p><p>First, this function extracts the todo item’s temporary ID and text from the query ➊. One important job of the server-side :todo/new! parser is to now assign a permanent ID to each todo item, which it accomplishes in a concurrency-safe manner by generating a new integer using a sequencer ➋. Next, it stores the todo item with server-side state, <em>using the permanent ID </em>➌. Finally, it returns a vector, which associates the temporary ID with the permanent ID ➍: This will be sent back to the client to let it know about this new ID.</p><p>It should be noted that in qlkit (as in Om Next), it is usually a bad idea to return information from a mutation. Instead, mutations are usually followed by a read query term that is sent to the server as part of the same query. For many reasons we won’t go into here, this will lead to cleaner code when using a graph query architecture.</p><p>However, there are two exceptions to this rule (i.e., two situations in which it is appropriate to have a graph query mutation return a value):</p><ol><li>In order to associate temporary IDs with permanent IDs (as we did in the mutate parser above ➍).</li><li>In order to report meta-level information about the mutation (such as whether it was successful or how long it took).</li></ol><h3>Handling the Response From the Server</h3><p>After the server is done handling the new todo item, it sends the following response back to the client:</p><pre>[[&quot;ff81aa5b-9cde-4399-a2fd-d648e8ef9503&quot; 2]]</pre><p>As we can see, this response lets the client know that the item’s temporary ID has been replaced by the server with an ID of 2. The client now needs to parse the original remote query, along with this result information, to properly update the client-side application state so that it matches the data maintained by the server. We do this by with the final type of parser function, the sync parser function:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/08b0e8894b6f5473a37dc338ea216274/href">https://medium.com/media/08b0e8894b6f5473a37dc338ea216274/href</a></iframe><p>A sync method receives the server results as an argument ➊— in this case it is receiving the temporary and permanent ID from the server’s :todo/new! mutation function, which we extract next ➋.</p><p>The job of the sync function is to update the client application state with the information from the server ➌. If you don’t use Clojure/ClojureScript regularly, the arguments to the swap! command here will likely look a bit daunting, but all that’s happening here is that all references in the state to the old temporary ID are being replaced with the permanent ID. You can see in this line ➍ how the temporary ID is being dissociated from the todo list. Finally a replacement item is inserted with the permanent ID ➎. The client application state is now fully in sync with the information on the server.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1019/1*65YiqsX09EGRvznu9I85nw.png" /></figure><h3>Deleting a Todo Item: Unlocking the Power of Graph Queries Through Nesting!</h3><p>Because creating a new todo item is a “top-level” concern in our example app, the logic that we’ve looked at so far for routing the creation event for updating the server have been simple enough that they could have been handled just as easily using traditional methods (such as a more traditional component library and a traditional RESTful interface).</p><p>Graph queries really shine in situations where there is significant “local context” that influences actions in an application (or influences what data needs to be returned from a server request). In this next part, we’re going to look at the steps for deleting an existing item. This will allow us to see, with greater clarity, the benefits of building UIs with graph queries.</p><p>Here, again, is the code for our TodoItem component:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/fc8e15e9b02332cbca2bde9759df750b/href">https://medium.com/media/fc8e15e9b02332cbca2bde9759df750b/href</a></iframe><p>When the user clicks on the right “delete” icon, an on-click event handler is triggered ➊. This event triggers a :todo/delete!transaction ➋. This transaction appears very straightforward, but an observant reader may ask the following question: “Nowhere in this transaction is it specified <em>which</em> todo item is to be deleted. How will qlkit know how to properly process this transaction?” If you are used to a more traditional UI library (such as a library modeled on React Flux), you would expect the transaction to specify the ID of the item. However, with qlkit we can perform a “naked” deletion transaction ➋ where no ID is specified. In essence, with qlkit the component can say “When the user clicks the icon, please delete me” without having to reference an explicit ID. This is made possible through a feature of qlkit (also borrowed from Om Next) called “component-local transactions,” which helps to keep components fully isolated from external concerns.</p><h3>How Component-Local Transactions Are Handled by Qlkit</h3><p>When qlikit.core/transact! is called, qlkit works behind the scenes to determine what the current component is. Qlkit can determine where in the current UI component hierarchy this component resides and automatically expands the graph query in the transaction to reflect the context of the component.</p><p>For instance, here is the query we passed into transact! :</p><pre>[:todo/delete!]</pre><p>Qlkit will automatically convert this component-local query into a global query that looks as follows:</p><pre>[:qlkit-todo/todos {:todo-id 2} [:todo/delete!]]</pre><p>As you can see, in generating the global query, the precise context of the todo item becomes clear: We are deleting the todo item with todo-id = 2. Qlkit accomplishes this feat by keeping track of the environment each component was created in, which is a feature we call <em>autoparamaterization.</em> Read our <a href="https://github.com/forward-blockchain/qlkit/wiki/Qlkit-FAQ#what-is-autoparameterization">FAQ for more info on this feature</a>. In the case of a todo item, it exists in the context of a list of todo items, via the :qlkit-todo/todos read parser we saw earlier:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b4a4fc68ab9f2341edae78b4b00ceadd/href">https://medium.com/media/b4a4fc68ab9f2341edae78b4b00ceadd/href</a></iframe><p>As you may remember, we added a :todo-id to the environment before calling the parsers for each individual item in the todo list ➊. Qlkit keeps track of these additions to the environment and automatically adds them as parameters when component-local transactions are executed.</p><h3>Deleting the Todo Item from the Client Application State</h3><p>As we’ve seen, deleting an item generates the following transaction that will need to be executed to update information on both the client and the server:</p><pre>[:qlkit-todo/todos {:todo-id 2} [:todo/delete!]]</pre><p>We already know how the :qlkit-todo/todos term is handled on the client: It calls the same read parser that we were just looking at:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e6d3c14498a7f9ecc63b8e90c4c3e90a/href">https://medium.com/media/e6d3c14498a7f9ecc63b8e90c4c3e90a/href</a></iframe><p>Importantly, remember we discussed that this read parser can handle both default and <em>parameterized</em> versions of the query- In other words, it extracts the todo-id out of the parameters ➊ and then only parses queries for children if they have the correct ID. This is critical, since in this case we’re going to be calling :todo/delete! against todo items, and we certainly don’t want to delete the entire list of items!</p><p><em>(Notice how the same </em><em>:qlkit-todo/todos read parser that adds the </em><em>:todo-id to the environment is also the same function that needs to make use of the same parameter, at a future point in time. Our </em><a href="https://github.com/forward-blockchain/qlkit/wiki/Qlkit-FAQ#what-is-autoparameterization"><em>autoparameterization</em></a><em> feature completely shields the remainder of the app from having to “know” about this implementation detail.)</em></p><p>Once an item with the correct ID is found, the :todo/delete! mutation is called:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/59bfbb178ce385372455311f1dac149e/href">https://medium.com/media/59bfbb178ce385372455311f1dac149e/href</a></iframe><p>This is a very simple function; it simply optimistically dissociates the todo item from the client application state ➊. Next, we need to delete this item from the server.</p><h3>Deleting the Todo Item from the Server</h3><p>The remote parser for our deletion transaction is also very simple:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ac4c10dc7261176d3ccce4ef3138c8d7/href">https://medium.com/media/ac4c10dc7261176d3ccce4ef3138c8d7/href</a></iframe><p>It simply says “Any query that deletes an item needs to also be sent to the server.”</p><p>Consequently, the request we send to the server is unchanged (though it also gets a pass through the :qlkit-todo/todos remote parser, which also leaves it unchanged). It looks as follows:</p><pre>[:qlkit-todo/todos {:todo-id 2} [:todo/delete!]]</pre><p>On the server side, this query is again fed through read and mutation parsers, starting with the <em>server-side version</em> of the :qlkit-todo/todos parser:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b53ba89dbcc0d5fef114e30f2a5cc943/href">https://medium.com/media/b53ba89dbcc0d5fef114e30f2a5cc943/href</a></iframe><p>This does the same thing as on the client, extracting the todo-id from the parameters ➊ (which may or may not be present, depending on whether this is the default or parameterized version of the query) and then parses the child todo item queries, as appropriate for the todo-id ➋.</p><p>Finally, this triggers the todo/delete! mutation on the server, to fully erase the todo item from existence:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/04e4301bc842f039ebfe46e284421146/href">https://medium.com/media/04e4301bc842f039ebfe46e284421146/href</a></iframe><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dhV-aTg4-0815e5QnTXobw.png" /></figure><h3>Conclusion</h3><p>A UI framework that relies on graph queries (as qlkit does) makes it possible to have a deeply nested component hierarchy with complex data requirements that may overlap between multiple components. Qlkit makes it possible to tame such complex UIs in the following way:</p><ul><li>Each UI component can ask for data (and perform mutations against data) in a very natural way. The component’s queries can pretend that the data is stored in a layout that exactly mirrors the UI hierarchy. This prevents any errant data transformation logic from “creeping into” your UI code.</li><li>The “hard work” in the app happens in the parsers: These translate the root query from the UI (which, by the nature of a UI, is tree-shaped) into actions against the local state, against the server, or both.</li><li>The client-side application state can be kept flat and fully normalized (i.e., the state doesn’t have to contain any duplication of data, even if multiple UI components need to access the same UI data).</li><li>Each UI component has a <em>context</em> in which it exists, but the component does not need to burden itself with the details of the context. The framework ensures that any actions the component takes (in the form of transactions) are expressed in an appropriate way that specifies the context.</li><li>Similarly, the locally processed queries and server queries need to have different <em>contexts</em>: A developer can write remote parsers that cleanly express rules such as “This is something only the client cares about; don’t send it to the server” or “The server needs to know this extra thing about the query (i.e., this extra parameter) before it is sent the query.”</li></ul><p>The graph query system in qlkit completely insulates components from the implementation details of the data, which may live on the client or server or partially on both. Additionally, graph queries allow for highly efficient, atomic, and batched server requests that can be kept in sync with client-side data in a very predictable way. Given the benefits of graph queries for UI development, we at ForwardBlockchain hope that the release of our new qlkit library can help, either directly or indirectly, to increase the adoption of graph-query-based UI frameworks across the industry!</p><blockquote><em>Qlkit definitely is a philosophical descendant of Om Next and we want to thank David Nolen, the author of Om Next, for his inspiration. We also want to thank the other folks who have provided valuable resources to the Om Next community, including António Monteiro and Tony Kay. Additionally, I’d like to thank Craig Ludington, Bruce Hauman for their work on the qlkit codebase, and </em>Sophie Michals for feedback on this post.</blockquote><p>- Conrad Barski</p><p>Conrad’s Twitter: <a href="http://twitter.com/lisperati">@lisperati</a><br><a href="https://github.com/forward-blockchain/qlkit">Qlkit Github Page</a><br><a href="http://forwardblockchain.com/">ForwardBlockchain.com</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=79b7b118ddac" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>