<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: NoRedInk</title>
    <description>The latest articles on DEV Community by NoRedInk (@noredink).</description>
    <link>https://dev.to/noredink</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F417%2Ff313561f-dd61-40e9-a0f6-b6079e173732.jpg</url>
      <title>DEV Community: NoRedInk</title>
      <link>https://dev.to/noredink</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/noredink"/>
    <language>en</language>
    <item>
      <title>Haskell for the Elm Enthusiast</title>
      <dc:creator>Jasper Woudenberg</dc:creator>
      <pubDate>Tue, 03 Aug 2021 15:40:01 +0000</pubDate>
      <link>https://dev.to/noredink/haskell-for-the-elm-enthusiast-ckm</link>
      <guid>https://dev.to/noredink/haskell-for-the-elm-enthusiast-ckm</guid>
      <description>&lt;p&gt;&lt;em&gt;This post was co-authored by Michael Glass, Stöffel, and myself. It first first appeared on &lt;a href="https://blog.noredink.com/post/658510851000713216/haskell-for-the-elm-enthusiast" rel="noopener noreferrer"&gt;the NoRedInk blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Many years ago NRI adopted Elm as a frontend language. We started small with a disposable proof of concept, and as the engineering team increasingly was bought into Elm being a &lt;em&gt;much&lt;/em&gt; better developer experience than JavaScript more and more of our frontend development happened in Elm. Today almost all of our frontend is written in Elm.&lt;/p&gt;

&lt;p&gt;Meanwhile, on the backend, we use Ruby on Rails. Rails has served us well and has supported amazing growth of our website, both in terms of the features it supports, and the number of students and teachers who use it. But we’ve come to miss some of the tools that make us so productive in Elm: Tools like custom types for modeling data, or the type checker and its helpful error messages, or the ease of writing (fast) tests.&lt;/p&gt;

&lt;p&gt;A couple of years ago we started looking into Haskell as an alternative backend language that could bring to our backend some of the benefits we experience writing Elm in the frontend. Today some key parts of our backend code are written in Haskell. Over the years we’ve developed our style of writing Haskell, which can be described as very Elm-like (it’s also still changing!).&lt;/p&gt;

&lt;h2&gt;
  
  
  🌳 Why be Like Elm?
&lt;/h2&gt;

&lt;p&gt;Elm is a small language with great error messages, great documentation, and a great community. Together these make Elm one of the nicest programming languages &lt;em&gt;to learn&lt;/em&gt;. Participants in an &lt;a href="https://github.com/elmbridge/curriculum" rel="noopener noreferrer"&gt;ElmBridge&lt;/a&gt; event will go from knowing nothing of the language to writing a real application using Elm in 5 hours.&lt;/p&gt;

&lt;p&gt;We have a huge amount of Elm code at NoRedInk, and it supports some pretty tricky UI work. Elm scales well to a growing and increasingly complicated codebase. The compiler stays fast and we don’t lose confidence in our ability to make changes to our code. You can learn more about our Elm story &lt;a href="https://www.youtube.com/watch?v=DoA4Txr4GUs" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  📦 Unboxing Haskell
&lt;/h2&gt;

&lt;p&gt;Haskell shares a lot of the language features we like in Elm: Custom types to help us model our data. Pure functions and explicit side effects. Writing code without runtime exceptions (mostly).&lt;/p&gt;

&lt;p&gt;When it comes to ease of learning, Haskell makes different trade-offs than Elm. The language is much bigger, especially when including the many optional language features that can be enabled. It’s entirely up to you whether you want to use these features in your code, but you’ll need to know about many of them if you want to make use of Haskell’s packages, documentation, and how-tos. Haskell’s compiler errors typically aren’t as helpful as Elm’s are. Finally, we’ve read many Haskell books and blog posts, but haven’t found anything getting us from knowing no Haskell to writing a real application in it that’s anywhere near as small and effective as the &lt;a href="https://guide.elm-lang.org/" rel="noopener noreferrer"&gt;Elm Guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏟️ When in Rome, Act Like a Babylonian
&lt;/h2&gt;

&lt;p&gt;Many of the niceties we’re used to in Elm we get in Haskell too. But Haskell has many additional features, and each one we use adds to the list of things that an Elm programmer will need to learn. So instead we took a path that many in the Haskell community took before us: limit ourselves to a subset of the language.&lt;/p&gt;

&lt;p&gt;There are many styles of writing Haskell, each with its own trade-offs. Examples include Protolude, RIO, the lens ecosystem, and many more. Our approach differs in being strongly inspired by Elm. So what does our Elm-inspired style of writing Haskell look like?&lt;/p&gt;

&lt;h3&gt;
  
  
  🍇 Low hanging fruit: the Elm standard library
&lt;/h3&gt;

&lt;p&gt;Our earliest effort in making our Haskell code more Elm-like was porting the Elm standard library to Haskell. We’ve open-sourced this port as a library named &lt;a href="https://hackage.haskell.org/package/nri-prelude" rel="noopener noreferrer"&gt;&lt;code&gt;nri-prelude&lt;/code&gt;&lt;/a&gt;. It contains Haskell counterparts of the Elm modules for working with &lt;code&gt;String&lt;/code&gt;s, &lt;code&gt;List&lt;/code&gt;s, &lt;code&gt;Dict&lt;/code&gt;s, and more.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://hackage.haskell.org/package/nri-prelude" rel="noopener noreferrer"&gt;&lt;code&gt;nri-prelude&lt;/code&gt;&lt;/a&gt; also includes a port of &lt;code&gt;elm-test&lt;/code&gt;. It provides everything you need for writing unit tests and basic property tests.&lt;/p&gt;

&lt;p&gt;Finally, it includes a GHC plugin that makes it so Haskell’s default &lt;code&gt;Prelude&lt;/code&gt; (basically its standard library) behaves like Elm’s defaults. For example, it adds implicit qualified imports of some modules like &lt;code&gt;List&lt;/code&gt;, similar to what Elm does.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎚️ Effects and the Absence of The Elm Architecture
&lt;/h3&gt;

&lt;p&gt;Elm is opinionated in supporting a single architecture for frontend applications, fittingly called The Elm Architecture. One of its nice qualities is that it forces a separation of application logic (all those conditionals and loops) and effects (things like talking to a database or getting the current time). We love using The Elm Architecture writing frontend applications, but don’t see a way to apply it 1:1 to backend development. In the F# community, they use the Elm Architecture for &lt;em&gt;some&lt;/em&gt; backend features (see: &lt;a href="https://safe-stack.github.io/docs/feature-clientserver-bridge/#when-to-use-elmishbridge" rel="noopener noreferrer"&gt;When to use Elmish Bridge&lt;/a&gt;), but it’s not generally applicable. We’d still like to encourage that separation between application logic and effects though, having seen some of the effects of losing that distinction in our backend code. Read our other post &lt;a href="https://blog.noredink.com/post/657392972659310592/pufferfish-please-scale-the-site" rel="noopener noreferrer"&gt; Pufferfish, please scale the site!&lt;/a&gt; if you want to read more about this.&lt;/p&gt;

&lt;p&gt;Out of many options we’re currently using &lt;a href="https://jaspervdj.be/posts/2018-03-08-handle-pattern.html" rel="noopener noreferrer"&gt;the handle pattern&lt;/a&gt; for managing effects. For each type of effect, we create a &lt;code&gt;Handler&lt;/code&gt; type (we added the extra &lt;code&gt;r&lt;/code&gt; in a typo way back and it has stuck around. Sorry). We use this pattern across our libraries for talking to outside systems: &lt;a href="https://hackage.haskell.org/package/nri-postgresql" rel="noopener noreferrer"&gt;&lt;code&gt;nri-postgresql&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://hackage.haskell.org/package/nri-http" rel="noopener noreferrer"&gt;&lt;code&gt;nri-http&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://hackage.haskell.org/package/nri-redis" rel="noopener noreferrer"&gt;&lt;code&gt;nri-redis&lt;/code&gt;&lt;/a&gt;, and &lt;a href="https://hackage.haskell.org/package/nri-kafka" rel="noopener noreferrer"&gt;&lt;code&gt;nri-kafka&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Without The Elm Architecture, we depend heavily on chaining permutations through a stateful &lt;code&gt;Task&lt;/code&gt; type. This feels similar to imperative coding: First, do A, then B, then C. Hopefully, when we’re later on in our Haskell journey, we’ll discover a nice architecture to simplify our backend code.&lt;/p&gt;

&lt;h3&gt;
  
  
  🚚 Bringing Elm Values to Haskell
&lt;/h3&gt;

&lt;p&gt;One way in which Haskell is different from both Elm and Rails is that it is not particularly opinionated. Often the Haskell ecosystem offers multiple different ways to do one particular thing. So whether it’s writing an http server, logging, or talking with a database, the first time we do any of these things we’ll need to decide &lt;em&gt;how&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When adopting a Haskell feature or library, we care about&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;smallness, e.g. introduce new concepts only when necessary&lt;/li&gt;
&lt;li&gt;how “magical” is it? E.g. How &lt;em&gt;surprising&lt;/em&gt; is it?&lt;/li&gt;
&lt;li&gt;How easy is it to &lt;em&gt;learn&lt;/em&gt;?&lt;/li&gt;
&lt;li&gt;how easy is it to &lt;em&gt;use?&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;how comprehensible is the documentation?&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;explicitness&lt;/em&gt; over *terseness (*but terseness isn’t implicitly bad).&lt;/li&gt;
&lt;li&gt;consistency &amp;amp; predictability&lt;/li&gt;
&lt;li&gt;“safety” (no runtime exceptions).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sometimes the Haskell ecosystem provides an option that fits our Elm values, like with the handle pattern, and so we go with it. Other times a library has different values, and then the choice not to use it is easy as well. An example of this is lens/prism ecosystem, which allows one to write super succinct code, but is almost a language onto itself that one has to learn first.&lt;/p&gt;

&lt;p&gt;The hardest decisions are the ones where an approach protects us against making mistakes in some way (which we like) but requires familiarity with more language features to use (which we prefer to avoid).&lt;/p&gt;

&lt;p&gt;To help us make better decisions, we often &lt;strong&gt;try it both ways.&lt;/strong&gt; That is, we’re willing to build a piece of software with &amp;amp; without a complex language feature to ensure the &lt;em&gt;cost&lt;/em&gt; of the complexity is worth the benefit that the feature brings us.&lt;/p&gt;

&lt;p&gt;Another approach we take is making decisions locally. A single team might evaluate a new feature, and then demo it and share it with other teams after they have a good sense the feature is worth it. Remember: a super-power of Haskell is easy refactorability. Unlike our ruby code, going through and doing major re-writes in our Haskell codebase is often an hours-or-days-long (rather than weeks-or-months-long) endeavor. Adopting two different patterns simultaneously has a relatively small cost!&lt;/p&gt;

&lt;h3&gt;
  
  
  Case studies in feature adoption:
&lt;/h3&gt;

&lt;h4&gt;
  
  
  🐘 Type-Check All Elephants
&lt;/h4&gt;

