joe

joe

Web Development with Clojure, Third Edition - Why is vec necessary in (vec (db/get-messages))

I’m really enjoying Web Development with Clojure, 3rd edition. Thanks to the authors for writing it. There are so many separate moving parts to web development in Clojure/Script, and while very powerful when understood, this book is really a “must read” for anyone new to the ecosystem.

While reading version B8.0 of the book I noticed something in the code examples that I’m having some difficulty understanding. Why do the later versions of the guestbook app need to call vec on the messages returned from the database?

In the earlier versions (see [1] below) of the app that render the page on the server, there’s no need to call vec:

[1]
Chapter 3, Think in Terms of Aplication Components > Routing Requests
pdf page: 74
book page: 61
File: https://media.pragprog.com/titles/dswdcloj3/code/guestbook/src/clj/guestbook/routes/home.clj

(defn home-page [request]
  (layout/render
   request "home.html" {:messages (db/get-messages)}))

But in later versions (see [2] and [3] below) of the guestbook app, which are using client-side rendering, vec is used:

[2]
Chapter 4, Build the UI with Reagent > Reimplementing the List
pdf page: 100
book page: 88
File: https://media.pragprog.com/titles/dswdcloj3/code/guestbook-reagent/src/clj/guestbook/routes/home.clj

(defn message-list [_]
  (response/ok {:messages (vec (db/get-messages))}))

[3]
Chapter 5, Services > Guestbook Messages
pdf page: 113
book page: 101
File: https://media.pragprog.com/titles/dswdcloj3/code/guestbook-services-1/src/clj/guestbook/messages.clj

(defn message-list []
  {:messages (vec (db/get-messages))})

When I removed the call to vec in [3], I noticed that the messages are displayed in ascending descending order of :id, which causes the more recent messages to show at the top of the page, instead of at the bottom as it does when vec is called.

I’m not clear how vec has this effect? When I try (vec (db/get-messages)) and just (db/get-messages) at the REPL, the messages are in the same order.

Marked As Solved

svmbrown

svmbrown

Author of Web Development with Clojure

Hi there Joe,

Thanks for reading, and thanks for the compliments!

Your observation that the ordering is the same between (db/get-messages) and (vec (db/get-messages)) is correct, and on the right track to figuring this out. If we were to update [1] to use (vec (db/get-messages)) you’d notice no change at all. In fact, if you write a few messages on [3] with the vec call removed (thus seeing them in the wrong order) and then refreshed the page, you’d see that they’d shuffle into the correct order. The fact that the issue only appears when we update our message-list is the key here.

We use vec here because the conj function prepends to lists (the default type from db/get-messages) but appends to vectors. Clojure does this because conj uses the most efficient method of inserting an element into a collection. Lists are implemented with cons cells, and so prepending is O(1) and appending is O(n), so conj prefers prepending. On the other hand, vectors are quite complex, but are optimized for append and lookup, so conj prefers appending. This is a subtle behaviour, but is an important one to know when working with Clojure.

Having our messages as a list was okay in [1], since we were never updating our list. We were always getting it fresh from the database. However, in [2] and [3], we are optimistically updating our message-list on the client. Since we’d like to append messages, we want a vector. We could convert it on the client side, but in practice it is preferable to have one consistent data model across the application. There is no need for us to keep messages as a list, so the most straightforward solution is to convert it as soon as we get it from our database.

Hope that helps. Enjoy the rest of the book!

Also Liked

joe

joe

Hi Scot,

Thank you for taking the time to write such a clear and thorough answer.

It’s much appreciated!

kentbull

kentbull

First of all, Thank you @joe for asking this question. I had the same question, mystified as to why vec was needed at all.

And thank you, @svmbrown for the clear and detailed answer. I learned something today because you took the time to teach it. For that I am grateful. Now I know why I would use vec and why you chose to use it here.

I’m loving this book and this community!

Where Next?

Popular Pragmatic Bookshelf topics Top

mikecargal
Title: Hands-On Rust (Chap 8 (Adding a Heads Up Display) It looks like ​.with_simple_console_no_bg​(SCREEN_WIDTH*2, SCREEN_HEIGHT*2...
New
rmurray10127
Title: Intuitive Python: docker run… denied error (page 2) Attempted to run the docker command in both CLI and Powershell PS C:\Users\r...
New
swlaschin
The book has the same “Problem space/Solution space” diagram on page 18 as is on page 17. The correct Problem/Solution space diagrams ar...
New
leba0495
Hello! Thanks for the great book. I was attempting the Trie (chap 17) exercises and for number 4 the solution provided for the autocorre...
New
hazardco
On page 78 the following code appears: <%= link_to ‘Destroy’, product, class: ‘hover:underline’, method: :delete, data: { confirm...
New
AufHe
I’m a newbie to Rails 7 and have hit an issue with the bin/Dev script mentioned on pages 112-113. Iteration A1 - Seeing the list of prod...
New
jonmac
The allprojects block listed on page 245 produces the following error when syncing gradle: “org.gradle.api.GradleScriptException: A prob...
New
creminology
Skimming ahead, much of the following is explained in Chapter 3, but new readers (like me!) will hit a roadblock in Chapter 2 with their ...
New
andreheijstek
After running /bin/setup, the first error was: The foreman' command exists in these Ruby versions: That was easy to fix: gem install fore...
New
Keton
When running the program in chapter 8, “Implementing Combat”, the printout Health before attack was never printed so I assumed something ...
New

Other popular topics Top

AstonJ
If it’s a mechanical keyboard, which switches do you have? Would you recommend it? Why? What will your next keyboard be? Pics always w...
New
dasdom
No chair. I have a standing desk. This post was split into a dedicated thread from our thread about chairs :slight_smile:
New
brentjanderson
Bought the Moonlander mechanical keyboard. Cherry Brown MX switches. Arms and wrists have been hurting enough that it’s time I did someth...
New
AstonJ
Curious to know which languages and frameworks you’re all thinking about learning next :upside_down_face: Perhaps if there’s enough peop...
New
AstonJ
This looks like a stunning keycap set :orange_heart: A LEGENDARY KEYBOARD LIVES ON When you bought an Apple Macintosh computer in the e...
New
PragmaticBookshelf
Build highly interactive applications without ever leaving Elixir, the way the experts do. Let LiveView take care of performance, scalabi...
New
DevotionGeo
The V Programming Language Simple language for building maintainable programs V is already mentioned couple of times in the forum, but I...
New
AstonJ
Continuing the discussion from Thinking about learning Crystal, let’s discuss - I was wondering which languages don’t GC - maybe we can c...
New
PragmaticBookshelf
Author Spotlight James Stanier @jstanier James Stanier, author of Effective Remote Work , discusses how to rethink the office as we e...
New
PragmaticBookshelf
Build efficient applications that exploit the unique benefits of a pure functional language, learning from an engineer who uses Haskell t...
New

Sub Categories: