Fork me on GitHub
#beginners
<
2019-08-13
>
GC00:08:17

Hi, I want to change value of namae in :keys, so that it gets reflected in hello as well

GC00:08:32

(def client {:namae "Super Co.", :location "Philadelphia", :description "The worldwide leader in plastic tableware."})

noisesmith00:08:05

you can replace the client var, but the map it points to is immutable

GC00:08:25

(let [{namae :namae     :keys [location description] :as hello} client
namae "myName"
]
(println hello))

GC00:08:30

This is what I tried

noisesmith00:08:45

let introduces a new symbol

noisesmith00:08:59

if it has the same name as one used before, it hides the old one

noisesmith00:08:08

it never modifies values

GC00:08:30

so let's say I define a new one called namae, will it effect the values of namae?

noisesmith00:08:37

if you want a new map that's mostly like an old one except one key, you can use assoc

noisesmith00:08:00

@gagan.chohan no, it just hides the old one, none of the rebindings mutate the previous values

GC00:08:18

by hide, do you mean override?

noisesmith00:08:03

(let [a 0 a 1 a 2] a) - that creates three bindings, only one is visible, the other two are hidden

noisesmith00:08:36

because the name is shadowed by a new one with the same name

GC00:08:11

so, in my case, so that change in namae is reflected in hello as well, you recommend me to use assoc?

noisesmith00:08:41

assoc returns a new map, that's the same as the old one except for the keys you provide

GC00:08:54

I will give it a try

noisesmith00:08:34

user=> (let [m {:a 0 :b 1} n (assoc m :b 3)] [m n])
[{:a 0, :b 1} {:a 0, :b 3}]

noisesmith00:08:00

the general idea in clojure is that you make new immutable data structures with the modifications you need, instead of replacing or modifying the original

GC00:08:02

if the original gets hidden, isn't it as good as replaced??

noisesmith00:08:35

it depends, is anyone else already using the original? if yes, it isn't the same at all, if no, that's pretty much how we do it, yeah

noisesmith00:08:31

@gagan.chohan this might illustrate what I mean

user=> (let [n 0 m {:a n} n 3] [m n])
[{:a 0} 3]

noisesmith00:08:02

m still uses the original value of n, even though n itself is shadowed with a new value

GC00:08:40

but m is using the value before it gets changed

GC00:08:49

yea, I sea your point

noisesmith00:08:26

it might help to think of it like numbers

noisesmith00:08:49

x might change from 5 to 6, but you don't expect other things using 5 to change to 6

seancorfield00:08:57

"before it gets changed" -- it doesn't get changed, it's immutable.

seancorfield00:08:29

Getting used to immutability is one of the biggest mind shifts for a lot of folks new to Clojure.

seancorfield00:08:28

We have a few controlled "reference types" that act a bit like variables in other languages but we use them only occasionally.

GC00:08:29

(let [{:keys [namae location] :as hello}   (some_request)    ])
I have some request coming, and keys are extracting the value, my main purpose is to change(new bind) value of namae, so that I don't have to change rest of code. I want to bind value of namae

GC00:08:39

so assoc will work in this scenario?

GC00:08:06

since hello and namae, and location will be used often

noisesmith00:08:47

what you have there suffices to get the old value of namae and location and hello, in order to use hello with a different value you'd want another binding, maybe hello2 (assoc hello :namae "myName") or you could rebind hello and shadow the old one

GC00:08:04

"or you could rebind hello and shadow the old one", yea this might work for me

GC01:08:53

still kinda confused

GC01:08:01

here, shouldn't shadowing work

GC01:08:06

(let [{namae :namae     :keys [location description] :as hello} client
   
   {namae "my name blabla" :keys [location description] :as hello} client
]     (println hello))

GC01:08:29

I am shadowing hello here, why it still produces older value

GC01:08:52

VAlue of client is same as earlier

(def client {:namae "Super Co.", :location "Philadelphia", :description "The worldwide leader in plastic tableware."})

seancorfield01:08:07

client won't change. It is immutable.

GC01:08:54

I was just telling the value of client, for those who doesn't wanna go back in chat

GC01:08:12

basically I am asking about value of hello, why shadowing doesn't work

dpsutton01:08:29

you aren't shadowing hello here though

dpsutton01:08:55

:as hello binds the value you are destructuring as hello.

GC01:08:13

my problem can be summed as follows:

(let [{     :keys [location] :as hello} client]
         (println hello))
It prints entire value of client

seancorfield01:08:17

(and it's the same value in both cases -- the contents of client)

hiredman01:08:20

The as binding in a map destructuring binds the entire map

hiredman01:08:52

So yes, hello is bound to client

GC01:08:20

and with :keys , we are just defining additional variables for use

GC01:08:47

yes, binding

GC01:08:02

lol, I need to get used to the word binding

seancorfield01:08:34

Note that this {namae "my name blabla" ... attempts to bind the value of the key "my name blabla" to the symbol namae

GC01:08:03

aah okay, it doesn't change hello

seancorfield01:08:11

user=> (let [{x "string"} {:x "X" "string" 42}] (println x))
42
See how x binds to the value that "string" is the key for.

👍 4
bartuka02:08:08

hi, anyone has an example of docker-compose to work with clojure and emacs?

Chase08:08:36

hello! I'm trying to understand some advice I got on exercism. My solution to creating my own reverse string function is:

(defn reverse-string [s]                                          
  (loop [s s                                                      
         reversed-chars []]                                       
    (if (empty? s)                                                
      (apply str reversed-chars)                                  
      (recur (butlast s) (conj reversed-chars (last s))))))

Chase08:08:45

And the advice is: "Notice that this may require walking "s" every time you want to access/drop the last character. Notice that conj is smart and adds new elements where it is most efficient for the data structure - try it with a vector [] vs a list '(). You could also use destructuring inside the loop binding to simplify the code if you used first+rest...."

Chase08:08:30

Is he saying my method would be inefficient? I can change the [] to a '() and use cons to get the desired result but don't understand how that makes it a better function.

Chase08:08:09

I'm also trying to refactor into using reduce instead of loop/recur per my advice I got here yesterday but struggling with that again.

Chase08:08:36

The destructuring part is a struggle for me to understand too. Is he saying do something like [first & remaining] s? I'm not sure how to reconcile that with the fact I'm using last and butlast

andy.fingerhut08:08:31

I did not write that advice, but note that for a long string / sequence s, this function will take O(N^2) steps, because butlast and last take linear time to get to the end of the string/sequence.

Chase08:08:01

That was my hunch at what he was getting at. I'm still learning about this O(N) stuff and definitely going to struggle with actually applying it to my algorithms.

Chase08:08:18

so there is a much faster way to get the last item from a string/sequence?

andy.fingerhut08:08:38

One simple way to think of it, perhaps: butlast / last take N steps for a string/sequence of length N. So if you have a length 10 string, the first time through the loop it takes 10 steps, the next time 9, the next 8, etc. So 10+9+8+7+6+5+4+3+2+1 steps is 55. The sum from 1 to N is equal to N*(N+1)/2, which is nearly identical to (N^2)/2

andy.fingerhut08:08:24

To give a hint in a different direction, rather than quickly getting access to the end of the string, you can definitely instead quickly get access to the chars of the string from first to last.

andy.fingerhut08:08:43

Where could you put the first character in some other collection, such that at the end that other collection is the chars reversed?

Chase08:08:44

ok, ok. This is great! So if I take the first thing from one collection and put it in the first thing of another collection technically it will reverse right if I keep putting more things on the front.

Chase08:08:39

(defn reverse-string [s]                                          
  (loop [s s                                                      
         reversed-chars '()]                                      
    (if (empty? s)                                                
      (apply str reversed-chars)                                  
      (recur (rest s) (cons (first s) reversed-chars)))))

Chase08:08:52

such a simple change but that is now way more efficient huh?

Crispin08:08:00

consider this:

Crispin08:08:04

user> (->> "this is a string"
           (into '())
           (apply str))
"gnirts a si siht"

andy.fingerhut08:08:44

Yes, for short strings you won't notice any significant difference in time, but give it a 100 megabyte string and there would be a huge time difference.

Chase08:08:39

I made another attempt at destructuring now but my tests are taking forever so I think I've done something wrong:

Chase08:08:46

(defn reverse-string [s]                                          
  (loop [[x & remaining] s                                        
         reversed-chars '()]                                      
    (if (empty? s)                                                
      (apply str reversed-chars)                                  
      (recur remaining (cons x reversed-chars)))))

Chase08:08:10

Crispin's solution is so simple!

Crispin08:08:58

for another solution using java interop

Chase08:08:53

interesting. I'm still a fan of your into solution.

Chase08:08:10

I'm curious why my destructuring doesn't work. how is [[x & remaining] s] different than using (first s) and (rest s)?

andy.fingerhut10:08:55

Have you tried debugging your failing solution with print statements, to show what the values of various expressions are through the loop iterations?

andy.fingerhut10:08:00

In your last non-working version, note that s never changes.

Chase15:08:16

ok, interesting. I'll try and add in the print statements. I'm confused how s doesn't change because I'm feeding remaining into recur which should be the "new" s I thought. Thanks for your advice and hints, this was very helpful!

Noah Bogart13:08:25

macro question! i have a macro that wraps a function call and looks to see if a map with a certain entry is in a specific argument position. if it is, it uses the existing map in an inserted expression. if the map isn't there, it creates and inserts a new version of the map into the original call and uses it in the inserted expression. It looks like this (the eid is the map I'm focused on): (wait-for (resolve-ability state side eid card targets) [...] (more-calls))

Noah Bogart13:08:58

Sometimes, the eid is created by function call within the macro call: (wait-for (resolve-ability state side (make-eid state :feature) card targets) [...] (more-calls)). Looking at the macro-expansion, it looks like this is read as a list and not as a map. how do I check to see if it's a function within the macro and then call it to see if it's the eid-style map I need before creating a new one or not?

noisesmith17:08:42

macros cannot look at runtime data, only the form you are compiling

noisesmith17:08:47

why do you think this should be a macro?

Noah Bogart17:08:06

I inherited it as a macro, is the simple answer

Noah Bogart17:08:35

feels like a potential mess to move it away from a macro, but potentially not

noisesmith17:08:38

even with the example you show, where eid is pre-generated, your macro doesn't see the value of the eid, it seems the symbol eid

noisesmith17:08:59

macros are source-in / source-out and can't use the data generated when your code actually runs

noisesmith17:08:46

there's a few problems with that macro, first off ~'use-eid# is pure cargo cult

Noah Bogart17:08:14

please give me all of your wisdom, cuz I didn't write this and have found it frustrating to use

noisesmith17:08:20

~'use-eid would let you use a specific symbol if you want a dirty macro that uses a specific binding that user supplied code can also see

noisesmith17:08:43

use-eid# generates a symbol that's guaranteed not to clash with user bindings

noisesmith17:08:48

mixing the two is just weird

noisesmith17:08:10

the logic is a bit weird, but it all acts properly on the symbols of the input form - it would take me a while to sort out the real problem it solves but it's one you don't need a macro for at my first sniff test

Noah Bogart17:08:51

the logic is: take an expression (`action`) and a bunch of further expressions (`expr`). wrap the expr in a new function, and place that function in a global register bound to an eid that's used by the action. once action is finished, it'll call (effect-completed eid), which then pulls the expr function from the global register and executes it, thus executing the rest of the expressions

Noah Bogart17:08:01

a poor-man's async/`await` specific to this engine

Noah Bogart17:08:23

thanks for the feedback, i'll try converting it to a normal function!

noisesmith17:08:43

a pattern that clojure.core uses is functions using lambdas as args for this sort of thing, then a macro that constructs the lambdas

noisesmith17:08:03

putting logic in functions and isolating macros to just be syntactic tends to simplify code

Noah Bogart17:08:25

yeah, that makes sense. do you have any examples on hand?

noisesmith17:08:02

with-redefs vs. with-redefs-fn

noisesmith17:08:07

future vs. future-call

Noah Bogart17:08:48

🙏 this is great, thank you

noisesmith17:08:29

binding vs. with-bindings is almost this pattern, and I bet it was in a previous edit

Noah Bogart13:08:22

The obvious solution is to write (let [eid (make-eid state :feature)] (wait-for (resolve-ability state side eid card targets) [...]))) but that feels verbose when I'm already using a macro that "hides" it for me

ivangalbans15:08:36

hello ! I have a client side app in ClojureScript, shadow-cljs, re-frame... I am using bidi and pushy too. I am trying add pagination to my app, but my problems is that bidi do not support query-parameters. For pagination I need to specify in the re-frame effect :http-xhrio an uri like this: /something?_page=123. My problem is that I do not know how can I get the query-parameters from the url to dispatch events that depend on them. Some ideas, maybe I am missing something trivial 🙂. Thanks

Lu17:08:43

Where are you storing your navigation data? Being current handler and possible params?

ivangalbans17:08:15

the navigation data is saved into app state

ivangalbans18:08:48

maybe my option is get the query parameters from window.location.search and parse it

Lu19:08:35

So why can’t you do within the reframe handler something like: (str “something/?page=” (get-in db [path to param]))

ivangalbans20:08:38

because when you press f5 the db is initialized and the data is lost

Lu11:08:34

If it’s just a matter of waiting for the db to be populated first you can create a subscription that checks if the db is populated and if so then you can dispatch your event-fx

Lu11:08:20

If you share some code it’ll be a bit easier to understand

Jazzer18:08:16

Hi everyone. I've got my head around quite a bit of clojure and I love it so far. My question today is: is how would I write a function (in arbitrary namespace) which creates a namespaced keyword for the ns calling the function?

hiredman18:08:50

namespaces are not like that

hiredman18:08:11

functions don't execute in a namespace

andy.fingerhut18:08:32

There is the ::kw-name syntax for the reader, that creates a compile-time constant keyword, but its namespace will always be the one that the keyword is written in.

hiredman18:08:31

namespaces are a thing to organize global names, functions are values and don't know what names point to them

andy.fingerhut18:08:38

Not sure if I understand your question correctly, though. You want to write a function in namespace A, and whenever it is called by a function in any other namespace B, you want the function in A to be able to figure out B at run time?

hiredman18:08:41

just like 5 doesn't know what names point to it

Jazzer18:08:57

Yeah, I can use that, but until I started using namespaced keywords I had helper functions that would return the keyword that I wanted out of a map. e.g. (defn player-keyword [player] (keyword (str "player-" player))

Jazzer18:08:08

Just trying to see if I can convert something like that to use an appropriate namespace

andy.fingerhut18:08:07

So at runtime you would like to create a keyword where the name is calculated by your code, and the namespace comes from the namespace the function was defined in.

Jazzer18:08:16

Currently I'm getting back :player-1 I guess I'd like to get back :my.app.name.ns/player-1

hiredman18:08:40

my suggestion is 1. just write the namespace, it isn't that hard

Jazzer18:08:44

that sounds like exactly what I'm trying to do, with one small twist... Ideally I'd like to use an aliased namespace in the namespace the function was defined in

hiredman18:08:49

2. don't use code namespaces for data

hiredman18:08:54

using the same names for to organize your code and your data ties your data to you code, which is brittle because code changes often, while data often ends up at rest and in huge volumes and doesn't change

Jazzer18:08:33

I think I'm following you @hiredman so what namespace should I put keys to my data map in?

hiredman18:08:47

just pick one

seancorfield18:08:59

You don't need a "real" namespace for qualified keywords...

hiredman18:08:03

keyword namespaces are not tied to code namespaces in anyway

seancorfield18:08:34

user=> (alias 'foo (create-ns 'foo.bar.quux))
nil
user=> ::foo/player-1
:foo.bar.quux/player-1
user=> 

Jazzer18:08:31

So I'd create a new namespace just to hold the keys for my data map. Seems like an unnecessary namespace, but I think I can follow the reasoning behind it to decouple from the functions

hiredman18:08:49

I wouldn't create a namespace

hiredman18:08:45

you don't need to unless you want to use '::' which is a bit of syntax and the only thing that does tie code and keyword namespaces together

hiredman18:08:00

and using '::' is a bad idea

hiredman18:08:28

user=> :whatever/b
:whatever/b
user=>

hiredman18:08:42

a namespaced keyword without a code namespace at all

Jazzer18:08:47

That does make sense, although I'm not sure I completely follow why :: is a bad idea

hiredman18:08:00

:: is context sensitive reader syntax

hiredman18:08:24

if you copy and paste the form into another context (the repl, another file, etc) you will get different results

Jazzer18:08:52

Is there not a higher probability of collision if I use :whatever/key, than if I use :my.app.data/key

Jazzer18:08:12

In my particular case, I don't really care. I don't think my code will currently interact with the outside world. I'm just trying to understand the thinking behind everything so that in the future I can make the best decisions.

hiredman18:08:14

you can use :my.app.data/key without my.app.data existing as a code namespace

hiredman18:08:04

something like :http://my.app/key might be nice

Jazzer18:08:35

Agreed. I'll go with that. thank you for your help and also for bearing with me slightly missing the point a few times!

hiredman18:08:58

it is confusing because: 1. keywords have namespaces 2. code is organized in namespaces 3. those namespaces are disjoint 4. the spec docs use '::' a lot

hiredman19:08:44

and 5. '::' can connect the disjoint worlds of 1 and 2

seancorfield19:08:02

In addition to ::bar which is completely context-dependent, there's ::foo/bar which is dependent on foo existing as an alias (and being consistent). I think the latter is "less bad" than the former, but it does rely on the alias/`create-ns` "trick" which needs to be present in every ns where you use ::foo/bar so the alias is resolved.

seancorfield19:08:35

But, as @hiredman says, it's really not a giant pain to just use the whole namespace -- and that makes the code clearer since you don't have to go see what the alias is...

seancorfield19:08:28

...and the namespace in a keyword is only there to make it contextually unique, i.e., it only needs to be unique across the context(s) in which it is actually used. So inside your app you can often get away with fairly coarse granularity, such as :billing/member-id rather than a "full" qualification.

Chase21:08:38

curious about some style guide suggestions for a messy cond statement. Not sure if you are familiar with the "Bob" exercise on exercism:

(ns bob                                                                               
  (:require [clojure.string :as str]))                                                
                                                                                      
(defn response-for [s]                                                                
  (cond                                                                               
    (and                                                                              
      (= \? (last s))                                                                 
      (= s (str/upper-case s))                                                        
      (not= nil (re-seq #"[a-zA-z]" s))) "Calm down, I know what I'm doing!"          
    (= \? (last s))                      "Sure."                                      
    (= "" (str/trim s))                  "Fine. Be that way!"                         
    (and                                                                              
      (= s (str/upper-case s))                                                        
      (not= nil (re-seq #"[a-zA-z]" s))) "Whoa, chill out!"                           
    :else                                "Whatever."))

Chase21:08:01

would you line up the "right side" of the cond statements like this?

Chase21:08:19

or do you prefer just having it right next to it?

Chase21:08:23

how ugly is this?!? It successfully passes all the test but I can't see how anyone would actually figure out all that's going on here. but maybe that's just the nature of the problem/solution because it's a convoluted set of requirements?

noisesmith21:08:54

sometimes using a let binding to pre-compute and name conditions helps

Chase21:08:00

I'll definitely explore how to do that. I almost wanted to do something like that and bind it to keywords and just use keywords in the cond statement because it looks so clean.

hiredman21:08:05

these days I line conds up as a vertical stack

(cond case-1
      result-1
      case-2
      result-2)

👍 4
noisesmith21:08:22

eg. for a name (let [yelling? (= s (str/upper-case s)) ...] (cond (and asking? yelling?) "Calm down, I know what I'm doing!" ...))

Chase21:08:47

yeah I like that!

hiredman21:08:07

I used to favor

(cond
  case-1 result-1
  case-2 result-2)
but find it harder to deal with as the case expressions and result expressions grow

hiredman21:08:52

once you start splitting the case's and the results over multiple lines the vertical stack is a must

Chase21:08:07

yeah, I actually did this too to see how it looked with my ugly one above. maybe I combine both your suggestions

Chase21:08:06

the re-seq portions were to handle two edge test cases that basically had only numbers and non letters in them. like "4?" and "1, 2, 3" What do you think about that? everything else seems kind of self explanatory but not that part to me at least.

Chase21:08:14

is that what you would explain in a doc string?

Chase21:08:23

ooh maybe just come up with a good name for the binding in the let statement for that...

Chase22:08:11

New and improved:

(defn response-for [s]                                                                
  (let [yelling?  (= s (str/upper-case s))                                            
        question? (= \? (last s))                                                     
        nothing?  (= "" (str/trim s))                                                 
        just-numbers? (not= nil (re-seq #"[a-zA-z]" s))]                              
    (cond                                                                             
      (and question? yelling? just-numbers?)                                          
      "Calm down, I know what I'm doing!"                                             
      question?                                                                       
      "Sure."                                                                         
      nothing? "Fine. Be that way!"                                                   
      (and yelling? just-numbers?)                                                    
      "Whoa, chill out!"                                                              
      :else "Whatever.")))

Chase22:08:38

ugh, looks so ugly in the small thread but I swear it looks way better in person! lol

Chase22:08:57

guess it needs to be called not-just-numbers?

Chase22:08:07

anyways, thanks so much folks!