This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-01-10
Channels
- # announcements (1)
- # babashka (9)
- # beginners (372)
- # calva (3)
- # cider (9)
- # cljsrn (71)
- # clojure (57)
- # clojure-australia (2)
- # clojure-europe (26)
- # clojure-france (11)
- # clojure-nl (11)
- # clojurescript (5)
- # core-typed (1)
- # fulcro (33)
- # meander (3)
- # off-topic (1)
- # pathom (3)
- # re-frame (4)
- # reagent (6)
- # sci (1)
- # shadow-cljs (34)
- # xtdb (14)
(fn-name input-string [7 9 13] [25 26 29]) i am writing function where i am planning to apply substring of for each values of vactor example (sub input-string 7 25) (sub input-string 9 26) (sub input-string 13 29) How can I do it in clojure?
One way to do this would be to use map
. It takes a mapping function and any number of collections. The mapping function needs to take as many parameters as there are collections passed to map
.
The mapping function will then receive the first element of each collection as the its first set of arguments, then the second elements as the second set of arguments and so on.
So this
(map f [1 3] [2 4])
is roughly equivalent to
[(f 1 2)
(f 3 4)]
Does this help you?
I tried with map and that helped 🙂 sorru for late reply, wanted to complete the functionality
hmm, what did I not understood here :
; Use the list function, quoting, and read-string to create a list that, when
; evaluated, prints your first name and your favorite sci-fi movie.
(list '(read-string str) "Roelof Wobben, Star Wars")
(eval (list '(read-string str) "Roelof Wobben, Star Wars") )
error:
; Execution error (ClassCastException) at chapter7/eval16506 (form-init11571273748977578313.clj:9).
; class clojure.core$str cannot be cast to class java.lang.String (clojure.core$str is in unnamed module of loader 'app'; java.lang.String is in module java.base of loader 'bootstrap')
'(read-string str)
results in a list of the symbols read-string
and str
. When evaluated, it results in a function call to read-str
with whatever is bound to str
as its argument. So it's passing the function str
into read-str
.
The task is effectively to create a list of the symbol read-string
and your text.
(list '(read-string "my-text"))
would result in the list: ((read-string "my-text"))
, since you are quoting the list (read-string "my-text")
already.
You only need to quote read-string
.
So what it's asking for is this: (list 'read-string "my-text")
Getting used to quoting can take a while 🙂
Am I right that only "Roelof" is printed here
(list 'read-string "Roelof Wobben, Star Wars")
(eval (list 'read-string "Roelof Wobben, Star Wars"))
oh, sorry I think I misunderstood the task a bit. So the list we now create is this:
(read-string "Roelof Wobben, Star Wars")
But read-string
will only read the first form from a given string, which in this case will by the symbol Roelof
.
Is there a more detailed description to the task or any previous tasks that this one builds on top?
but this is the page everything is explained : https://www.braveclojure.com/read-and-eval/
Ah, braveclojure! I learned the language from there about 3 years ago. 🙂 Maybe I have my exercise code still lying around somewhere
I use it now for some 2 weeks to learn clojure but for every challenge I needed some help. Very frustating
Looking at the examples in that chapter, the exercise is just to get your familiar with those functions and concepts by combining them in some ways. One of my solutions was this:
(eval (read-string "(list 'Dirk 'Interstellar)"))
Don't beat yourself up if you don't get it right away. Especially all the macro related stuff can take some time. I also do remember that I found the second exercise in that chapter very tricky, so I skipped it at first and came back to it later.
(- (* 4 (+ 1 3)) 5)
does not equal (1 + 3 * 4 - 5)
, *
needs to take precedence over +
.
I can give you a link to my solution for the second exercise, if you want something to compare against.
make things to complicated
(defn infix [expr]
(let [splitted (str/split (subs expr 1 (- (count expr) 1)) #" ")]
))
do you take the expression as a string?
try passing it as a list instead by just quoting it:
(infix '(1 + 3 * 4 - 5))
That way you can use all of Clojure's data manipulation functions directly.
hmm, almost there I hope
(defn infix [expr]
(let [first (first expr)
second( nth expr 3)
thirth (nth expr 5)
fourth (nth expr 7)
fifth (nth expr 9)
sixth (nth expr 11)
seventh (nth expr 13)]
(list sixth(list second) )
))
(infix "(1 + 3 * 4 - 5)")
but that gives :
(\- (\+))
as expected. You are creating a list, consisting of the sixth element, and a nested list with the second element.
second element is +
, sixth is -
I think I got it :
(defn infix [expr]
(let [first (first expr)
second( nth expr 3)
thirth (nth expr 5)
fourth (nth expr 7)
fifth (nth expr 9)
sixth (nth expr 11)
seventh (nth expr 13)]
(list (read-string (str sixth)) (list (read-string (str second)) 2 3)1)
))
this gives the right answerare you still passing expr as a string?
do (eval (infix '(1 + 3 * 4 - 5)))
instead, then you can omit your read-string
and str
calls
you need to adjust your nth
indices then, too.
I assume your indices are off by 1 now.
can you paste your current code ?
(defn infix [expr]
(let [first (first expr)
second( nth expr 2)
thirth (nth expr 3)
fourth (nth expr 4)
fifth (nth expr 5)
sixth (nth expr 6)
seventh (nth expr 7)]
(list (sixth (list (second 2 3) )))))
(infix '(1 + 3 * 4 - 5))
off by 1. indices start at 0, so your second element will be at index 1 and so on.
still not well
(defn infix [expr]
(let [first (first expr)
second( nth expr 1)
thirth (nth expr 2)
fourth (nth expr 3)
fifth (nth expr 4)
sixth (nth expr 5)
seventh (nth expr 6)]
(list (sixth (list (second 2 3) )))))
(eval (infix '(1 + 3 * 4 - 5)) )
; Syntax error (IllegalArgumentException) compiling at (chapter7.clj:28:1).
; Can't call nil, form: (nil)
which line is line 28?
(list (sixth (list (second 2 3) )))
That part needs fixing. sixth
and second
are now bound to the symbols -
and +
. Calling a symbol as function here will just return nil, so the list you return from infix
ends up being (nil)
, which when eval'd results in that error.You want to construct lists with those symbols as the first element instead, so don't nest them in a function call
(list sixth (list second 2 3))
This works :
(defn infix [expr]
(let [first (first expr)
second (nth expr 1)
thirth (nth expr 2)
fourth (nth expr 3)
fifth (nth expr 4)
sixth (nth expr 5)
seventh (nth expr 6)]
(list sixth (list second (list fourth thirth fifth) first) seventh)))
congrats!
Now next step would be to make it work for other formulas, like (3 * 2 - 1)
and others
yes. I made a function which takes two operators, and returns true if the first one takes precedence over the second. To determine that, I put priority values in a map (one for each of the basic operators + - * /
) and then looked up the values for both operators in the map.
If the value for the first operator is higher than the second, I returned true.
This was my priority map, just to give you an idea: (def priorities {'+ 1, '- 1, '* 2, '/ 2})
To make your life easier, you can try to implement your infix function only for lists of exactly 5 elements first. (e.g (infix '(a op1 b op2 c))
) That way you only have to worry about 2 operators for now.
(defn infix2 [expr]
(let [splitted (re-matches #"((d+) (.) (d+) (.) (d+))" expr)]
splitted))
(infix2 '(10 + 2 * 3))
error:
; Execution error (ClassCastException) at chapter7/infix2 (form-init6806598393956264445.clj:29).
; class clojure.lang.PersistentList cannot be cast to class java.lang.CharSequence (clojure.lang.PersistentList is in unnamed module of loader 'app'; java.lang.CharSequence is in module java.base of loader 'bootstrap')
you are trying to use strings again
keep in mind that expr
is just a list of alternating values and operators.
so no. regex bad. (in this case at least 😄)
Also I'd like to point you at a section in a previous chapter: https://www.braveclojure.com/do-things/#Destructuring
you can destructure your expr
in the parameter list of the function for easy access to the elements of it.
yep, I can do that , I did that in my former solution But it is then not a problem when there are more or less parameters
I mean I can do this again :
(let [first (first expr)
second (nth expr 1)
thirth (nth expr 2)
fourth (nth expr 3)
fifth (nth expr 4)
It does work, but we can get to it after we made your approach work. 🙂
so I can start with this :
(defn infix [expr]
(let [first (first expr)
second (nth expr 1)
thirth (nth expr 2)
fourth (nth expr 3)
fifth (nth expr 4)
sixth (nth expr 5)
seventh (nth expr 6)]
Yes, but you need to adjust it. The minimum case for the infix is an expression of 3 elements. For example (1 + 3)
. This means that you can't use nth
for anything after the third element, since if there is no fourth, it will error with an IndexOutOfBounds.
So how can you remove the first 3 elements from expr
to get a (potentially empty) list of all remaining elements?
If you have a list of remaining elements, you can check if it is empty. If it is, you have the base case and can just return a list of the second, first, and third element.
we have then this :
(defn infix2 [expr]
(let [first (first expr)
second (nth expr 1)
thirth (nth expr 2)
r (drop 3 expr)]
))
at this point I would suggest to use other names than first
and second
, because you are hiding the respective clojure core functions by doing that.
Yeah. That's what I ended up using too. You can also be more explicit, like first-number
, operator
, second-number
. It's totally up to you in the end, it just should be easy to read and understand when you come back to the code later.
oke, we have this :
(defn infix2 [expr]
(let [first-number (first expr)
operator (nth expr 1)
second-number (nth expr 2)
r (drop 3 expr)]
(if (empty? r))
(list operator first-number second-number)
))
correct
if r
is not empty, where will the second operator be?
more precisely it will be the first element of r.
Also I just noticed your if
does not surround the (list ..
below it
you have some options to lookup the priorities, where these 2 would be the most commonly used: Call get
with the priorities map and an operator or use the priorities map itself as the lookup function.
If I'm giving you too many directions at any point, let me know. I don't want to ruin the learning experience by just pushing a solution onto you 😅
so like this :
(defn infix2 [expr]
(let [first-number (first expr)
operator (nth expr 1)
second-number (nth expr 2)
r (drop 3 expr)]
(if (empty? r)
(list operator first-number second-number)
(if ( < (get priorities operator) (get priorities (first r)))
"do something"
"do something else"
))))
exactly
he, im i righht here
1 + 2 * 3
will be in clojure (+ ( * 2 3) 1)
and
2 * 3 + 1
will also be (+ ( * 2 3) 1) `
partially correct. only if the second operator has a higher priority
in the first case I would do something like (list operator(list (infix2 r second) first-number)
but that would not work because then the first-number will be the operator and that is not good
let's first deal with the (in my opinion) easier case, which is the second case.
How would you do that?
not quite. You need to prepend (operator first-number second-number)
to r
, and pass that into infix2
sorry I mean (list operator first-number second-number)
yeah, that should do it
let's break it up. What would the nested call to infix2 look like?
yes, but infix2 only takes one argument, so you need to prepend second-number
to r
infix2 instead of index, but yeah 😄
so the call will be (list operatotor (list (infix2 (conj r second-number))) first-number)
code so far :
(defn infix2 [expr]
(let [first-number (first expr)
operator (nth expr 1)
second-number (nth expr 2)
r (drop 3 expr)]
(if (empty? r)
(list operator first-number second-number)
(if (< (get priorities operator) (get priorities (first r)))
(list operator (list (infix2 (conj r second-number))) first-number)
(infix2 (conj r (list operator first-number second-number)))))))
there are 2 issues here
(list operator (list (infix2 (conj r second-number))) first-number)
1. you don't need to put the nested infix2 in another list
thanks, this seems to work
(defn infix2 [expr]
(let [first-number (first expr)
operator (nth expr 1)
second-number (nth expr 2)
r (drop 3 expr)]
(if (empty? r)
(list operator first-number second-number)
(if (< (get priorities operator) (get priorities (first r)))
(list operator (infix2 (conj r second-number)) first-number)
(infix2 (conj r (list operator first-number second-number)))))))
2. first-number should come after operator, not as the last element (it doesn't make a functional difference here, but it would if you want to expand the functionality to work with nested infix expressions like (3 * (1 + 2))
)
well done!
pff, still have the feeling that I have to learn a lot how to approach this sort of problems
That comes with time and practice
ah, no worries, I was only playing games on my main monitor most of the time 😄
I would like to quickly go back to the topic of destructuring if you have a few more minutes
sure, what's it?
which is correct
which is also correct
if you imagine replacing 2 * 3
with 6 ->
(10 + 6) -> (+ 10 6)
(6 + 10) -> (+ 6 10)
it's just keeping the order of arguments like they were in the original expression. Which is expected in my opinion.
You don't want it to accidentally re-order the arguments to a division or subtraction.
Now that I think about it, that is the actual important reason for the 2) issue I mentioned earlier. :thinking_face:
yeah, I never used it much when I was working with Java, but now I use it quite regularly
That's more than I know. I only know Java and Clojure 😅
So quickly back to destructuring: The infix function breaks expr
into 4 parts: the first three elements and the rest after those. Which can be done with destructuring quite easily:
(defn infix2 [[first-number operator second-number & r]]
(if (empty? r)
(list operator first-number second-number)
(if (< (get priorities operator) (get priorities (first r)))
(list operator first-number (infix2 (conj r second-number)))
(infix2 (conj r (list operator first-number second-number))))))
I thought that as well at first, but destructuring works in quite a few places
want a small extra challenge? 😄
I think that should be doable. The challenge would be to make your infix function work with arbitrarily nested expressions. Example
(infix2 '(3 * (2 + (5 - 3) / 2)))
=> (* 3 (+ 2 (/ (- 5 3) 2)))
there is only one spot that needs to be changed
In case the book did not mention it yet: To check if something is a list, you call (list? the-thing)
makes sense in OOP, since you rather use methods and inheritance there. But since we are just juggling data around here, we need to be able to check what kind of data we're dealing with.
I see what needs to be changed
; (* 3 (+ 2 (/ (- 5 3) 2))) => current outcome
; (* 3 (2 + (5 - 3) / 2)) => preffered outcome
technically correct, but maybe not in the way you think right now. Where would you put that if and what would it check for?
I was thinking just before the if then we have now and it would do a check if first number is a list
why do you want to check it there?
now what would you do if the first argument is a list and not a number?
not really
if an argument is a list, (so a nested expression) we want to turn it into its infix representation.
there's only one place in the function where all arguments are ultimately put into an infix format. At that place, you either put in the argument itself or the infix version of it if it's a list.
yep exactly. one for each first-number
and second-number
so like this :
(list operator (if (list? first-number) ....) (if (list? second-number) ....)
chips, I does not know about match
(defn infix2 [[first-number operator second-number & r]]
(if (empty? r)
(list operator (match [(list? first-number) (list? second-number)]
[true false] "do something"
[false true] "do something else"
[true true] "do another something"))
(if (< (get priorities operator) (get priorities (first r)))
(list operator first-number (infix2 (conj r second-number)))
(infix2 (conj r (list operator first-number second-number))))))
and this is not going to solve it
(ns chapter7
(:require [clojure.core.match :refer [match]]))
you are thinking to complicated 🙂
match is a cool thing, but definitely not needed here
let's rename first-number
to a
and second-number
to b
for now, makes it easier to write about 😄
I had this :
(defn infix2 [[first-number operator second-number & r]]
(if (empty? r)
(list operator (if (list? first-number)
(list operator (infix2 firstnumber) second-number)
)
(if (list? second-number)
(list operator firstnumber (infix2 second-number))
)
(if (< (get priorities operator) (get priorities (first r)))
(list operator first-number (infix2 (conj r second-number)))
(infix2 (conj r (list operator first-number second-number)))))))
and witth this I get a null pointer exception
(defn infix2 [[first-number operator second-number & r]]
(if (empty? r)
(list operator (if (list? first-number)
(list operator (infix2 first-number) second-number)
(if (list? second-number)
(list operator first-number (infix2 second-number))
(list operator first-number second-number)
)
)
(if (< (get priorities operator) (get priorities (first r)))
(list operator first-number (infix2 (conj r second-number)))
(infix2 (conj r (list operator first-number second-number)))))))
you are already within (list operator ...
, you don't need to call that again in the nested ifs. The nested ifs should only return either the number or the infixed nested list.
keep in mind that everything is an expression. Those nested ifs do not directly return from your infix2 function.
(defn infix2 [[first-number operator second-number & r]]
(if (empty? r)
(list operator (if (list? first-number)
(infix2 first-number)
(if (list? second-number)
(infix2 second-number)
(first-number second-number))))
(if (< (get priorities operator) (get priorities (first r)))
(list operator first-number (infix2 (conj r second-number)))
(infix2 (conj r (list operator first-number second-number))))))
; Execution error (ClassCastException) at chapter7/infix2 (form-init6806598393956264445.clj:34).
; class java.lang.Long cannot be cast to class clojure.lang.IFn (java.lang.Long is in module java.base of loader 'bootstrap'; clojure.lang.IFn is in unnamed module of loader 'app')
you also shouldn't nest the (if (list? second-number)
in the check for the first one
instead:
(list operator
(if (list? first-number)
(infix2 first-number)
)
(if (list? second-number)
(infix2 second-number)
))
both ifs are independent of each other.
There is an else needed for both of them
I just don't want to give it to you straight away 🙂
first-number and second-number can be lists or numbers completely independent of each other
(1 + 2) ((1 * 3) + 2) (1 + (5 *2))
in all those cases operator
is the +
, and first-number and second-number can be anything
if an argument is not a list, it's a number. If it's a number we don't need to do anything with it, so we can just return it.
or we must do something like this :
(defn infix2 [[first-number operator second-number & r]]
(if (empty? r)
(list operator
(if (list? first-number)
(infix2 first-number)
first-number)
(if (list? second-number)
(infix2 second-number)
second-number
))
(if (< (get priorities operator) (get priorities (first r)))
(list operator first-number (infix2 (conj r second-number)))
(infix2 (conj r (list operator first-number second-number))))))
exactly :thumbsup:
yes 🙂
That's always the hardest part in my opinion. Breaking down the problem into the smallest pieces that can be individually solved.
How old are you if I may ask?
A rare occasion where I don't feel old with my 34 then. 😄
I think I deserve a break and maybe tomorrow or later the chapter about writing macros
I hope this month I will finish the brave book and hopefully not feeling like a super beginner
Feel free to contact me directly anytime if you have any questions or need help again.
Also huge respect for your patience with that stuff. I think I took multiple breaks during those sections in the book.
and I were several times on a point I would quit but because of persons like you I see and learn things
It's the only beginner clojure book I have read, so I can't really tell how good it is compared to others. But it is the one that ultimately got me to work with Clojure fulltime, so it can't be too bad 😄
There is currently a beginner meetup going on where participants read through the brave book, and then talk about it in occasional meetings to compare their exercise solutions and what they learned. Would this be something you'd be interested in?
if you are on Twitter: https://twitter.com/asamonek/status/1344913238933315585
same here, though I only have my cats to feed in between their sleep schedule
So you can probably just ask @asamonek here or on Twitter if you can join. Next meeting would be on 20.01. at 17:00 GMT+1. Topic for that meeting will be Chapter 3 of the book, so you are already ahead of the pack. 🙂
> my special need daugther is more important then a well payed job Very true. Fortunately 2020 made it clear to pretty much every company that software developers can work from home. I worked from home with Clojure for a British company for the past 2 years.
yeah understandable. Working from home is definitely less social than in office.
backend for a website. Frontend was regular HTML+JS, but I didn't have to work on that.
so everything between incoming requests and the database.
and some smaller services around it, like for sending emails and notifications
oke, when Im that far I like to write back-end for a gallery where the data is coming from the rijksmuseum api and a second project is a sort of crm for a volunteer organisation
to keep track of payments, who has a subcription and if the subscriptions are payed or not
first getting better in clojure and then look what I can use to make the two ideas work
tomorrow study this page : https://www.braveclojure.com/writing-macros/#Exercises
> I like to write back-end for a gallery where the data is coming from the rijksmuseum api and a second project is a sort of crm for a volunteer organisation sounds like great projects! I'd love to see them in action some day 🙂
looked at the challenges of the writing macros and I hope I can solve them. right now I doubt it
for me too. I'll be around in slack tomorrow again. see you 👋
we use a whole bunch of individual Clojure and Java libraries, not a prepackaged framework. I don't remember many of the libraries though, I don't have access to the code to check atm. The ones I remember are • compojure • ring and various middleware • component • jsonista • clojure.java.jdbc • clj-time
now the most difficult ones
(defn defattrs [& functions]
'(do .....))
(def c-int (comp :intelligence :attributes))
(def c-str (comp :strength :attributes))
(def c-dex (comp :dexterity :attributes))
(defattrs c-int :intelligence
c-str :strength
c-dex :dexterity)
I'm occupied with work atm, I'll probably be available in ~2 hours
A test in this exercism problem wants this: (is (thrown? IllegalArgumentException (nth-prime/nth-prime 0)))
and so far my code has this: (if (< n 1) (throw (IllegalArgumentException.))
which seems to do what I want but the test results tell me: Exception in thread main, syntax error caused by ... java.lang.IllegalArgumentException. tests failed
@chase-lambert try or
instead of if
hmm, but my else branch of the if is what solves the actual problem. I tried using a :pre
condition but that didn't pass the test either.
huh, now that I've actually instituted the else branch (I was just returning n while seeing if my exception logic worked) all tests pass. Sorry for wasting your time @jr0cket
I have to supply a lot of functions that all do the same thing and was wondering how to make one function instead of a lot. Here is an example:
(defn count-a [s] (my-counter "a" s))
(defn count-b [s] (my-counter "b" s))
(defn count-c [s] (my-counter "c" s))
....
Is it possible to replace this somehow? Like having a function that can generate function names and if a user would call count-ß it would be replied with the correct function?I think this might be my solution: https://stackoverflow.com/a/7854594/4919081 but I don't understand it
So you would like to be able to do something like this?
(defcount "a")
;; now a function with the name count-a exists
(defmacro defcount [x]
`(defn ~(symbol (str "count-" x)) [s#] (my-counter ~x s#)))
(pprint (macroexpand-1 '(defcount "a")))
(clojure.core/defn
count-a
[s__155__auto__]
(user/my-counter "a" s__155__auto__))
of course, if you're going to write a macro to make one function, you might want it to make many too
yeah, it's just wrapping a doseq around the body basically
So, when something is accessing my name-space, every function inside will be called. So when the function is making functions, these will be available? what happens if I leave something that prints? will it be printed everytime someone uses my namespace?
All top level forms in a namespace are evaluated when you require or use a namespace. If some of those cause printing, then that will happen
Print statements inside of defn forms are not evaluated until the function is called, but print at top level, or in do doseq etc will all be evaluated at require time
If a top level form in a namespace executed a loop, and each iteration of that loop causes something to be defn’d, then that will happen at the time the namespace is required
I am currently learning about Components and while googling I came across this example app https://github.com/seancorfield/usermanager-example/ In the WebServer component they associate a promise in the map here https://github.com/seancorfield/usermanager-example/blob/develop/src/usermanager/main.clj#L165 What is the motivation of doing that?
https://github.com/seancorfield/usermanager-example/blob/develop/src/usermanager/main.clj#L232
when running from main that will block waiting for the promise to be delivered, which only happens when the component shutsdown. so in practice, this will just never be fulfilled and the webserver runs forever.
But it also allows you to shutdown the server via code if you need to.
I am not sure I understand. I thought in -main they start the server but the comment says the opposite
The component is started, which starts the web server, and then the promise is pulled out of that component and waited on.
In normal operation, it'll just wait forever. But you could deliver the promise via a REPL to shut it down.
Or you could just start/stop the component yourself in the REPL.
i don't see how you could shut it down in code though. you'd need access to the component that started to get access to that promise?
When we started out, we used this pattern, along with code that allowed a specific URL to be hit with certain params to deliver the promise (and shut the process down).
> The component is started, which starts the web server, and then the promise is pulled out of that component and waited on. I think that makes sense now. Thanks!
More recently, we tend to record the started component into a top-level Var so it can be manipulated via a remote REPL more easily.
I've been meaning to clean up how the usermanager example works to better match what we do at work.
It seems like having conditional logic to either start Jetty or http-kit is confusing for folks -- but it was done to show how easy it is to switch web servers.
Is Component the most popular library for that kind of pattern? I see there are Integrant, Mount, Clip that pop up when googling
Integrant has, I think, seven lifecycle hooks (compared to Component's two). Mount relies on global data (which just completely discounts it as a choice for me). I haven't looked at Clip.
Now that I think about it, the promise was added to make it easier to switch between Jetty and http-kit. Jetty by default blocks the main thread when you start it and you have to say :join? false
to have it be non-blocking; http-kit is non-blocking by default. That's what you want for REPL usage. But for -main
usage, you want some sort of blocking behavior -- otherwise you start the server and then immediately fall off the bottom of -main
and the server shuts down. Adding the promise means that -main
can wait on it, regardless of the web server in use. Shutting down the system doesn't matter much in -main
since the process is most likely going to stay running until it is externally killed. But in the REPL you're going to start the component (and the web server in a non-blocking way) and then later shut the component down when you're done (and so the promise doesn't really matter). I'll clean up the docs around that when I simplify the example.