&lt;p&gt;One example where our approach is Elm-like in some ways but not in others is how we talk to the database. We’re using a GHC feature called quasiquoting for this, which allow us to embed SQL query strings directly into our Haskell code, like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{-# LANGUAGE QuasiQuotes #-}

module Animals (listAll) where

import Postgres (query, sql)

listAll :: Postgres.Handler -&amp;gt; Task Text (List (Text, Text))
listAll postgres =
  query postgres [sql|SELECT species, genus FROM animals|]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;A library called &lt;a href="https://hackage.haskell.org/package/postgresql-typed" rel="noopener noreferrer"&gt;&lt;code&gt;postgresql-typed&lt;/code&gt;&lt;/a&gt; can test these queries against a real Postgres database and show us an error at compile time if the query doesn’t fit the data. Such a compile-time error might happen if a table or column we reference in a query doesn’t exist in the database. This way we use static checks to eliminate a whole class of potential app/database compatibility problems!&lt;/p&gt;

&lt;p&gt;The downside is that writing code like this requires everyone working with it to learn a bit about quasi quotes, and what return type to expect for different kinds of queries. That said, using some kind of querying library instead has a learning curve too, and query libraries tend to be pretty big to support all the different kinds of queries that can be made.&lt;/p&gt;

&lt;h4&gt;
  
  
  🔣 So Many Webserver Options
&lt;/h4&gt;

&lt;p&gt;Another example where we traded additional safety against language complexity is in our choice of webserver library. We went with &lt;code&gt;servant&lt;/code&gt; here, a library that lets you express REST APIs using types, like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Servant

data Routes route = Routes
  { listTodos ::
      route
        :- "todos"
        :&amp;gt; Get '\[JSON\] [Todo],
    updateTodo ::
      route
        :- "todos"
        :&amp;gt; Capture "id" Int
        :&amp;gt; ReqBody '[JSON] Todo
        :&amp;gt; Put '[JSON] NoContent,
    deleteTodo ::
      route
        :- "todos"
        :&amp;gt; Capture "id" Int
        :&amp;gt; Delete '[JSON] NoContent
  }
  deriving (Generic)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Servant is a big library that makes use of a lot of type-level programming techniques, which are pretty uncommon in Elm, so there’s a steep learning cost associated with &lt;em&gt;understanding&lt;/em&gt; how the type magic works. Using it without a deep understanding is reasonably straightforward.&lt;/p&gt;

&lt;p&gt;The benefits gained from using Servant outweigh the cost of expanded complexity. Based on a type like the one in the example above, the servant ecosystem can generate functions in other languages like Elm or Ruby. Using these functions means we can save time with backend-to-frontend or service-to-service communication. If some Haskell type changes in a backward-incompatible fashion we will generate new Elm code, and this might introduce a compiler error on the Elm side.&lt;/p&gt;

&lt;p&gt;So for now we’re using servant! It’s important to note that what we &lt;em&gt;want&lt;/em&gt; is compile-time server/client compatibility checking, and that’s why we swallow Servant’s complexity. If we could get the same benefit without the type-level programming demonstrated above, we would prefer that. Hopefully, in the future, another library will offer the same benefits from a more Elm-like API.&lt;/p&gt;

&lt;h2&gt;
  
  
  😻 Like what you see?
&lt;/h2&gt;

&lt;p&gt;We're running the libraries discussed above in production. Our most-used Haskell application receives hundreds of thousands of requests per minute without issue and produces hardly any errors.&lt;/p&gt;

&lt;p&gt;Code can be found at &lt;a href="https://github.com/NoRedInk/haskell-libraries" rel="noopener noreferrer"&gt;&lt;code&gt;NoRedInk/haskell-libraries&lt;/code&gt;&lt;/a&gt;. Libraries have been published to hackage and stackage. We'd love to know what you think!&lt;/p&gt;

</description>
      <category>haskell</category>
      <category>elm</category>
    </item>
    <item>
      <title>☄️ Pufferfish, please scale the site!</title>
      <dc:creator>Jasper Woudenberg</dc:creator>
      <pubDate>Thu, 22 Jul 2021 07:43:08 +0000</pubDate>
      <link>https://dev.to/noredink/pufferfish-please-scale-the-site-o4p</link>
      <guid>https://dev.to/noredink/pufferfish-please-scale-the-site-o4p</guid>
      <description>&lt;p&gt;&lt;em&gt;This post was co-authored by Michael Glass, Stöffel, and myself. It first first appeared on &lt;a href="https://blog.noredink.com/post/657392972659310592/pufferfish-please-scale-the-site" rel="noopener noreferrer"&gt;the NoRedInk blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We created Team Pufferfish about a year ago with a specific goal: &lt;strong&gt;&lt;em&gt;to avert the MySQL apocalypse&lt;/em&gt;&lt;/strong&gt;! The MySQL apocalypse would occur when so many students would work on quizzes simultaneously that even the largest MySQL database AWS has on offer would not be able to cope with the load, bringing the site to a halt.&lt;/p&gt;

&lt;p&gt;A little over a year ago, we forecasted our growth and load-tested MySQL to find out how much wiggle room we had. In the worst case (because we dislike apocalypses), or in the best case (because we like growing), we would have about a year’s time. This meant we needed to get going!&lt;/p&gt;

&lt;p&gt;Looking back on our work now, the most important lesson we learned was the importance of timely and precise feedback at every step of the way. At times we built short-lived tooling and process to support a particular step forward. This made us so much faster in the long run.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏔 Climbing the Legacy Code Mountain
&lt;/h2&gt;

&lt;p&gt;Clear from the start, Team Pufferfish would need to make some pretty fundamental changes to the Quiz Engine, the component responsible for most of the MySQL load. Somehow the Quiz Engine would need to significantly reduce its load on MySQL.&lt;/p&gt;

&lt;p&gt;Most of NoRedInk runs on a Rails monolith, including the Quiz Engine. The Quiz Engine is big! It’s got lots of features! It supports our teachers &amp;amp; students to do lots of great work together! Yay!&lt;/p&gt;

&lt;p&gt;But the Quiz Engine has some problems, too. A mix of complexity and performance-sensitivity has made engineers afraid to touch it. Previous attempts at big structural change in the Quiz Engine failed and had to be rolled back. If Pufferfish was going make significant structural changes, we would need to ensure our ability to be productive in the Quiz Engine codebase. Thinking we could &lt;em&gt;just do it&lt;/em&gt; without a new approach would be foolhardy.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚡ ️The Vengeful God of Tests
&lt;/h3&gt;

&lt;p&gt;We have mixed feelings about our test suite. It’s nice that it covers a lot of code. Less nice is that we don’t really know what each test is intended to check. These tests have evolved into complex bits of code by themselves with a lot of supporting logic, and in many cases, tight coupling to the implementation. Diving deep into some of these tests has uncovered tests &lt;em&gt;no longer covering any production logic at all&lt;/em&gt;. The test suite is large and we didn’t have time to dive deep into each test, but we were also reluctant to delete test cases without being sure they weren’t adding value.&lt;/p&gt;

&lt;p&gt;Our relationship with the Quiz Engine test suite was and still is a bit like one might have with an angry Greek god. We’re continuously investing effort to keep it happy (i.e. green), but we don’t always understand what we’re doing or why. Please don’t spoil our harvest and protect us from (production) fires, oh mighty RSpec!&lt;/p&gt;

&lt;p&gt;The ultimate goal wasn’t to change Quiz Engine functionality, but rather to reduce its load on MySQL. This is the perfect scenario for tests to help us! The test suite we want is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fast&lt;/li&gt;
&lt;li&gt;comprehensive, and&lt;/li&gt;
&lt;li&gt;not dependent on implementation&lt;/li&gt;
&lt;li&gt;includes performance testing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unfortunately, that’s not the hand we were given:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The suite takes about 30 minutes to run in CI and even longer locally.&lt;/li&gt;
&lt;li&gt;Our QA team finds bugs that sneaked past CI in PRs with Quiz Engine changes relatively frequently.&lt;/li&gt;
&lt;li&gt;Many tests ensure that &lt;em&gt;specific queries&lt;/em&gt; are performed in a &lt;em&gt;specific order&lt;/em&gt;. Considering we might replace MySQL wholesale, these tests provide little value.&lt;/li&gt;
&lt;li&gt;And because a lot of Quiz Engine code is extremely performance-sensitive, there’s an increased risk of performance regressions only surfacing with real production load.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fighting with our tests meant that even small changes would take hours to verify in tests, and then, because of unforeseen regressions not covered by the tests, take multiple attempts to fix, resulting in multiple-day roll-outs for small changes.&lt;/p&gt;

&lt;p&gt;Our clock is ticking! &lt;strong&gt;&lt;em&gt;We needed&lt;/em&gt;&lt;/strong&gt; &lt;strong&gt;&lt;em&gt;to&lt;/em&gt;&lt;/strong&gt; &lt;strong&gt;&lt;em&gt;iterate faster than that if we were going to&lt;/em&gt;&lt;/strong&gt; &lt;strong&gt;&lt;em&gt;avert the apocalypse.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🐶 I have no idea what I’m doing 🧪
&lt;/h3&gt;

&lt;p&gt;Reading complicated legacy Rails code often raises questions that take surprising amounts of effort to answer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is this method dead code? If not, who is calling this?&lt;/li&gt;
&lt;li&gt;Are we ever entering this conditional? When?&lt;/li&gt;
&lt;li&gt;Is this function talking to the database?&lt;/li&gt;
&lt;li&gt;Is this function &lt;em&gt;intentionally&lt;/em&gt; talking to the database?&lt;/li&gt;
&lt;li&gt;Is this function only &lt;em&gt;reading&lt;/em&gt; from the database or also &lt;em&gt;writing&lt;/em&gt; to it?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It isn’t even clear &lt;em&gt;what&lt;/em&gt; code was running. There are a few features of Ruby (and Rails) which optimize for &lt;em&gt;writing&lt;/em&gt; code over &lt;em&gt;reading&lt;/em&gt; it. We did our best to unwrap this type of code:&lt;/p&gt;

&lt;p&gt;Rails provides devs the ability to wrap functionality in hooks. &lt;code&gt;before_&lt;/code&gt; and &lt;code&gt;after_&lt;/code&gt; hooks let devs write setup and tear-down code once, then forget it. However, the existence of these hooks means calling a method might &lt;em&gt;also&lt;/em&gt; evaluate code defined in a different file, and you won’t know about it unless you explicitly look for it. &lt;em&gt;Hard to read!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Complicating things further is Ruby’s dynamic dispatch based on subclassing and polymorphic associations. Which &lt;code&gt;load_students&lt;/code&gt; am I calling? The one for &lt;code&gt;Quiz&lt;/code&gt; or the one for &lt;code&gt;Practice&lt;/code&gt;? They each implement the &lt;code&gt;Assignment&lt;/code&gt; interface but have pretty different behavior! And: they each have their own set of hooks🤦. Maybe it’s something completely different!&lt;/p&gt;

&lt;p&gt;And then there’s &lt;code&gt;ActiveRecord&lt;/code&gt;. &lt;code&gt;ActiveRecord&lt;/code&gt; makes it easy to write queries — a little too easy. It &lt;em&gt;doesn’t&lt;/em&gt; make it easy to know where queries are happening. It’s ergonomic that we can tell &lt;code&gt;ActiveRecord&lt;/code&gt; what we need, and let it figure how to fetch the data. It’s less nice when you’re trying to find out where in the code your queries are happening and the answer to that question is, “absolutely anywhere”. We want to know &lt;em&gt;exactly what queries&lt;/em&gt; are happening on these code paths. &lt;code&gt;ActiveRecord&lt;/code&gt; doesn’t help.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧵A rich history
&lt;/h3&gt;

&lt;p&gt;A final factor that makes working in Quiz Engine code daunting is the sheer size of the beast. The Quiz Engine has grown organically over many years, so there’s a lot of functionality to be aware of.&lt;/p&gt;

&lt;p&gt;Because the Quiz Engine itself has been hard to change for a while, APIs defined between bits of Quiz Engine code often haven’t evolved to match our latest understanding. This means understanding the Quiz Engine code requires not just understanding what it does today, but also how we thought about it in the past, and what (partial) attempts were made to change it. This increases the sum of Quiz Engine knowledge even further.&lt;/p&gt;

&lt;p&gt;For example, we might try to refactor a bit of code, leading to tests failing. But is this conditional branch ever reached in production? 🤷&lt;/p&gt;

&lt;h2&gt;
  
  
  Enough complaining. What did we do about it?
&lt;/h2&gt;

&lt;p&gt;We knew this was going to be a huge project, and huge projects, in the best case, are shipped late, and in the average case don’t ever ship. The only way we were going to have confidence that our work would ever see the light of day was by doing the riskiest, hardest, scariest stuff first. That way, if one approach wasn’t going to work, we would find out about it sooner and could try something new before we’d over-invested in a direction.&lt;/p&gt;

&lt;p&gt;So: where is the risk? What’s the scariest problem we have to solve? History dictates: The more we change the legacy system, the more likely we’re going to cause regressions.&lt;/p&gt;

&lt;p&gt;So our first task: cut away the part of the Quiz Engine that performs database queries and port this logic to a separate service. Henceforth when Rails needs to read or change Quiz Engine data, it will talk to the new service instead of going to the database directly.&lt;/p&gt;

&lt;p&gt;Once the legacy-code risk has been minimized, we would be able to focus on the (still challenging) task of changing where we store Quiz Engine data from single-database MySQL to something horizontally scalable.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⛏️ Phase 1: Extracting queries from Rails
&lt;/h3&gt;

&lt;h4&gt;
  
  
  🔪 Finding out where to cut
&lt;/h4&gt;

&lt;p&gt;Before extracting Quiz Engine MySQL queries from our Rails service, we first needed to know where those queries were being made. As we discussed above this wasn’t obvious from reading the code.&lt;/p&gt;

&lt;p&gt;To find the MySQL queries themself, we built some tooling: we monkey-patched &lt;code&gt;ActiveRecord&lt;/code&gt; to warn whenever an unknown read or write was made against one of the tables containing Quiz Engine data. We ran our monkey-patched code first in CI and later in production, letting the warnings tell us where those queries were happening. Using this information we decorated our code by marking all the reads and writes. Once code was decorated, it would no longer emit warnings. As soon as all the writes &amp;amp; reads were decorated, we changed our monkey-patch to not just warn but fail when making a query against one of those tables, to ensure we wouldn’t accidentally introduce new queries touching Quiz Engine data.&lt;/p&gt;

&lt;h4&gt;
  
  
  🚛 Offloading logic: &lt;strong&gt;Our first approach&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Now we knew where to cut, we decided our place of greatest risk was moving a single MySQL query out of our rails app. If we could move a single query, we could move all of them. There was one rub: if we &lt;em&gt;did&lt;/em&gt; move all queries to our new app, we would add a lot of network latency. because of the number of round trips needed for a single request. Now we have a constraint: Move a single query into a new service, but with very little latency.&lt;/p&gt;

&lt;p&gt;How did we reduce latency?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get rid of network latency by getting rid of the network — we hosted the service in the same hardware as our Rails app.&lt;/li&gt;
&lt;li&gt;Get rid of protocol latency by using a dead-simple protocol: socket communication.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We ended up building a socket server in Haskell that took data requests from Rails, and transformed them into a series of MySQL queries, which rails would use to fetch the data itself.&lt;/p&gt;

&lt;h4&gt;
  
  
  🛸 Leaving the Mothership: Fewer Round Trips
&lt;/h4&gt;

&lt;p&gt;Although co-locating our service with rails got us off the ground, it required significant duct tape. We had invested a lot of work building nice deployment systems for HTTP services and we didn’t want to re-invent that tooling for socket-based side-car apps. The thing that was preventing the migration was having too many round-trip requests to the Rails app. How could we reduce the number of round trips?&lt;/p&gt;

&lt;p&gt;As we moved MySQL query generation to our new service, we started to see this pattern in our routes:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MySQL Read some data       ┐
Ruby  Do some processing   │ candidate 1 for
MySQL Read some more data  ┘ extraction
Ruby  More processing
MySQL Write some data      ┐
Ruby  Processing again!    │ candidate 2 for
MySQL Write more data      ┘ extraction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To reduce latency, we’d have to bundle reads and writes: In addition to porting reads &amp;amp; writes to the new service, we’d have to port the ruby logic &lt;em&gt;between&lt;/em&gt; reads and writes, which would be a lot of work.&lt;/p&gt;

&lt;p&gt;What if instead, we could &lt;em&gt;change&lt;/em&gt; the order of operations and make it look like this?&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MySQL Read some data       ┐ candidate 1 for
MySQL Read some more data  ┘ extraction
Ruby  Do some processing
Ruby  More processing
Ruby  Processing again!
MySQL Write some data      ┐ candidate 2 for
MySQL Write more data      ┘ extraction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then we’d be able to extract batches of queries to Haskell and leave the logic behind in Rails.&lt;/p&gt;

&lt;p&gt;One concern we had with changing the order of operations like this was the possibility of a request handler &lt;em&gt;first&lt;/em&gt; writing some data to the database, then reading it back again later. Changing the order of read and write queries would result in such code failing. However, since we now had a complete and accurate picture of &lt;em&gt;all&lt;/em&gt; the queries the Rails code was making, we knew (luckily!) we didn’t need to worry about this.&lt;/p&gt;

&lt;p&gt;Another concern was the risk of a large refactor like this resulting in regressions causing long feedback cycles and breaking the Quiz Engine. To avoid this we tried to keep our refactors as dumb as possible: Specifically: we mostly did a lot of inlining. We would start with something like this&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class QuizzesControllller &amp;lt; ActionController
  def show
    quiz = load_quiz! # here are queries sometimes
    quiz_type = which_quiz(quiz) # and here other times
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;and we would aggressively inline functions to surface where and why we were querying&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class QuizzesControllller &amp;lt; ActionController
  def show
    quiz = Quiz.find(quiz_id_param)
    quiz_type =
      if quiz.for_credit?
        :for_credit
      else
        load_practice_quiz_type
      end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;and again, and again&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class QuizzesControllller &amp;lt; ActionController
  def show
    quiz = Quiz.find(quiz_id_param)
    quiz_type =
      if quiz.for_credit?
        :for_credit
      else
        how_much_fun = QuizForFun.find(quiz_id_param)
        if how_much_fun &amp;gt; 9000
          :super_saiyan
        else
          load_sub_syan_fun_type # TODO: inline me
        end
      end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;These are refactors with a relatively small chance of changing behavior or causing regressions.&lt;/p&gt;

&lt;p&gt;Once the query was at the top level of the code it became clear when we needed data, and that understanding allowed us to push those queries to happen first.&lt;/p&gt;

&lt;p&gt;e.g. from above, we could easily push the previously obscured &lt;code&gt;QuizForFun&lt;/code&gt; query to the beginning:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class QuizzesControllller &amp;lt; ActionController
  def show
    quiz = Quiz.find(quiz_id_param)
    how_much_fun =
      if quiz.for_credit?
        nil
      else
        QuizForFun.find(quiz_id_param)
      end
    quiz_type = if quiz.for_credit?
        :for_credit
      elsif how_much_fun &amp;gt; 9000
        :super_saiyan
      else
        load_sub_syan_fun_type # TODO: inline me
      end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You might expect our bout of inlining to introduce a ton of duplication in our code, but in practice, it surfaced a lot of dead code and made it clearer what the functions we left behind were doing. That wasn’t what we set out to do, but still, nice!&lt;/p&gt;

&lt;h2&gt;
  
  
  👛 Phase 2: Changing the Quiz Engine datastore
&lt;/h2&gt;

&lt;p&gt;At this point all interactions with the Quiz Engine datastore were going through this &lt;strong&gt;&lt;em&gt;new&lt;/em&gt;&lt;/strong&gt; Quiz Engine service. Excellent! This means for the second part of this project, the part where we were actually going to avert the MySQL apocalypse, we wouldn’t need to worry about our legacy Rails code.&lt;/p&gt;

&lt;p&gt;To facilitate easy refactoring, we built this new service in Haskell. The effect was immediately noticeable. Like an embargo had been lifted, from this point forward we saw a constant trickle of small productive refactors get mixed in the work we were doing, slowly reshaping types to reflect our latest understanding. Changes we wouldn’t have made on the Rails side unless we’d have set aside months of dedicated time. Haskell is a great tool to use to manage complexity!&lt;/p&gt;

&lt;p&gt;The centerpiece of this phase was the architectural change we were planning to make: switching from MySQL to a horizontally scalable storage solution. But honestly, figuring the architecture details here wasn’t the most interesting or challenging portion of the work, so we’re just putting that aside for now. Maybe we’ll return to it in a future blog post (sneak peek: we ended up using Redis and Kafka). Like in step 1, the biggest question we had to solve was &lt;strong&gt;&lt;em&gt;“how are we going to make it safe to move forward quickly?”&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One challenge was that we had left most of our test suite behind in Rails in phase one, so we were not doing too well on that front. We added Haskell test coverage of course, including many &lt;strong&gt;&lt;em&gt;golden result tests&lt;/em&gt;&lt;/strong&gt; which are worth a post on their own. Together with our QA team we also invested in our Cypress integration test suite which runs tests from the browser, thus integration-testing the combination of our Rails and Haskell code.&lt;/p&gt;

&lt;p&gt;Our most useful tool in making safe changes in this phase however was our production traffic. We started building up what was effectively a parallel Haskell service talking to Redis next to the existing one talking to MySQL. Both received production load from the start, but until the very end of the project only the MySQL code paths’ response values were used. When the Redis code path didn’t match the MySQL, we’d log a bug. Using these bug reports, we slowly massaged the Redis code path to return identical data to MySQL.&lt;/p&gt;

&lt;p&gt;Because we weren’t relying on the output of the Redis code path in production, we could deploy changes to it many times a day, without fear of breaking the site for students or teachers. These deploys provided frequent and fast feedback. Deploying frequently was made possible by the Haskell Quiz Engine code living in its own service, which meant deploys contained only changes by our team, without work from other teams with a different risk profile.&lt;/p&gt;

&lt;h2&gt;
  
  
  🥁 So, did it work?
&lt;/h2&gt;

&lt;p&gt;It’s been about a month since we’ve switched entirely to the new architecture and it’s been humming along happily. By the time we did the official switch-over to the new datastore it had been running at full-load (but with bugs) for a couple of months already. Still, we were standing ready with buckets of water in case we overlooked something. Our anxiety was in vain: the roll-out was a non-event.&lt;/p&gt;

&lt;p&gt;Architecture, plans, goals, were all important to making this a success. Still, we think the thing most crucial to our success was continuously improving our feedback loops. Fast feedback (lots of deploys), accurate feedback (knowing &lt;strong&gt;all&lt;/strong&gt; the MySQL queries Rails is making), detailed feedback (lots of context in error reports), high signal/noise ratio (removing errors we were not planning to act on), lots of coverage (many students doing quizzes). Getting this feedback required us to constantly tweak and create tooling and new processes. But even if these processes were sometimes short-lived, they've never been an overhead, allowing us to move so much faster.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Type-Safe MySQL Queries via Postgres </title>
      <dc:creator>Christoph Hermann</dc:creator>
      <pubDate>Tue, 05 Nov 2019 08:19:05 +0000</pubDate>
      <link>https://dev.to/noredink/type-safe-mysql-queries-via-postgres-5cnk</link>
      <guid>https://dev.to/noredink/type-safe-mysql-queries-via-postgres-5cnk</guid>
      <description>&lt;h1&gt;
  
  
  Type-Safe MySQL Queries via Postgres
&lt;/h1&gt;

&lt;p&gt;My team and I are working on NoRedInk's transition from Ruby on Rails and MySQL to Haskell and Postgres. A major benefit of our new stack (Postgres/Haskell/Elm) is type-safety between the boundaries (db to backend, backend to frontend). To ensure our code is type-safe between Haskell and &lt;a href="(https://elm-lang.org/)"&gt;Elm&lt;/a&gt; we use &lt;a href="https://www.servant.dev/"&gt;servant&lt;/a&gt;. Servant allows us to generate types and functions for Elm based on our APIs. To ensure type-safety between Postgres and Haskell we use &lt;a href="https://hackage.haskell.org/package/postgresql-typed-0.6.1.0"&gt;postgresql-typed&lt;/a&gt;. This means that when we change our database schema, our Haskell code won't compile if there is a mismatch, similarly to how our Elm code won't compile if there is a mismatch with our backend endpoints.  We're going to focus on type-safety between our databases and Haskell in this post and on how we introduced MySQL into this.  But first, let's briefly look at how we can check out Postgres queries at compile-time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Postgres-Typed
&lt;/h2&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;todos&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;PGConnection&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;IO&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;todos&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt; &lt;span class="n"&gt;postgresConn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;
    &lt;span class="n"&gt;pgQuery&lt;/span&gt;
      &lt;span class="n"&gt;postgresConn&lt;/span&gt;
      &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pgSQL&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;!
        SELECT id, description, completed
        FROM todos
        WHERE user_id = ${userId}
        ORDER BY id ASC
      &lt;span class="o"&gt;|]&lt;/span&gt;
  &lt;span class="n"&gt;pure&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fmap&lt;/span&gt; &lt;span class="n"&gt;todoFromRow&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We use &lt;a href="https://wiki.haskell.org/Template_Haskell"&gt;Template Haskell&lt;/a&gt; to write SQL queries instead of a DSL, allowing our engineers to use their existing knowledge of SQL instead of having to learn some library. &lt;code&gt;pgSQL&lt;/code&gt; is a &lt;a href="https://wiki.haskell.org/Quasiquotation"&gt;&lt;code&gt;QuasiQuoter&lt;/code&gt;&lt;/a&gt; it creates a value of type &lt;code&gt;PGQuery&lt;/code&gt; which gets executed by &lt;code&gt;pgQuery&lt;/code&gt;. The quasi quoted query gets verified against the database schema and executed (ignoring results and without arguments) at compile-time. This is all we need to know from &lt;a href="https://hackage.haskell.org/package/postgresql-typed-0.6.1.0"&gt;postgresql-typed&lt;/a&gt; for this post, but I recommend checking out the &lt;a href="https://hackage.haskell.org/package/postgresql-typed-0.6.1.0"&gt;docs&lt;/a&gt; and looking at their &lt;a href="https://github.com/dylex/postgresql-typed/blob/master/test/Main.hs"&gt;example&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving to Haskell and Postgres
&lt;/h2&gt;

&lt;p&gt;Our Rails application uses a MySQL database. Gradually moving functionality/new features to Haskell often means that we need access to data that isn't yet moved.  We solved this initially by creating an endpoints on our Rails application and requested the data via http. This proved to be toilsome and complicated development and deploys, and therefore made it harder for teams to commit to building things in Haskell. In order to remove this hurdle, we started to think about reading data directly from our MySQL database. We really liked &lt;a href="https://hackage.haskell.org/package/postgresql-typed-0.6.1.0"&gt;postgresql-typed&lt;/a&gt;, because it gave us the ability to write actual SQL queries and type-safety between the database and Haskell. Unfortunatlly, there isn’t a mysql-typed, but we found an interesting solution provided by Postgres itself; foreign-data-wrappers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Foreign Data Wrappers
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://wiki.postgresql.org/wiki/Foreign_data_wrappers"&gt;Foreign Data Wrappers&lt;/a&gt; (short fdw) offer a way to manage remote data directly within your Postgres database. This allows us to use &lt;a href="https://hackage.haskell.org/package/postgresql-typed-0.6.1.0"&gt;postgresql-typed&lt;/a&gt; to access the MySQL data via Postgres.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;SERVER&lt;/span&gt; &lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
  &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;DATA&lt;/span&gt; &lt;span class="n"&gt;WRAPPER&lt;/span&gt; &lt;span class="n"&gt;mysql_fdw&lt;/span&gt;
  &lt;span class="k"&gt;OPTIONS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;host&lt;/span&gt; &lt;span class="s1"&gt;'${host}'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="s1"&gt;'${port}'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Mapping for the application user, so it can perform queries.&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="n"&gt;MAPPING&lt;/span&gt; &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="err"&gt;${&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;SERVER&lt;/span&gt; &lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
  &lt;span class="k"&gt;OPTIONS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="s1"&gt;'${username}'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="s1"&gt;'${passphrase}'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;AUTHORIZATION&lt;/span&gt; &lt;span class="err"&gt;${&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;IMPORT&lt;/span&gt; &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="err"&gt;${&lt;/span&gt;&lt;span class="n"&gt;dbname&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;SERVER&lt;/span&gt; &lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
  &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Unfortunately, we quickly ran into problems when converting http requests to the Rails application with queries directly to MySQL. Fdw has some limitations that were blockers for use. The schema imported as a fdw is imported without any constraints other than &lt;code&gt;NOT NULL&lt;/code&gt;. An even bigger problem was that some SQL constructs weren’t pushed down to MySQL. As an example the fdw wouldn’t forward &lt;code&gt;LIMIT&lt;/code&gt; clauses and therefore grab all rows of a table, send them over the wire, and then apply the &lt;code&gt;LIMIT&lt;/code&gt; in Postgres. This obviously has huge performance implications and meant that we needed to find a different solution without suffering type-safety.&lt;/p&gt;

&lt;h2&gt;
  
  
  Type-Safe MySQL Queries
&lt;/h2&gt;

&lt;p&gt;We had three options; either find a library that would give us the guarantees we were used to from &lt;a href="https://hackage.haskell.org/package/postgresql-typed-0.6.1.0"&gt;postgresql-typed&lt;/a&gt; for MySQL, building our own library or somehow abusing &lt;a href="https://hackage.haskell.org/package/postgresql-typed-0.6.1.0"&gt;postgresql-typed&lt;/a&gt;.  Building our own library wasn't an option because that would have exceeded the scope of this project, and we couldn't find a library that met our requirements.  Fortunately, &lt;a href="https://hackage.haskell.org/package/postgresql-typed-0.6.1.0"&gt;postgresql-typed&lt;/a&gt; allows us to access &lt;code&gt;getQueryString&lt;/code&gt; to get the raw query string from the quasi-quoted query. This we can then execute with &lt;a href="http://hackage.haskell.org/package/mysql-simple-0.4.5"&gt;mysql-simple&lt;/a&gt; instead of &lt;a href="https://hackage.haskell.org/package/postgresql-typed-0.6.1.0"&gt;postgresql-typed&lt;/a&gt;. This allows us to check MySQL and Postgres queries at compile time using a fdw, but connecting directly to Postgres and MySQL at runtime.  Finally we can write queries to our MySQL database using the same API as for Postgres and having full type-safety between the database and Haskell.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;cats&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Database&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;MySQL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Simple&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Connection&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;IO&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;cats&lt;/span&gt; &lt;span class="n"&gt;mySQLConnection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;
    &lt;span class="kt"&gt;Database&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;MySQL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Simple&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query_&lt;/span&gt;
      &lt;span class="n"&gt;mySQLConnection&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="c1"&gt;-- direct connection to MySQL&lt;/span&gt;
      &lt;span class="n"&gt;remove&lt;/span&gt; &lt;span class="s"&gt;"mysql-schema-name."&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;
      &lt;span class="c1"&gt;-- ^ The schemaname (fdw) only exists during compile time.&lt;/span&gt;
      &lt;span class="n"&gt;getQueryString&lt;/span&gt; &lt;span class="n"&gt;unknownPGTypeEnv&lt;/span&gt;
      &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pgSql&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;!
        SELECT id, description, color
        FROM mysql-schema-name.cats
        ORDER BY id ASC
      &lt;span class="o"&gt;|]&lt;/span&gt;
  &lt;span class="n"&gt;pure&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fmap&lt;/span&gt; &lt;span class="n"&gt;catFromRow&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I’ve inlined the functions from &lt;a href="https://hackage.haskell.org/package/postgresql-typed-0.6.1.0"&gt;postgresql-typed&lt;/a&gt; and mysql-simple to keep the code examples simple. We actually have two modules &lt;code&gt;MySQL&lt;/code&gt; and &lt;code&gt;Postgres&lt;/code&gt; that abstract the database specific functions away allowing us to use the exact same API for both databases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caveats
&lt;/h2&gt;

&lt;p&gt;We need to prefix tables with the schema name that we used to import the fdw (see &lt;code&gt;mysql-schema-name.cats&lt;/code&gt; in the example above). Without the prefix the compilation won't succeed. The problem is that we don't have a schema with this name at runtime. We can simply work around this by running &lt;code&gt;Text.replace "mysql-schema-name." "" sqlString&lt;/code&gt; before executing the query.  Queries need to use standard SQL features and shouldn't rely on MySQL specific features. This is actually kind of a feature, because it forces us to write queries in a way that simplifies converting them to Postgres later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;While this is a nice solution for us, I don't really recommend you to use MySQL in this way. This solution allows us to simplify the transition between Rails/MySQL and Haskell/Postgres. I would love for library like &lt;a href="https://hackage.haskell.org/package/postgresql-typed-0.6.1.0"&gt;postgresql-typed&lt;/a&gt; to exist for MySQL, but it wasn't feasable to build such a thing. Honestly, I don't even know if it would be possible. I hope this post showed you how to create a nice experience for transitioning to a different technology.&lt;br&gt;
Thanks for reading ❤️&lt;/p&gt;




&lt;p&gt;Special thanks to everyone involved in building this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/asterite"&gt;Ary&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/allenap"&gt;Gavin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/jwoudenberg"&gt;Jasper&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.mavnn.co.uk/"&gt;Michael&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>haskell</category>
      <category>postgres</category>
      <category>mysql</category>
      <category>noredink</category>
    </item>
    <item>
      <title>Drag &amp; Drop without Draggables &amp; Dropzones</title>
      <dc:creator>Jasper Woudenberg</dc:creator>
      <pubDate>Fri, 02 Aug 2019 16:21:20 +0000</pubDate>
      <link>https://dev.to/noredink/drag-drop-without-draggables-dropzones-2be5</link>
      <guid>https://dev.to/noredink/drag-drop-without-draggables-dropzones-2be5</guid>
      <description>&lt;p&gt;Why is building drag &amp;amp; drop UIs so hard? They've been around for a while, so we would be forgiven for thinking they're a solved problem. Certainly there are high quality libraries to help us build drag &amp;amp; drop UIs, and these days we even have an official HTML5 drag &amp;amp; drop API! What's going on here?&lt;/p&gt;

&lt;p&gt;I've come to the conclusion a big part of the problem is our choice of tools. Draggables and dropzones are often the wrong abstraction, and choosing different tools can simplify construction of our drag &amp;amp; drop UIs massively.&lt;/p&gt;

&lt;p&gt;That's a pretty sweeping statement for a broad term like 'drag &amp;amp; drop', so let's look at three examples of drag &amp;amp; drop UIs built without draggables or dropzones. These examples come from real projects, but I've stripped them down to their drag &amp;amp; drop essentials for this post. I'll include what I believe to be the most relevant code snippets in this post, and provide links to the full source code.&lt;/p&gt;

&lt;p&gt;Let's start with a quick refresher on draggables and dropzones before we get to the examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Refresher on Draggables &amp;amp; Dropzones
&lt;/h2&gt;

&lt;p&gt;A draggable is a UI element that follows the cursor when the user presses down on it. A dropzone is a UI element that gets an event when we release a draggable over it.&lt;/p&gt;

&lt;p&gt;For an example lets look at the application &lt;a href="https://trello.com/"&gt;Trello&lt;/a&gt;. Trello allows us to organize cards in lists. We could make the cards draggables and the lists dropzones. That way we'd get an event every time a card gets dropped on a list, which we could use to update our app's state with the new location of the card.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZtivznQK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/x9lpraW.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZtivznQK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/x9lpraW.png" alt="A screenshot of the Trello app, showing a card being dragged from one list to another."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On closer inspection though things aren't as clear cut. For example, in the real Trello application it's not necessary to drop a card on top of a list. We can drop a card on some empty space and it will move into the list nearest to where we dropped it. That's much nicer for the user, but it's not clear how to create this behavior using draggables and dropzones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our first example, A Timeslot Selector
&lt;/h2&gt;

&lt;p&gt;Our first example is a UI for selecting a time slot in a day. This UI represents a day as a horizontal bar, in which we can select a slot using drag &amp;amp; drop.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HGz4jZjs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i.imgur.com/U0Ft47l.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HGz4jZjs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i.imgur.com/U0Ft47l.gif" alt="Dragging to select a continuous region of time across a bar showing the hours of the day."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'd like to start these examples by imagining what a draggables and dropzones implementation might look like. In this first example it's unclear what our draggables and dropzones even are. We could make the time slider the draggable, because clicking anywhere in it should start the drag operation even if we don't drag the time slider itself. The screen as a whole might serve as a dropzone, because the user should be able to release the cursor anywhere to finish the drag. Already this approach feels pretty hacky, and we haven't even written any code yet!&lt;/p&gt;

&lt;p&gt;Let's start from scratch without assuming draggables or dropzones. As we regularly do, we'll begin building our Elm application by designing a Model. Our application needs to store a time slot which is nothing more than a start and end hour.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- In this example a time slot always covers whole hours,&lt;/span&gt;
&lt;span class="c1"&gt;-- but we could make this minutes or seconds if we wanted.&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;selectionStart&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Hour&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;selectionEnd&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Hour&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Hour&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Int&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we turn to implementing the drag &amp;amp; drop behavior, without draggables and dropzones. When the user presses down we store the hour the cursor is over as the &lt;code&gt;selectionStart&lt;/code&gt; field of the timeslot. Then as the cursor moves we will update the &lt;code&gt;selectionEnd&lt;/code&gt; field of the timeslot, until the user releases.&lt;/p&gt;

&lt;p&gt;At any point we will need to know which hour the cursor is over. We can calculate this if we know the position of the cursor and the position and dimensions of the slider on the screen. Lets be optimistic and assume we just get that information on our drag events. If so we can design our &lt;code&gt;Msg&lt;/code&gt; type like this:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;DragEvent&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Coords&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sliderPosition&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Rect&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;DragEvent&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Start&lt;/span&gt;
      &lt;span class="c1"&gt;-- We don't need to do anything special on a Stop event, so we can treat&lt;/span&gt;
      &lt;span class="c1"&gt;-- it the same as a Move event.&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;MoveOrStop&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Coords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Float&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Float&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Rect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Float&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Float&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Float&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Float&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The information in this &lt;code&gt;Msg&lt;/code&gt; is enough to calculate the hour the cursor is over.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;cursorAtHour&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Hour&lt;/span&gt;
&lt;span class="n"&gt;cursorAtHour&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sliderPosition&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt;
        &lt;span class="n"&gt;dx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;sliderPosition&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;

        &lt;span class="n"&gt;atMost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="n"&gt;min&lt;/span&gt;

        &lt;span class="n"&gt;atLeast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="n"&gt;max&lt;/span&gt;
    &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dx&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;sliderPosition&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;floor&lt;/span&gt;
        &lt;span class="c1"&gt;-- Ensure we get a number between 0 and 23, even if the cursor moves to&lt;/span&gt;
        &lt;span class="c1"&gt;-- the left or right of the slider.&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;atMost&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;atLeast&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;All that's left to do is use &lt;code&gt;cursorAtHour&lt;/code&gt; in our &lt;code&gt;update&lt;/code&gt; function. When we get a &lt;code&gt;Start&lt;/code&gt; event we use it to update the &lt;code&gt;selectionStart&lt;/code&gt; field in the model, and when we get a &lt;code&gt;MoveOrStop&lt;/code&gt; event the &lt;code&gt;selectionEnd&lt;/code&gt; field.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;
&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt;
        &lt;span class="n"&gt;hour&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="n"&gt;cursorAtHour&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;
    &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Start&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;coordsInRect&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sliderPosition&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;selectionStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hour&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;selectionEnd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hour&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;else&lt;/span&gt;
                &lt;span class="n"&gt;model&lt;/span&gt;

        &lt;span class="kt"&gt;MoveOrStop&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;
                &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;selectionEnd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hour&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;coordsInRect&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Coords&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Rect&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;
&lt;span class="n"&gt;coordsInRect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Debug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Implementation omitted for brevity."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And we have an app! Well almost, we've not discussed where these &lt;code&gt;Msg&lt;/code&gt;s will be coming from. There's a bit of JavaScript responsible for that, which I'll talk more about in a bit. Those wanting to peek ahead can check out the &lt;a href="https://ellie-app.com/6gbc3wS5CYVa1"&gt;time slider source code&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Second example, a polygon editor
&lt;/h2&gt;

&lt;p&gt;Our second example is a tool for editing polygons. We want users to be able to pick up a vertex of a polygon and move it somewhere else.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Oq0Ng3Yn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i.imgur.com/tpsNuZS.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Oq0Ng3Yn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i.imgur.com/tpsNuZS.gif" alt="Dragging a vertex of a polygon"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See those vertices? Those sure look like draggables! But these vertices are small and easy to miss, so we'd want our draggables to be bigger. We could achieve this by making the draggables invisible &lt;code&gt;div&lt;/code&gt; elements centered on these vertices, but that gets us in trouble when the &lt;code&gt;div&lt;/code&gt; elements are so close they overlap. At that point a click won't select the closest vertex but the top vertex.&lt;/p&gt;

&lt;p&gt;We're going to leave those draggables in the toolbox and see how far we can get without them. As usual we start by defining a model to store the polygon we're editing.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;polygon&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Polygon&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;draggedVertex&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="kt"&gt;Id&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Polygon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Dict&lt;/span&gt; &lt;span class="kt"&gt;Id&lt;/span&gt; &lt;span class="kt"&gt;Coords&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Int&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The drag starts when the user presses down on a vertex to select it. For this we'll need to calculate the vertex closest to the cursor, which we can do if we know the positions of the cursor and all vertices. Lets create a &lt;code&gt;Msg&lt;/code&gt; type that contains those positions. We can reuse the &lt;code&gt;Coords&lt;/code&gt; and &lt;code&gt;Rect&lt;/code&gt; types from the previous example.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;DragEvent&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Coords&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handlers&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Rect&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;DragEvent&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Start&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Move&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Stop&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Perfect! Now we can calculate the rectangle closest to the cursor when the user clicks.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;closestRect&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Coords&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Rect&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="n"&gt;closestRect&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="n"&gt;handlers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;handlers&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Tuple&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapSecond&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;distance&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;center&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="c1"&gt;-- Find the vertex closest to the cursor.&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sortBy&lt;/span&gt; &lt;span class="kt"&gt;Tuple&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;second&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="kt"&gt;Tuple&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;


&lt;span class="n"&gt;center&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Rect&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Coords&lt;/span&gt;
&lt;span class="n"&gt;center&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Debug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Implementation omitted for brevity"&lt;/span&gt;


&lt;span class="n"&gt;distance&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Coords&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Coords&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Float&lt;/span&gt;
&lt;span class="n"&gt;distance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Debug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Implementation omitted for brevity"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Once we have found the vertex the user picked up we have to move it to the cursor on every &lt;code&gt;Move&lt;/code&gt; event. The resulting &lt;code&gt;update&lt;/code&gt; function looks like this.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;
&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handlers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Start&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;
                &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;draggedVertex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                    &lt;span class="n"&gt;handlers&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;
                            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                                &lt;span class="n"&gt;distance&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;center&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;
                            &lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;closestRect&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kt"&gt;Move&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;draggedVertex&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
                &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;polygon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Dict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;polygon&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="kt"&gt;Nothing&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="c1"&gt;-- The user is dragging the cursor, but nothing was picked up on the&lt;/span&gt;
                    &lt;span class="c1"&gt;-- start event. We'll sit this one out.&lt;/span&gt;
                    &lt;span class="n"&gt;model&lt;/span&gt;

        &lt;span class="kt"&gt;Stop&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;draggedVertex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Nothing&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And that's that! Again I skipped over the JavaScript that produces our messages and I promise we'll get to that in a moment. The full &lt;a href="https://ellie-app.com/6gbMHDr6SbGa1"&gt;polygon editor source code&lt;/a&gt; is available for those interested!&lt;/p&gt;

&lt;h2&gt;
  
  
  Last example: an outline editor
&lt;/h2&gt;

&lt;p&gt;Our final example is an outline editor. An outline is a tool for organizing our thoughts on a subject, but creating a list of concepts related to the thought, each of which have their own related thoughts, and so forth. The following image shows an example outline which can be re-arranged using drag &amp;amp; drop. We'll keep our scope small again by not bothering with creating and deleting nodes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rJOPHNKa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i.imgur.com/0MuKonT.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rJOPHNKa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://i.imgur.com/0MuKonT.gif" alt="Grabbing a node of an outline and dragging it to a different parent node"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We'll start by creating a model for our outline editor. It will need to keep track of two things: the outline itself and which node we're dragging.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;outline&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="kt"&gt;OutlineNode&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;draggedNode&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="kt"&gt;DraggedNode&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;DraggedNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="c1"&gt;-- For simplicity sake we're going to use the node's contents as an id.&lt;/span&gt;
    &lt;span class="c1"&gt;-- We get away with that here because we can ensure the nodes are unique:&lt;/span&gt;
    &lt;span class="c1"&gt;-- the user will not be able to edit them in this example.&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursorOnScreen&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Coords&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursorOnDraggable&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Coords&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;OutlineNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Tree&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Tree&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Tree&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;children&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Tree&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we'll need to write behavior for the drag start, move, and end events.&lt;/p&gt;

&lt;p&gt;The drag starts when the user pressed down on a node. We can put an &lt;code&gt;onClick&lt;/code&gt; handler on each node to detect when this happens. We'll skip the implementation in this post, but it's part of the full &lt;a href="https://ellie-app.com/6gbMfDjCnH4a1"&gt;outline editor source code&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Then, as the user drags a node around we need to update that node's location in the outline. This part we're going to look at in detail.&lt;/p&gt;

&lt;p&gt;Lastly the drag stop event. We already changed the outline while the user was moving the cursor, and so all that's left to do here is change the model to its non-dragging state by setting &lt;code&gt;draggedNode&lt;/code&gt; to &lt;code&gt;Nothing&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Moving nodes in an outline
&lt;/h3&gt;

&lt;p&gt;The most challenging part is to decide what the user's intention is. Are they intending to move the dragged node in front of another node, behind it, or nested beneath it?&lt;/p&gt;

&lt;p&gt;Using dropzones we could draw invisible boxes in those positions that activate when the user moves over them, but the experience is unlikely to be great. Make the boxes too small and the user will not spend a lot of time over them, making the interface unresponsive for most of the time. Make the boxes too big and they start to overlap, causing the uppermost box to receive the dragged node even if it's not the closest. And even if we get the boxes right a future update changing page styles might move the boxes, breaking the drag &amp;amp; drop interaction.&lt;/p&gt;

&lt;p&gt;Let's forget about dropzones and think about the behavior we want. There are candidate positions in the outline where we could drop a dragged node. As the user moves we'd like to display the dragged node in whichever of those positions is closest to the cursor. To figure out which position is closest we need to know where they all are. To do that we are going to put invisible elements in the DOM at each position where we can insert the dragged node. Contrary to the dropzones approach we're not going to bother giving these elements any special dimensions or positioning. We want them to just flow with the content on the page, and keep us apprised of their location. These aren't dropzones but beacons.&lt;/p&gt;

&lt;p&gt;Apart from their coordinates in the DOM, our beacons will also need to describe their location in the outline. A beacon can define its location relative to another node in the outline.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;CandidatePosition&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Before&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;After&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;PrependedIn&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;AppendedIn&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We'll create a JSON encoder for this type so we can tag each beacon element with a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/data-*"&gt;data attribute&lt;/a&gt; containing its position in the outline. We'll then set up our JavaScript to find all elements with such a data attribute in the DOM and feed their coordinates back to us on each drag event. That will allow us to define a type for drag events containing the positions of our beacons on the screen.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;DragMsg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Coords&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;beacons&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="kt"&gt;Beacon&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Beacon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;CandidatePosition&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Rect&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Remember the &lt;code&gt;closestRect&lt;/code&gt; function from the polygon example? It's what we need to find the &lt;code&gt;CandidatePosition&lt;/code&gt; closest to the cursor! Once we know which candidate position is closest, all we need is a function to that moves a node to its new position in an outline. It's a tricky function to write, but it doesn't have much to do with drag &amp;amp; drop and so I'm skipping the implementation here. I include a solution with the &lt;a href="https://ellie-app.com/6gbMfDjCnH4a1"&gt;outline editor source code&lt;/a&gt;. For those interested in some thoughts on how to approach data transformations like these, I refer to an earlier &lt;a href="https://dev.to/jwoudenberg/conversion-functions-five-stars-2l87"&gt;post on conversion functions&lt;/a&gt;, which includes an example of a similar tree manipulation problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Necessary JavaScript
&lt;/h2&gt;

&lt;p&gt;I promised I'd get back at the JavaScript required to make these examples work. All three examples use the same JavaScript code, because it turns out they have the same needs. In every example there's one or more Html elements on the page that we need to track the position and dimensions of as a drag interaction takes place. What our JavaScript code needs to do is generate events when the mouse gets pressed, moved, and released, and bundle with those events the positions of all elements we want to track. We identify those elements by giving them a data attribute with their 'beacon ID'.&lt;/p&gt;

&lt;p&gt;There's tons of ways to write this code and I don't believe mine is particularly insightful, so I'll not reprint it here. The &lt;a href="https://gist.github.com/jwoudenberg/38d5f63dcdbb288525f1694f238bd4ed"&gt;draggable.js source code&lt;/a&gt; for these examples is available though for those interested.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;When we need to perform a complicated task it's natural to start by looking for a library to do the heavy lifting for us. For building drag &amp;amp; drop interactions libraries give us draggables and dropzones but they are often a bad fit for drag &amp;amp; drop UIs, of which we've seen three in this post.&lt;/p&gt;

&lt;p&gt;Are drag &amp;amp; drop libraries always a bad idea? I don't think so. In particular there are libraries for specific drag &amp;amp; drop widgets such as re-arrangable lists, for example &lt;a href="https://package.elm-lang.org/packages/annaghi/dnd-list/latest/"&gt;annaghi/dnd-list&lt;/a&gt;. Using those when possible could save a lot of time. There are probably UIs where draggables and dropzones are precisely the right abstraction. Please send me a note if you ran into one of those, I'd love to learn about it! Drag &amp;amp; drop covers an incredibly broad range of functionality though, and so often an off-the-shelf solution will not be available. For those I'd put serious thought into whether draggables and dropzones are going to help build the UI or make it harder.&lt;/p&gt;

&lt;p&gt;I recommend using 'beacon elements' if you build your own drag &amp;amp; drop behavior. These are regular DOM elements marked so we can access their location on every drag event. Because beacon elements don't need to &lt;em&gt;do&lt;/em&gt; anything any element using any positioning strategy can be a beacon. This passive nature distinguishes beacons from draggables or dropzones, both of which include behavior.&lt;/p&gt;

&lt;p&gt;I've showed a different approach to building drag &amp;amp; drop UIs. In this approach we subscribe to drag &amp;amp; drop events telling us what's happening in the DOM, update the state of our model based on those events, and finally update the screen to reflect the new state of the model. This is nothing more than the Elm architecture. I hope the examples in this post show writing drag &amp;amp; drop logic does not need to be an arduous task.&lt;/p&gt;

&lt;p&gt;Thanks to Ary, Blake, Brian and Stöffel for their reviews of drafts!&lt;/p&gt;

</description>
      <category>draganddrop</category>
      <category>elm</category>
    </item>
    <item>
      <title>A type for views</title>
      <dc:creator>Jasper Woudenberg</dc:creator>
      <pubDate>Mon, 02 Jul 2018 13:00:43 +0000</pubDate>
      <link>https://dev.to/noredink/a-type-for-views-5g04</link>
      <guid>https://dev.to/noredink/a-type-for-views-5g04</guid>
      <description>&lt;p&gt;&lt;em&gt;This post originally appeared on the &lt;a href="https://blog.noredink.com/post/175470670558/a-type-for-views" rel="noopener noreferrer"&gt;NoRedInk blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One of the joys of writing Elm apps is the many ways the compiler catches our mistakes, and the useful error messages it gives us when it does. It's a two-way street though, meaning that the degree to which the compiler can weed out mistakes depends on what information we disclose to it. In other words, we have to help the compiler help ourselves. In this post we'll see that this is truer for the view function than anywhere else, and we'll see how we can help the compiler help us get our view functions right.&lt;/p&gt;

&lt;h2&gt;
  
  
  A book recommendation application
&lt;/h2&gt;

&lt;p&gt;Suppose we're building a web page showing the latest book releases of your favorite authors, ordered by popularity. The main page of the app is a simple list of books, listing the title, author name, and the ISBN of each book. Lets create a model for this application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;authors&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="kt"&gt;Author&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;books&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="kt"&gt;Book&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;favorite&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Book&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbn&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;popularity&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next up is the view function of this application. For this view we need to transform the data from our model a bit. First of all we're not interested in displaying books of authors the user has not favorited, so we can filter those out. Secondly, we want to order the books of the remaining authors by popularity. Thirdly we want to display items on the page that contain data from the &lt;code&gt;Book&lt;/code&gt; (title, isbn) and &lt;code&gt;Author&lt;/code&gt; (name) types.&lt;/p&gt;

&lt;h2&gt;
  
  
  Intermezzo: Are we sure we want to do this?
&lt;/h2&gt;

&lt;p&gt;But wait, maybe instead of writing a transformation function it would be easier to change our &lt;code&gt;Model&lt;/code&gt; type to more closely resemble the structure of the page! For example, suppose our model looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;books&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="kt"&gt;Book&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Book&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;authorName&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;authorIsFavorite&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbn&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;popularity&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now our view function will be much easier to write. But this model comes with a disadvantage. Two books of the same author can disagree on whether that author is one of the users favorites. The previous model prevented this impossible state because the &lt;code&gt;favorite&lt;/code&gt; status of an author was only stored in a single place. When faced with this trade-off we prefer our model to prevent as many impossible states as possible, even if our view function will need do some transformations.&lt;/p&gt;

&lt;h2&gt;
  
  
  View transformations at NoRedInk
&lt;/h2&gt;

&lt;p&gt;Many of the Elm apps we have at NoRedInk have a model that looks quite different from the Html we draw on the screen. As a result our view functions will need to transform data, but that's not a problem, especially if the compiler helps make sure we're transforming correctly. But there's the rub: we'll need to work a little bit to get the compiler to help us here.&lt;/p&gt;

&lt;p&gt;To understand why, let's look at a transformation present in every Elm app: The update function that transforms an old model state into a new one. The compiler helps us a lot with this one, it ensures that regardless of what happens our update function will always return a valid new model, which prevents all sorts of bugs. But when it comes to the view function we get no such guarantee, the compiler only ensures it returns Html. If all goes well that Html will look like our specs, but it might as well be the image of a cat as far as the compiler is concerned. Both are Html and so equally valid results of calling our view function.&lt;/p&gt;

&lt;p&gt;To prevent such bugs we put some extra constraints on what we consider valid output from a view function. We can do this by defining a new type describing the shape of our page. Our view function we then split into two parts. First we transform our model into this &lt;code&gt;View&lt;/code&gt; type, this is the hard part. Then we take that type and render it into Html. Because the type was designed to be similar to the structure of the page, this second part should be easy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's transform some books
&lt;/h2&gt;

&lt;p&gt;Let's get back to our book recommendation app and write a &lt;code&gt;View&lt;/code&gt; type for it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="kt"&gt;BookView&lt;/span&gt;


&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;BookView&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;authorName&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbn&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;popularity&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can write the transformation function that turns our &lt;code&gt;Model&lt;/code&gt; into a &lt;code&gt;View&lt;/code&gt; type. This is the hardest part of the job, but because the transformation takes place between two specialized types &lt;code&gt;Model&lt;/code&gt; and &lt;code&gt;View&lt;/code&gt;, we will get a lot of help from the Elm compiler.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;toView&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt;
&lt;span class="n"&gt;toView&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;authors&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;favorite&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;concatMap&lt;/span&gt; &lt;span class="n"&gt;booksForAuthor&lt;/span&gt;


&lt;span class="n"&gt;booksForAuthor&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Author&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="kt"&gt;BookView&lt;/span&gt;
&lt;span class="n"&gt;booksForAuthor&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;books&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbn&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;popularity&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;authorName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isbn&lt;/span&gt;
                &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;popularity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;popularity&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sortBy&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;popularity&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, all that's left is to write the view function that renders our &lt;code&gt;View&lt;/code&gt; type to &lt;code&gt;Html&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;
&lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;toHtml&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toView&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;toHtml&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;
&lt;span class="n"&gt;toHtml&lt;/span&gt; &lt;span class="n"&gt;books&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ul&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;viewBook&lt;/span&gt; &lt;span class="n"&gt;books&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;viewBook&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;BookView&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;
&lt;span class="n"&gt;viewBook&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;li&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Author: "&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;authorName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Title: "&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ISBN: "&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isbn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We've seen how we can use a &lt;code&gt;View&lt;/code&gt; type to write our view function using some extra help from the compiler.&lt;/p&gt;

&lt;p&gt;As a nice side-effect, this approach also makes it easy to unit-test our view logic. We can write ordinary Elm unit tests for our &lt;code&gt;toView&lt;/code&gt; function, which contains all of our actual transformation logic. This allows us to write less tests against our &lt;code&gt;Html&lt;/code&gt; returning view function, which although possible is harder.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foyy0h8rawyzl596pjyh3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foyy0h8rawyzl596pjyh3.png" width="100" height="100"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Stöffel&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://twitter.com/schtoeffel" rel="noopener noreferrer"&gt;@schtoeffel&lt;/a&gt;&lt;br&gt;
Engineer at &lt;a href="http://noredink.com" rel="noopener noreferrer"&gt;NoRedInk&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft0xvt33lzq8y5bt1ib80.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft0xvt33lzq8y5bt1ib80.png" width="100" height="100"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Jasper Woudenberg&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://twitter.com/jasperwoudnberg" rel="noopener noreferrer"&gt;@jasperwoudnberg&lt;/a&gt;&lt;br&gt;
Engineer at &lt;a href="http://noredink.com" rel="noopener noreferrer"&gt;NoRedInk&lt;/a&gt;&lt;/p&gt;

</description>
      <category>elm</category>
    </item>
  </channel>
</rss>
