Fork me on GitHub
#beginners
<
2020-05-15
>
Spaceman00:05:19

How can I write the following macro?:

(db-event :add-to-cart [product-id] {:cart {product-id (-> db :cart (get product-id) inc)}})
should give
(db-event :add-to-cart [product-id]
     (assoc-in db [:cart product-id] (-> db :cart (get product-id) inc)))
and
(db-event :foo [resp] {:abc {:def resp :efg (more-stuff resp)} :foo1 {:bar1 (stuff resp)}})         
should give
(db-event :foo [resp]
          (->
           (assoc-in db [:abc :def] resp)
           (assoc-in [:abc :efg] (more-stuff resp))
           (assoc-in [:foo1 :bar1] (stuff resp))))

Spaceman00:05:20

so basically doing a bunch of assoc-in, just giving a map of all the things that are to be changed.

Spaceman00:05:44

and hiding any reference to the original map db

Spaceman00:05:56

except when it's used in a value of the updated

Spaceman00:05:59

if it helps, I'm trying to extend the db-event macro which is this:

(defmacro db-event [event-key params & body]
`(do
     (re-frame.core/reg-event-db ~event-key
                                 (fn [~'db [_# ~@params]]
                                   ~@body))
     (defn ~(symbol event-key) ~params (re-frame.core/dispatch [~event-key ~@params]))))

potetm01:05:10

:thinking_face: “def a fn that dispatches by event to an anonymous fn which is registered by that event.”

potetm01:05:38

Out of curiosity, why not just register events and not def fns?

hiredman01:05:46

The best way to write a macro is to write a function that does what you want, then write a macro that expands into a call to that function

Cameron06:05:10

I likewise will often write my macros that way -- and I think it does speak to the nature of the macro a bit; to me, its easier to think of the body of a macro as not the macro itself, but an inline defined function that the macro will smuggle into the compiler.

Cameron06:05:23

well, 'often' probably not often, usually I don't have to

Spaceman12:05:48

what would a function say assoc-map look like, that takes in a map1 and another map2 and assocs in map1 into map2, basically embeds map1 into map2?

Spaceman12:05:32

like a way to recursively iterate through a map

Spaceman12:05:55

(defn deep-merge "Recursively merges maps." [& maps] (letfn [(m [& xs] (if (some #(and (map? %) (not (record? %))) xs) (apply merge-with m xs) (last xs)))] (reduce m maps)))

Spaceman12:05:59

works really well

potetm01:05:20

not wrong tho^

Cameron06:05:10

I likewise will often write my macros that way -- and I think it does speak to the nature of the macro a bit; to me, its easier to think of the body of a macro as not the macro itself, but an inline defined function that the macro will smuggle into the compiler.

Bardia Pourvakil06:05:40

What’s the best way to iterate over a highly varieties custom nested map and vector data structure and collect all the values that have a specific key across the entire structure?

raspasov08:05:53

@bardiapourvakil cljs.user=> (map :a [{:a 1} {:a 2}]) (1 2)

Cameron08:05:58

@bardiapourvakil I can imagine a lot of different scenarios with that description, it'd be easier to answer with some examples

Fredrik Cumlin09:05:27

What is the best way to check if a word consists of only letter and/or one colon?

Fredrik Cumlin09:05:52

I've written the following: (some #(= arg %) (vector (re-matches #"[a-zA-Z]+:[a-zA-Z]+" arg) (re-matches #"[a-zA-Z]+" arg)))

delaguardo09:05:55

#"[a-zA-Z]+(:[a-zA-Z]*)?"

hindol12:05:24

There are some pre-defined character classes in Java RegEx. E.g. \p{IsAlphabetic} can match any Unicode alphabetic characters. They are sometimes useful. https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html

hindol12:05:19

This works too, \p{IsAlphabetic}+(:\p{IsAlphabetic}+)?

👍 4
nick11:05:23

Got my first Clojure app deployed to the real production project 🚀

👍 20
8
🎉 16
😃 4
nick11:05:28

It wasn't easy but I really like the result(performance/maintainability). Hopefully future releases/apps wouldn't be as difficult

Michaël Salihi12:05:32

What did the more difficult part for you ?

nick12:05:20

The most difficult part was absense of someone with practical experience in clj/cljs ecosystem so I had to explore all those(fantastic) choices myself. So my onboarding to CLJ world was rather tough - started with Luminous(clj/cljs), then got excited about Fulcro(still got stuck after a few weeks of learning it), switched to macchiato(got it working locally but couldn't make it into production - slack channel and macchiato's small community is rather very small so I can't complain). Eventually I rewrote it using Luminus stack hoping Heroku's 512Mb RAM limit would be enough). I'm currently monitoring this app - RAM usage is steady at 60%(318Mb), performance it really good

8
nick12:05:39

Clojure community is super friendly and super helpful but when you have deadlines.. you can't just spam it with dozens of questions every hour 🙂

Aron12:05:15

same problems here too, sometimes I was waking up 2 AM and 3 AM just so I can ask questions on the slack since in my timezone either people were sleeping or working, so not able to answer.

Aron12:05:04

It's a bit easier if one can setup a CI pipeline, but the whole cloud direction is like getting in brace with Bezos, I am not sure how that's going to work out. OTOH, the language itself would benefit from more diversity anyway, so each possibility has it's ups too.

nick12:05:47

The time zone issue is real. If you post something at morning time(in Europe), there is high chance no one gets to it by the time America wakes up 😄

👍 4
Aron14:05:54

which is late afternoon, early evening. If you want to have a conversation that goes back and forth a few time, it's quite expected to take until 22:00 there is some exhaustive resolve

Michaël Salihi15:05:16

Thank for the details @U0113AVHL2W 👍

Endre Bakken Stovner12:05:04

In a line like

user=> #com.stuartsierra.dependency.MapDependencyGraph {}
; #com.stuartsierra.dependency.MapDependencyGraph{:dependencies nil, :dependents nil}
what is the #com... which interprets the map as a dependency graph called?

Endre Bakken Stovner12:05:33

Thanks! Do those that turn regular data structures into more specific kinds have a special name? Because there are other kinds of uses for #something, right?

Alex Miller (Clojure team)12:05:38

Actually in this case I think that’s a Java constructor, not a tagged literal

👍 4
Alex Miller (Clojure team)12:05:06

It happens to be a Java constructor for a Clojure record in this case

Endre Bakken Stovner12:05:54

Are there any docs for all possible uses/meanings of #?

delaguardo12:05:29

interesting, thanks for noticing that @U064X3EF3

👍 4
Alex Miller (Clojure team)12:05:15

In general the # is the character that triggers the dispatch reader

👍 4
Alex Miller (Clojure team)12:05:09

So that says what to do based on the character after the #

Endre Bakken Stovner12:05:38

That is so cool! I will have to learn to read the java source eventually 🙂

Spaceman12:05:58

suppose I have a macro that has a body parameter, and I want to apply a function only to the last statement in the body if there are multiple ones:

(defmacro foo [foo bar & body]
   ~@body
)
so basically I want to split the body into all-but-the-last and last. How can I do that?

Endre Bakken Stovner13:05:53

Is it possible to have a function that takes an optional keyword argument, but the keyword itself is optional? That is, I want (f 2) to work too:

(defn f [& {:keys [a] :or {a 1}}] a)
; (f) => 1, (f :a 2) => 2
; (f 2) => Execution error (IllegalArgumentException) No value supplied for key: 2

Endre Bakken Stovner13:05:50

I am used to def f(a=1): return a in Python. Works with f(), f(2) , and f(a=2) .

Alex Miller (Clojure team)13:05:25

you could add another arity ([a] ...) to do that

Alex Miller (Clojure team)13:05:38

I wouldn't recommend it, but it is possible :)

Alex Miller (Clojure team)13:05:15

(defn f
  ([] ...)
  ([a] ...)
  ([& {:keys [a] ...}))

delaguardo13:05:14

I think it will complain about mixing fixed arity with variadic one

Alex Miller (Clojure team)13:05:35

ah right, you might need to get rid of the no arg there

delaguardo13:05:38

(defn foo
  ([] (foo {:a 1}))
  ([{:keys [a] :or {a 1} :as m}]
   (if (map? m)
     a ;; here should be actual function code
     (foo :a m)))
  ([a & args]
   (foo (apply hash-map (list* a args)))))

(foo)       ;; => 1
(foo 2)     ;; => 2
(foo :a 3)  ;; => 3
I end up with this. Arity 1 is the main

👍 4
delaguardo13:05:10

but I would not recommend to use it) probably not the best way to structure function api

Alex Miller (Clojure team)13:05:44

the arities can invoke each other

Endre Bakken Stovner13:05:54

Yeah, I actually like the strictness of having to supply the kw; makes it impossible to make a mistake in the ordering of arguments.

jaihindhreddy15:05:37

You could always take a map as an argument

MatthewLisp17:05:20

Hello 👋 Is it possible to use defprotocol with :pre/:post ?

seancorfield17:05:34

defprotocol doesn't provide function implementations -- it's more like a Java interface -- but :pre/`:post` are part of an implementation so, no.

MatthewLisp17:05:39

I thought so

seancorfield17:05:41

You also cannot Spec a protocol function by the way: which is why it's fairly common to have wrapper functions that call the protocol functions (so you can Spec the wrapper or have variadic functions etc).

bfabry17:05:02

^ that strategy, a wrapper that calls the protocol function will also work for :pre/:post the same as it does for spec

Alex Miller (Clojure team)18:05:27

in general, in stuff we build in things like spec, we almost always have a wrapper function around protocols rather than using them directly

Alex Miller (Clojure team)18:05:54

it's a very useful hook point for a variety of reasons

Lukas18:05:42

Hi all 👋, I would like to get some more insights about the philosophy behind "simple". I kinda get what is meant by that, but I would like to get some practical adivice how I (as a less experienced programmer) can judge that a solution is "simple" and how i can build a "simple" solution. Any resource, regarding this topic, to study will be much appreciated.

👋 4
noisesmith18:05:27

for a large part it's recognizing how Clojure's design decisions are meant to be simple, and making things that are compatible with those choices

phronmophobic18:05:32

you’ve probably already seen https://www.infoq.com/presentations/Simple-Made-Easy/ , but just in case

💯 4
noisesmith18:05:36

I think the biggest items: use immutable things, program in terms of values, use abstract structures not domain specific ones, prefer 100 functions on 1 data type over 10 functions each on 10 data types

noisesmith18:05:50

but that's just my elevator pitch version

phronmophobic18:05:13

when thinking about simplicity, a decent place to start is trying to break down your design down into who, what, where, when, why, and how. it’s a very concrete place to start which should hopefully get the wheels turning.

Lukas18:05:45

hey thanks for the answer I m familiar with the suggestion, i guess my question aims more for the bigger picture. Like when i desing a system. How to judge if it is still simple after writing a lot of code

Lukas18:05:06

hope this makes sense :thinking_face:

phronmophobic18:05:35

ideally, you want to produce a “simple” design before you write a lot of code

😂 4
phronmophobic18:05:50

if you haven’t, chances are good that your design isn’t simple

Lukas18:05:39

ur right a problem has to be understood

Lukas18:05:55

but i guess more clearity will come with more experience

phronmophobic18:05:24

i’m not really a TDD (test driven design) person, but TDD will highlight where your design isn’t simple. basically, if your design is simple, then you can take your program apart. if you can take your program apart, then it’s typically much easier to test

Lukas18:05:35

ye makes sense

phronmophobic18:05:40

some questions you can ask to see if your design is simple: • is your program built from smaller pieces? • what are the smaller pieces? • can the pieces be used in different contexts? • can you run a piece of your program in isolation? (ie. can you run a part of your program without setting up a context that requires the rest of your program)

Lukas18:05:07

I definitly will try this

Lukas18:05:29

really appreciate ur input and thanks for the effort 😍

😁 4
krzyz01:05:12

Focus on data and values instead of complicated abstractions and wrappers. When you have data you can apply powerful Clojure functions to it. Simple is good. It means you can use tools you are familiar with and not have to guess how you are supposed to deal with something. All the problems start to look familiar and tractable. (as opposed to, for example, having to learn a new API every time you interact with a different object/class)

💯 4
Lukas09:05:41

@U013CA8A28J ty for ur input, clojure makes this approach especially easy. Where I struggle is when I try to apply these ideas in other languages for example java. Do u have any resources or examples outside the clojure ecosystem where the principles of "simple" are applied?

krzyz21:05:40

@UP2FUP0TT Are you asking how you can make Java as simple as Clojure? Oh, that made me laugh 😆. You cannot. It is designed around the class system. Java code can only take the ideas so far because the language doesn't help you. You can do certain things, like rely on factory methods instead of instantiation with new. That simplifies things for you, but much of this way of programming depends on the language itself. Take JavaScript as an example. There, your functions are first class and you have data literals just like Clojure. That makes it a lot easier. Many of the "patterns" of Java just fall away and you use functions/literals instead. There is a really great talk by Douglas Crockford where he brings up something called "parasitical inheritance". It's like a decorator pattern. Say you have a function makeDwelling which produces some kind of specification map for houses, like number of rooms, area, furnishings, etc. You could then create a new function that specializes on top of that, such as makeLakehouse: function makeLakehouse() { const house = makeDwelling(); return {...house, hasLake: true }; } Nice and simple. No need for classes or any rigid hierarchy. You basically have a factory function and then use it as part of another factory function. It sounds almost ludicrous to call it a factory function because it's so simple. You get nice encapsulation and the ability to change the implementation later. In Java, this is simply not an option. No matter how you decide to implement it, you must use classes and methods, and you'll probably have getters and setters. There will be boilerplate. It's just the way it is. JavaScript has its own problems, but you can at least choose to simplify your life and use the nice parts of it.

Justin Roche20:05:31

So I'm having an issue with the reagent template project. I ran 'lein new reagent myproject' as described here: https://github.com/reagent-project/reagent. But when I make changes to source, I have to manually hit reload in the browser to get updates. I tried to implement the fixes here: https://stackoverflow.com/questions/46287101/figwheel-doesnt-detect-most-changes-in-my-code#46298820 to no avail. How do I get figwheel to reload the browser?

AE21:05:29

Is there a good recommendation for finding a "tutor" or someone to help me with my learning? I don't mind paying. I've found good resources for learning the language generally but hitting some basic issues in my tooling and looking to walk through with someone

andy.fingerhut22:05:53

If you don't mind paying, I would recommend contacting Eric Normand at https://purelyfunctional.tv and find out what he might charge, or if he has recommendations for tutors.

✔️ 16
andy.fingerhut22:05:15

Clojure tooling tends to be somewhat disparate, with at least 5 major different IDEs/editors each with their own Clojure-specific add-ons, Clojure vs. ClojureScript, etc.

andy.fingerhut22:05:19

There are specific channels on this Slack for all of them that I know about, e.g. #cider #emacs #calva #cursive #vscode #clojurescript #shadow-cljs #figwheel etc.

💯 4
AE22:05:23

Thanks @U0CMVHBL2 I'll try Eric! Not sure if its an IDE problem or user problem (me) but I've been having a hard time getting programs from tutorials to run on my machine, looking to break through that barrier so I can learn by emulating

AE22:05:18

thanks @U7RJTCH6J that looks like a great resource

👍 8
sova-soars-the-sora12:05:46

I can highly recommend Eric's videos and he is available for you 24/7 via e-mail, very excellent service he provides! Take your Clojure to the next level with some of his excellent videos.