beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
2021-04-14T00:04:52.045900Z

the type function works in clj and cljs

👍 1
Prabu Rajan 2021-04-14T05:09:14.055700Z

Hello, I am new to clojure coming from the Java and Javascript world. I think its a beautiful, expressive language and I am loving learning it. I am building a small headless restaurant app. My data is : menu ( a map of id -> { name, price, quantity } ), orders (a map of id -> [ { name, quantity } ] ). Both menu and orders are atoms I am trying to write a function that takes in a list of order-items and creates an order. So, I need to deduct the ordered quantity from the overall inventory from the menu map and then create an order. Below is my implementation. I see that only the orders atom gets updated, and the menu atom isnt. However, if I separate the swap atom code for menu into a separate function, it works. Please help pointing out what is wrong

(def orders (atom {}))
(def menu (atom {}))

(defn create-order [{:keys [order-items]}]
  {:pre [(s/assert ::order-items order-items)]}
  (for [item order-items]
      (swap! menu update-in
             [(keyword (slugify (:name item))) :quantity]
             #(- % (:quantity item))))
  (swap! orders
         assoc
         (keyword (str (UUID/randomUUID))) order-items))

(create-order {:order-items [{::name "chilly parotta"
                              ::quantity 30}
                             {::name     "mutton biriyani"
                              ::quantity 3}
                             {::name     "masal dosa"
                              ::quantity 1}]})

seancorfield 2021-04-14T05:13:27.057Z

@prabu.rajan for is lazy. Functions evaluate each expression but only return the last expression. So a lazy expression is only “evaluated” at the top level, effectively.

seancorfield 2021-04-14T05:13:48.057500Z

(so your for doesn’t get realized so it doesn’t do anything)

seancorfield 2021-04-14T05:14:55.058400Z

In general, you should not mix laziness with side-effects so you should use doseq (imperative) here rather than for (which produces a lazy sequence).

seancorfield 2021-04-14T05:15:18.059Z

Also, you should try to write your functions as pure functions that don’t have side-effects where you can.

seancorfield 2021-04-14T05:16:00.059900Z

In this case, create-order could be passed the current menu and orders values and return the new values of both, for the caller to keep track of.

seancorfield 2021-04-14T05:21:15.064800Z

(defn create-order [orders menu {:keys [order-items]}]
  [(assoc orders (keyword (str (UUID/randomUUID))))
   (reduce (fn [m item]
             (update-in m [(keyword (slugify (:name item))) :quantity] #(- % (:quantity item))))
             menu
             order-items)])
;; and then:
(let [[new-orders new-menu] (create-order orders menu {:order-items [...]})]
  (println new-orders)
  (println menu))
and each time you call create-orders with the current orders and menu values you get a pair of new values back to use in the next call.

piyer 2021-04-14T16:18:25.091300Z

@prabu.rajan I would consider creating order and doing a dec on the inventory to be one atomic operation.

Prabu Rajan 2021-04-14T16:23:03.091500Z

Thanks @munichlinux you say both orders and menu (inventory) should be maps in the same atom in terms of data structure?

piyer 2021-04-14T16:26:01.091700Z

I was advocating for using ref when you perform the transaction.

Prabu Rajan 2021-04-14T16:53:37.092Z

I am a beginner, and just started learning for 2 weeks now. Are you talking about this? - https://clojure.org/reference/refs

seancorfield 2021-04-14T17:33:33.094700Z

ref usage is very, very rare in the wild. A lot of programs can work just fine with a single atom of their state if they really need mutable data at all.

seancorfield 2021-04-14T17:34:04.094900Z

The nice thing about writing your function as a pure transformation of input to output is that you can usually adapt it for use with swap! on a single atom.

seancorfield 2021-04-14T17:37:17.095100Z

In this case, you could have (atom {:menu {...} :orders {}) and then #(let [[orders menu] (create-order (:orders %) (:menu %) %2) {:orders orders :menu menu}) could be your adapter:

(swap! restaurant #(let ..) {:order-items [..]})
(off the top of my head)

seancorfield 2021-04-14T17:38:04.095300Z

(or just rewrite create-order a bit to accept the restaurant hash map as input — first argument — and return an updated hash map)

Prabu Rajan 2021-04-14T20:41:07.150100Z

Thanks @seancorfield makes sense. I like the idea of minimizing code that deal with side effects as much as possible. I was mainly using atoms here, since that seems to be the clojure idiomatic way to deal with mutable state and this is a small single module app anyways. In larger apps, there might be better ways to handle mutable state like caches? I haven’t gone to that extent so far

seancorfield 2021-04-14T20:42:24.150300Z

https://github.com/clojure/core.cache — expects to be used with an atom for each cache. The clojure.core.cache.wrapped namespace is the easiest/safest way to use it.

seancorfield 2021-04-14T20:44:02.150600Z

For a reference point, here’s some stats about our codebase at work:

Clojure build/config 19 files 224 total loc
Clojure source 350 files 89404 total loc,
    3579 fns, 904 of which are private,
    574 vars, 30 macros, 92 atoms,
    858 specs, 33 function specs.
Clojure tests 379 files 23537 total loc,
    4 specs, 1 function specs.
We have no ref’s at all. We have 136 agent’s, which are mostly used to track metrics that we report via New Relic.

seancorfield 2021-04-14T20:44:56.150800Z

(we have another ~50 atom’s in our test code which we don’t bother to report in our stats)

Prabu Rajan 2021-04-14T21:21:17.151Z

Great! I was going through the usages and the articles related to this library. Will try to put that to use soon in my next web app REST APIs

seancorfield 2021-04-14T05:21:26.065Z

HTH?

👍 1
seancorfield 2021-04-14T05:23:12.066200Z

(and, FWIW, I miss dosas… a local restaurant had a southern chef for a while and he had a variety of dosas on the menu, but he left and they haven’t had much southern food for a while 😞 )

🙂 1
Prabu Rajan 2021-04-14T05:25:14.067700Z

Thanks a lot @seancorfield, really helpful. As you can see, it takes a lot to unlearn the imperative Java / Javascript way of coding and learn the new clojure functional way! but I hope I will get better at it over time!

Prabu Rajan 2021-04-14T05:26:51.068100Z

Sad to hear and wishing you get to eat your favourite dosas soon!

seancorfield 2021-04-14T05:28:12.068900Z

(that code isn’t quite right but should give you some guidance… I’m now trying to get it right in the REPL!)

Prabu Rajan 2021-04-14T05:29:37.069800Z

You are so helpful, I think you shouldn’t worry since I got the idea. Let me swim the river and learn to swim 🙂

seancorfield 2021-04-14T05:32:58.070Z

dev=> (defn slugify [s] (str/replace s " " "-"))
#'dev/slugify
dev=> (def orders {})
#'dev/orders
dev=> (def menu {:chana-masala {:quantity 2} :paneer-bhurji {:quantity 1}})
#'dev/menu
dev=> (defn create-order [orders menu {:keys [order-items]}]
 #_=>   [(assoc orders (keyword (str (UUID/randomUUID))) order-items)
 #_=>    (reduce (fn [m item]
 #_=>              (update-in m [(keyword (slugify (:name item))) :quantity] #(- % (:quantity item))))
 #_=>              menu
 #_=>              order-items)])
#'dev/create-order
dev=> (let [[new-orders new-menu] (create-order orders menu {:order-items [{:name "paneer bhurji" :quantity 1}]})]
 #_=>   (println new-orders)
 #_=>   (println new-menu))
{:f3666dfc-a9f2-4432-8979-bdffedb83b80 [{:name paneer bhurji, :quantity 1}]}
{:chana-masala {:quantity 2}, :paneer-bhurji {:quantity 0}}
nil
dev=> 

seancorfield 2021-04-14T05:33:16.070200Z

(this is making me hungry!)

Prabu Rajan 2021-04-14T05:35:27.070800Z

wow! great, thanks! looks like you are a huge fan of indian food!

seancorfield 2021-04-14T17:41:54.095800Z

Thank you! A couple of those are a bit far away for me — I’m in Castro Valley in the East Bay — but Dosa Hut is pretty close and I see there’s another Saravanaa Bhavan nearby at 3720 Mowry Ave, Fremont, CA 94538.

Prabu Rajan 2021-04-14T21:57:34.152200Z

oh ok. I stay in Fremont. Yes, you can try the Mowry Avenue Saravana Bhavan, but their sambar and chutneys have lost their touch, but dosas are still good.

1
seancorfield 2021-04-14T05:46:34.070900Z

Born and raised in the UK — it’s the national dish 🙂 Now I live in the San Francisco Bay Area and good Indian food is hard to find. I’m lucky to have a fairly decent restaurant fairly locally but I’ve mostly been very disappointed with Indian restaurants in America.

Prabu Rajan 2021-04-14T05:48:51.071100Z

Oh nice! I live in the SF bay area too. Having moved from south India to the bay area just 3 years back, I absolutely echo your sentiments about food! That is the biggest thing I miss about india here!

seancorfield 2021-04-14T06:10:40.071600Z

If you have any local recommendations, I’m all ears! 🙂

rafalw 2021-04-14T07:29:48.073Z

Hi, I'm looking at https://github.com/redstarssystems/context/blob/master/src/org/rssys/context/core.clj What is that convention to prefix var with *, for example *ctx ?

2021-04-14T13:37:00.073800Z

hi. What syntax is this, the #: part.

(let [m #:domain{:a 1, :b 2}
      {:domain/keys [a b]} m]           
  [a b])                                ;  1, 2

Tomas Brejla 2021-04-14T13:41:52.074100Z

basically just a different (shorter, easier to read/write) representation of map containing namespaced keys

user=> {:domain/a 1, :domain/b 2}
#:domain{:a 1, :b 2}
It can only be applied if all the ("top-level") keys in that map share the same namespace. If you use more than one key namespace, this form cannot be applied
user=> {:domain/a 1, :domain/b 2, :anotherdomain/a 3}
{:domain/a 1, :domain/b 2, :anotherdomain/a 3}

Tomas Brejla 2021-04-14T13:43:41.074300Z

documentation: https://clojure.org/reference/reader see this part

Map namespace syntax
Added in Clojure 1.9

Tomas Brejla 2021-04-14T13:46:58.074600Z

btw it's also possible to use #:: form, didn't know about that :thumbsup:

user=> #::{:some-key "some value"}
#:user{:some-key "some value"}

🆒 1
Tomas Brejla 2021-04-14T13:48:53.074900Z

btw there's also a nice page with description of "weird characters", very handy https://clojure.org/guides/weird_characters

Eric Ihli 2021-04-14T13:57:27.077500Z

I have a custom data type that I want to print in the REPL as if it were an IPersistentMap. It implements everything needed to print as an IPersistentMap but it doesn't implement everything to act as an IPersistentMap. How can I do that? I'm looking over https://github.com/clojure/clojure/blob/master/src/clj/clojure/core_print.clj don't see an obvious way.

ghadi 2021-04-14T13:58:54.078800Z

link/paste what you have so far and describe how it is not acting as a map there are a lot of interfaces and methods to implement to get it to work well

ghadi 2021-04-14T14:00:22.080300Z

(core_print will not be helpful as it precedes the definition of deftype/reify during clojure's load)

Eric Ihli 2021-04-14T14:00:36.080600Z

https://github.com/eihli/clj-tightly-packed-trie/blob/main/src/com/owoga/tightly_packed_trie.clj It's ILookup. I want the seq of key/values to print. But it's "read-only". Can't assoc into it or empty it or anything.

ghadi 2021-04-14T14:01:21.081100Z

oh I thought you said it was printing

Eric Ihli 2021-04-14T14:02:33.082400Z

Perhaps I'm using the wrong terms. When I evaluate the TightlyPackedTrie in my editor, I see:

;; => #object[com.owoga.tightly_packed_trie.TightlyPackedTrie 0x21f76d8d "com.owoga.tightly_packed_trie.TightlyPackedTrie@21f76d8d"]
I want to see (which is what I see with the same data as a https://github.com/eihli/clj-tightly-packed-trie/blob/main/src/com/owoga/trie.clj#L57https://github.com/eihli/clj-tightly-packed-trie/blob/main/src/com/owoga/trie.clj#L57https://github.com/eihli/clj-tightly-packed-trie/blob/main/src/com/owoga/trie.clj#L57):
;; => {(1 2) 12, (1 3) 13, (1) 1}

2021-04-14T14:02:55.082800Z

Yes. Thanks

ghadi 2021-04-14T14:03:15.083400Z

(deftype Foo [a b c]....)
;;
(defmethod print-method Foo [my-foo ^java.io.Writer w]
  ... my custom foo printing method)

ghadi 2021-04-14T14:03:47.084400Z

clojure has a multimethod that allows you to participate in the printing system in a custom way for your deftype

ghadi 2021-04-14T14:04:53.085800Z

you don't have to manually write to the Writer, you can make the map representation of your dreams and print that by delegation

ghadi 2021-04-14T14:05:39.086800Z

(defmethod print-method Foo [my-foo ^java.io.Writer w]
  ;; delegate to calling print-method on your desired map
  (print-method (function-that-returns-the-desired-representation my-foo) w))

ghadi 2021-04-14T14:06:01.087Z

s/Foo/TightlyPackedTrie

Eric Ihli 2021-04-14T14:08:25.087500Z

Bingo. Thanks! That's exactly what I was looking for.

(defmethod print-method TightlyPackedTrie [trie ^java.io.Writer w]
  (print-method (into {} trie) w))

🎉 2
alexmiller 2021-04-14T14:09:00.088Z

often you will also want to override print-dup in a similar way

Eric Ihli 2021-04-14T14:10:19.089200Z

Is print-method/print-dup like Pythons __str__ vs __repr__ , the former being the human-readable repl format and latter is the string-serialized representation to recreate the object?

alexmiller 2021-04-14T14:11:00.089900Z

I don't know python enough to answer that part, but sounds like the same idea

👍 1
alexmiller 2021-04-14T14:11:14.090100Z

in clojure print vs pr

Andrei a. a. 2021-04-14T17:24:28.094400Z

Hi, everyone. I am a clojure newbie and am super excited about learning and playing with this beautiful language! I am working my way through a roman numerals clojure exercise. I have

(def roman-numerals {"M" 1000 "D" 500 "C" 100 "L" 50 "X" 10 "V" 5 "I" 1})
and I'd like to convert let's say "XVI" to numbers - as a start. But
(map #(println %)  (sequence "XIV")) 
prints
X
(nilI
 nilV
 nil)

How can I get only the character and not the nil value so that something like this
(map #(get roman-numerals %)  (sequence "XIV")) 
produces numbers and not 
(nil nil nil)

?

2021-04-14T17:42:55.096800Z

@aaandre what you see in the repl is the mixing of the side effect of printing with the value returned by printing

2021-04-14T17:43:32.097700Z

further, map always calls seq on its input for you and #(println %) can be replaced by println

2021-04-14T17:43:55.098300Z

user=> (run! println "XIV")
X
I
V
nil

Andrei a. a. 2021-04-14T17:43:56.098500Z

How would I get (map #(get roman-numerals %) (sequence "XIV")) to produce the values of the hash correspoonding to the keys?

2021-04-14T17:44:03.098700Z

run! is like map but for side effects

2021-04-14T17:44:24.099200Z

if you want to process and not print, use the hash as a function

Andrei a. a. 2021-04-14T17:44:56.100400Z

And, how would I convert (\X \I \V) to ["X" "I" "V"] ?

2021-04-14T17:44:57.100500Z

user=> (map {\X 10 \I 1 \V 5} "XIV")
(10 1 5)

2021-04-14T17:45:08.100900Z

you don't need to? or you can map str

2021-04-14T17:45:46.101600Z

user=> (->> "XIV"
           (map str)
           (map {"X" 10 "I" 1 "V" 5}))
(10 1 5)

Andrei a. a. 2021-04-14T17:46:52.102500Z

If (map {\X 10 \I 1 \V 5} "XIV") works, why doesn't (map roman-numerals "XIV") ?

2021-04-14T17:47:00.102900Z

or you can be a functional programming nerd:

user=> (map (comp {"X" 10 "I" 1 "V" 5} str) "XIV")
(10 1 5)

sb 2021-04-14T17:47:03.103100Z

#(roman-numerals (str %)) maybe in this way easier to understand (str chararcter type)

2021-04-14T17:47:29.103800Z

@aaandre seq of a string provides characters, not strings

2021-04-14T17:47:58.104600Z

@sb oh yeah that's how a normal person would write that huh :D

😄 1
Andrei a. a. 2021-04-14T17:48:15.105Z

So roman-numerals has strings for keys and seq produces characters, and characters are not strings?

2021-04-14T17:48:25.105300Z

right

Andrei a. a. 2021-04-14T17:48:32.105600Z

(I am coming from ruby 🙂

2021-04-14T17:49:08.106200Z

@aaandre my first approach would be to just use characters as keys since the input is characters

2021-04-14T17:49:35.106800Z

the string as key is just a complexity introduced by your intuition about ruby IMHO

Andrei a. a. 2021-04-14T17:51:19.107900Z

Got it. Changed roman-numerals to (def roman-numerals {\M 1000 \D 500 \C 100 \L 50 \X 10 \V 5 \I 1}) and now (map roman-numerals "XIV") works as intended

🍻 2
2021-04-14T17:51:28.108100Z

but that's just a style thing, converting the characters to strings also solves the problem

Andrei a. a. 2021-04-14T17:52:28.109100Z

You mean converting each character from the provided string to a string? how would I do that?

2021-04-14T17:52:42.109400Z

I guess the fun part is going to be the state machine for the prefixes hu

Andrei a. a. 2021-04-14T17:53:00.110Z

Yes, one step at a time 🙂

Andrei a. a. 2021-04-14T17:53:22.110600Z

You'll be hearing from me again!

Andrei a. a. 2021-04-14T17:53:51.111300Z

Loving clojure so much and how it makes me think about problems. It. just. flows.

2021-04-14T17:54:59.112700Z

@aaandre for a standalone seq to seq: #(map str %) but it's usually more helpful to use the function rather than the mapping as the building block

Andrei a. a. 2021-04-14T17:55:45.114400Z

and how would I convert character to symbol - if I am to have a proper hash keys?

2021-04-14T17:55:55.114700Z

so that's why (map roman-numerals input) can become (map (comp roman-numerals str) input) or (map #(roman-numerals (str %)) input)

👍 1
2021-04-14T17:56:34.115400Z

@aaandre this is a thing that's different in clojure - we call :foo a keyword, not a symbol, and 'foo is a symbol

2021-04-14T17:56:51.116Z

a "proper" map can have keys that are any immutable type

2021-04-14T17:57:06.116500Z

often a character or a string as a key is better than a keyword

Andrei a. a. 2021-04-14T17:57:15.116700Z

got it thank you

Andrei a. a. 2021-04-14T17:58:30.117600Z

(map keyword (map str "XIV"))

💯 1
2021-04-14T17:58:38.117800Z

in fact adding code complexity by converting to keywords is a big pet peeve of mine (but once again that's a style thing and reasonable programmers can disagree)

em 2021-04-14T17:59:18.119100Z

@aaandre Yeah it's sweet, I remember doing a integer -> roman numeral parser and the solution ended up representing the logic of the conversion, and not a hard coded table of all the symbols (which is what you'll basically strictly find with a google search), which was neat

2021-04-14T17:59:40.120Z

I mean, what actually makes {:a 0} better than {\a 0} ? if your domain is characters the second one is more straightforward IMHO

Andrei a. a. 2021-04-14T17:59:42.120100Z

so defining the hash with keys closest to the data at hand is good practice despite data at hand may not be keywords - got it'

2021-04-14T18:01:09.122100Z

right - keywords IMHO are great for when you have them as repeated constants throughout your code, but I don't think that's the role of the hash here

Andrei a. a. 2021-04-14T18:01:24.122500Z

in my case it's roman numeral -> integer and it'll need logic, too as roman numbers sometimes indicate addition and sometimes subtraction, depending on positioning.

2021-04-14T18:01:44.123300Z

right - but the subtask here is token -> integer

2021-04-14T18:01:50.123600Z

why put keyword in the middle?

em 2021-04-14T18:01:54.123700Z

yeah a state machine would be one way to do that

Andrei a. a. 2021-04-14T18:02:55.125100Z

thank you for the help! I will be back when I need more guidance. Haven't be so excited about a language since I encountered Ruby.

sb 2021-04-14T18:04:47.125400Z

or “simply” with one transducer, like a csv parser

Andrei a. a. 2021-04-14T18:31:00.128300Z

Ok, another question: (map roman-numerals "XIV") produces (10 15 1) but anything I try to do with this results in an error. I don't even know how to call this structure - integers as expression? - and not sure how to convert it into something workable? Confused by map not returning a vector for example. What is the proper way to use the result of a map? Should I convert it to a vector? If yes, how?

2021-04-14T18:32:01.128900Z

a list, when presented to the compiler, becomes an invocation

2021-04-14T18:32:06.129100Z

invoking a number is an error

2021-04-14T18:32:32.129900Z

(that's a lazy-seq not a list, but () as input makes a list)

Andrei a. a. 2021-04-14T18:32:33.130100Z

how do I work with invocations consisting of the result of a map?

2021-04-14T18:32:49.130400Z

by calling another function that expects a sequential input

2021-04-14T18:32:59.130700Z

the ->> macro can help a lot here

2021-04-14T18:33:41.131400Z

(ins)user=> (->> "abc")
"abc"
(ins)user=> (->> "abc" (map {\a 0 \b 1 \c 2}))
(0 1 2)
(ins)user=> (->> "abc" (map {\a 0 \b 1 \c 2}) (filter even?))
(0 2)

Andrei a. a. 2021-04-14T18:33:43.131500Z

Interesting, even (map (1 2 3)) errors out.

2021-04-14T18:34:02.131900Z

right, (1 2 3) is not a valid input to the compiler

2021-04-14T18:34:23.132400Z

as I stated above, the compiler treats (...) as asking for invocation

2021-04-14T18:34:40.132800Z

it's a valid printed form for a result, of course

yuhan 2021-04-14T18:34:56.133200Z

it's only a problem when you try to copy and paste the output of an evaluation

2021-04-14T18:34:59.133400Z

you can use [1 2 3] which will have the same behavior under map / filter / etc.

Andrei a. a. 2021-04-14T18:35:26.134400Z

(filter even? [1 2 3]) works but (filter even? (1 2 3)) produces an error

2021-04-14T18:35:29.134500Z

@qythium good point, you can use *1 to get the return value of the last expression

🤯 1
2021-04-14T18:36:01.135400Z

@aaandre right as I said (...) asks for invocation, invoking numbers is invalid

Andrei a. a. 2021-04-14T18:36:19.135800Z

OK, so map returns an invocation with all of its elements - how do I work with this?

2021-04-14T18:36:29.136100Z

no, it returns something sequential

2021-04-14T18:37:11.136900Z

@aaandre in a repl you can use def to assign a name to a return value

2021-04-14T18:37:36.137400Z

just typing in what the last form printed will not usually be the same as using the value that was printed from

yuhan 2021-04-14T18:38:51.138900Z

yup, you can also quote lists to prevent them from being evaluated like '(1 2 3)

yuhan 2021-04-14T18:39:18.139900Z

but this might cause more confusion in the short term

2021-04-14T18:39:38.140200Z

right - but that's pretty much never useful, since you can use [1 2 3] or (list* [1 2 3]) if you really need list instead of vector

Andrei a. a. 2021-04-14T18:40:27.140900Z

I see, "something sequential" meaning "you can push this into anything that accepts a sequence"

2021-04-14T18:40:32.141100Z

right

Andrei a. a. 2021-04-14T18:42:37.141900Z

But... (map even? (1 2 3)) produces an error and (map even? '(1 2 3)) does not

2021-04-14T18:42:59.142500Z

right, that's because the ' operator prevents all evaluation

Andrei a. a. 2021-04-14T18:43:04.142700Z

but what I get out of (map roman-numerals "XIV") is (10 1 5) without '

2021-04-14T18:43:23.143Z

right, because ' is a reader facility, not a data type

2021-04-14T18:44:09.144300Z

'(foo) expands in the reader to (quote foo) and quote is a special instruction to the compiler that says "my arg is to be used as is and not compiled"

Andrei a. a. 2021-04-14T18:44:14.144400Z

how can I turn (1 2 3) into [1 2 3] ? ... and should I?

2021-04-14T18:44:35.145200Z

you are still misunderstanding the relationship between what the repl prints and the data itself

yuhan 2021-04-14T18:44:46.145700Z

what you get out of (map roman-numerals "XIV") is a lazy sequence whose printed representation happens to be (10 1 5)

2021-04-14T18:44:51.145900Z

you can use [1 2 3] in a repl for experimenting

Andrei a. a. 2021-04-14T18:45:02.146500Z

a-ha! so I can't copy-paste the repl data because it's "printed"

yuhan 2021-04-14T18:45:21.147200Z

when you enter (10 1 5) into the repl, clojure interprets it as "call the function 10 with the arguments 1 and 5"

2021-04-14T18:45:22.147300Z

right - the printed form is sometimes but not always valid for evaluation

Andrei a. a. 2021-04-14T18:46:13.148100Z

thank you again 🙂

2021-04-14T18:47:35.149600Z

in your code structure you can use let to bind results of expressions to names (that you can then use in all following forms in the let block), or directly wrap the expression in its consumer (with helpers like -> / ->> to avoid deep () nesting)

Franco Gasperino 2021-04-14T21:39:13.151200Z

(10 1 5)
=> nil
; 
; Execution error (TypeError) at (<cljs repl>:1).

`(10 1 5)
=> (10 1 5)

Franco Gasperino 2021-04-14T21:39:49.151400Z

if you use a single quote / tick mark before the list, it'll not be considered a function

Franco Gasperino 2021-04-14T21:40:40.151600Z

or you can transform it as you mentioned:

(into [] `(10 1 5))
=> [10 1 5]

Franco Gasperino 2021-04-14T21:43:41.151800Z

(reduce conj [] `(10 1 5))
=> [10 1 5]

Franco Gasperino 2021-04-14T21:43:51.152Z

several ways to accomplish it

walterl 2021-04-14T22:46:33.153500Z

What's the Right Way to do (update m :key conj 123), ensuring that :key's value is a vector (rather than a list) even if it didn't exist before?

walterl 2021-04-14T22:47:30.154Z

I.e. if m is {}, I want the result to be {:key [123]} and not {:key '(123)}

2021-04-14T22:47:42.154200Z

fnil

🎉 6
2021-04-16T18:06:12.179900Z

95% of my fnil usage is either (fnil conj []) or (fnil conj #{})

👍 1
1
sova-soars-the-sora 2021-04-18T05:31:12.201Z

please teach a novice like me the ways of fnil foo (fnil fu)

walterl 2021-04-14T22:48:53.154500Z

Wonderful, thanks! (update {} :key (fnil conj []) 123)

walterl 2021-04-14T22:49:25.154900Z

fnil is one of those super useful core functions that I don't use enough for its to "stick"

lsenjov 2021-04-14T23:45:13.155300Z

TIL fnil exists

🎉 4