Fork me on GitHub
#beginners
<
2021-01-08
>
Daniel Tobias03:01:51

hello all, I am trying to make an app in cljs (with reagent) and it needs to interact with an API that's being hosted by a django backend. Is there a way to do the neat little development environment but somehow proxy all my requests to the django backend?

dumrat03:01:36

In this video, there's a quote from Rich Hickey and the presenter says that if there's one thing to be taken away from the talk, it should be that:

When you combine two pieces of data you get data. When you combine two machines you get trouble.
I understand what this means (I think). But can someone explain why this has to be stated explicitly? Or what's the context in which this was quipped by Rich Hickey? https://www.youtube.com/watch?v=C-kF25fWTO8

euccastro03:01:07

@dan.yb.tobias IIUC your django backend will be the only web server, right? then just tell your build tool to put the generated javascript somewhere that your django backend will know to serve

euccastro03:01:41

in principle it's not very different to working with a Clojure backend

Daniel Tobias03:01:08

doesnt figwheel or whatever spin up a little development server?

Daniel Tobias03:01:14

i dont want to have to compile every time i make a change etc

euccastro03:01:45

that's independent of the server that actually serves your main web page

euccastro03:01:16

if you serve a html doc like the one described in that page from your web server, and if your web server serves the right file when asked for "/cljs-out/dev-main.js", you should be good

Daniel Tobias04:01:48

im a bit confused here, the doc linked doesnt say anything about it

Daniel Tobias04:01:35

do i need to change the port of the ring server to match as well?

Daniel Tobias04:01:51

I am so confused, am i running fig:build or what?

euccastro04:01:51

your django server would replace the ring server. it doesn't matter what port it listens on, as long as you visit localhost:<that-port> in your browser

euccastro04:01:19

you can run any regular figwheel command

Daniel Tobias04:01:08

there is no html file produced though?

Daniel Tobias04:01:01

oof this is not working at all, i made an html and loaded the js there but

Daniel Tobias05:01:04

@U65FN6WL9 it looks like how the js is trying to load files is bad...it's using escape charatcers in strings?

zackteo03:01:48

Hi everyone, I'm not sure where I should ask this but am trying to get the automated testing for clojurewerkz/money working again. Seems like at least for openjdk8 all tests pass but am encountering Tried to use insecure HTTP repository without TLS as shown here https://travis-ci.org/github/clojurewerkz/money/builds/753470823 Understand that this is the result of the snapshot deployment link for Nexus, but does anyone know what link i should update it to in the project.clj here - https://github.com/clojurewerkz/money/blob/master/project.clj#L19

seancorfield04:01:33

@zackteo Change http:// to https://?

zackteo05:01:22

Wow it did work. Guess i should have just tried that

orpheus04:01:15

Hey guys and girls. New to Clojure, I'm learning by writing clojure to solve problems at http://projecteuler.net. I just solved #3 using Clojure and I'd like to get some advice and tips about how I could've written my code better. Here's a gist, lmk if the link works: https://gist.github.com/orpheus/e803cb8fb90f925fda90b07576273741 Also, there's an error I could use help debugging with the getFactor function, if passed 6857 (`getFactor 6857`) there's a stack overflow that I'm not sure how to go about solving.

dumrat04:01:33

I did some Project Euler problems some time ago. You can find here: https://github.com/nakiya/euler/blob/master/euler.clj Just check for problem 3

Rowan Barnard09:01:46

You might also want to look at https://www.4clojure.com/ which has problems designed to help you learn the language.

zackteo04:01:57

@seancorfield thought about that. Guess I could give that a try. Was thinking if the link was meant to be inaccessible

euccastro04:01:06

@titanroark a quick thing to try would be to replace recursive calls to getFactor with recur: https://clojuredocs.org/clojure.core/recur

orpheus16:01:44

Thank you, this is the kind of advice I was looking for

euccastro04:01:26

also, to find factors you only need to check up to the square root... but now we're talking math. if your current primary focus is learning Clojure, http://projecteuler.net problems may have the focus too much on the math side?

euccastro04:01:42

factorial is a different math concept. stick to the name "factor"

3
Daniel Tobias04:01:06

euccastro i think what you suggested before isnt quite right

👀 3
euccastro04:01:56

sorry, I had missed your reply; I've just replied in that thread

euccastro04:01:16

lines 8-11 are the same as line 14, just specialized for the number 2. you could just call (getFactor x 2) at that point

3
zackteo05:01:15

Not sure if this falls under beginners but has anyone encountered a situation where a new github pull request (that just changes the readme breaks the existing clojure tests? CI on github actions) https://github.com/zero-one-group/fxl/pull/31/checks?check_run_id=1666803003 It is very strange. Because when I try rerunning the tests on the previous commit there is no issue. But once I make a new pull request, even in the minimal example just amending the readme, breaks the CI

seancorfield06:01:50

It's possible the tests are non-deterministic or something in the tests is not compatible with the GitHub Actions environment...

zackteo06:01:37

hmmm, my issue is that it would make sense that the github actions doing the test would have failed for the previous commits then or failed when I reran the actions but no issues

zackteo06:01:02

It is just generally quite an odd situation

seancorfield06:01:51

Computers are deterministic by definition 🙂

zackteo06:01:34

My own theory is that the github secrets might be having issues when I am the one pushing the commit

zackteo06:01:35

Tho I am not sure if that theory checks out with the github actions history :thinking_face:

seancorfield06:01:46

In which it should be intermittent and kicking off the process again should succeed...

zackteo06:01:51

Oh I mean the owner of the repo might be the only one able to access the secrets - I can't tell if that makes sense

zackteo06:01:11

guess all I can do is sleep on it

roelof08:01:07

Do not know what is the right channel for pedestral questions so I ask here I have this :

`(ns hello
  (:require [io.pedestral.http :as http]
            [io.pedestral.http.route :as route]))

(defn respond-hello [request]
  {:status 200 :body "Hello, world!"})

(respond-hello {})

pavlosmelissinos08:01:34

You have a typo, it's called pedestal, not pedestral. (Remove the r ) FYI there's #pedestal

roelof13:01:42

See that I have to learn a lot more about clojure to make my own sites and really understand how things really work

roelof14:01:02

interceptors are I think the hard part

pavlosmelissinos14:01:01

You don't need interceptors to make a site

pavlosmelissinos14:01:36

Pedestal bundles a few things together

pavlosmelissinos14:01:31

If you find it hard to grok you could go a step back. I think the absolute minimum for a beginner is something like compojure and figwheel but it's best to ask around

roelof14:01:46

maybe I will or go back to duct or take a few steps more back. Before pedestal I did a few tutorials about duct and before that about ring.

roelof08:01:31

deps.edn

{:deps
 {io.pedestal/pedestal.service {:mvn/version "0.5.7"}
  io.pedestal/pedestal.route   {:mvn/version "0.5.7"}
  io.pedestal/pedestal.jetty   {:mvn/version "0.5.7"}
  org.slf4j/slf4j-simple       {:mvn/version "1.7.28"}}
 :paths ["src"]}

roelof08:01:01

but still I see this error :

; Execution error (FileNotFoundException) at hello/eval13893$loading (REPL:1).
; Could not locate io/pedestral/http__init.class, io/pedestral/http.clj or io/pedestral/http.cljc on classpath.

benny14:01:08

Your deps.edn looks fine. But you have a misspelling in your require. "pedestral"

pez09:01:59

What am I not getting?

(defn vec-of-14 [& seqs]
  (count seqs) ;=> 3
  (->> seqs
       (concat)
       (take 14)
       (vec)) ;=> Eval timed out!
  )

(comment
  (vec-of-14 (repeat 14 true)
             (repeat 1 false)
             (repeat true)) ;=> Eval timed out!
  
  (->> (concat (repeat 14 true)
               (repeat 1 false)
               (repeat true))
       (take 14)
       (vec)) ;=> [true true true true true true true true true true true true true true]
  )
So, the second form in the comment there works as I expect. Trying to make a function from it blows up. The seqs are realized is my guess, but when and how and why?

Matheus Moreira10:01:47

You probably need to replace (concat) by (apply concat) in your method.

pez10:01:05

Oh, of course… Thanks!

3
benny14:01:24

what would be the pattern if I basically want to block until a certain condition is true? I'm waiting on a file and the last-modification time keeps changing and I want to wait until it doesn't change any longer for 100ms or something

Chase15:01:08

So what exactly are the differences between `(filter #(<= 5 %) coll)` and `(filter (partial <= 5) coll)` ? Is that second example using a "transducer?"

Chase15:01:39

From reading the home page docs I don't think so. I should be creating an "xform." I get the feel that transducers are the current trend in performant, idiomatic clojure but I'm mostly confused about it all.

Chase15:01:48

Any resources you recommend to start thinking in the transducer way of doing things? I feel like that could (should?) be the next step in upping my fp game.

Chase15:01:50

What initially inspired this is this reddit thread: https://www.reddit.com/r/Clojure/comments/kr0j0f/newbie_question_on_idiomatic_iteration/ I want to start thinking like how SimonGray and joinr handled the problem

dpsutton15:01:57

neither of those are using transducers

Chase15:01:48

I knew the first one wasn't, just wasn't sure if that second one was. I guess I don't really know what I'm asking besides how to start thinking in the higher abstraction, transducer sense of solving problems. Probably the only answer is experience.

dpsutton16:01:42

read that guide and internalize what transducers are. i think its really important to remember they are functions that take reducing functions and return reducing functions

Chase16:01:21

Will do. The embarrassing part is the first paragraph reiterates: Note: this reduced arity is not currying or partial application. And here I am asking if that partial produced a transducer. lol

dpsutton16:01:56

no worries. they can be a bit difficult to grok them. follow that guide and build up your intuition and also keep that definition in mind: a function from a reducing function to a reducing function

Chase16:01:16

And are they more performant to use in most cases? I know that's probably to general to ask though. But is it something you usually try and reach for when writing idiomatic clojure?

dpsutton16:01:23

at the top is a link for "good use cases for transducers" which has a section about performance. nothing more authoritative or thoughtful than that section

Chase16:01:00

Fair enough! Thanks

didibus18:01:25

These are the same

didibus18:01:27

The only difference is that #(<= 5 %) creates the predicate function at read time, and (partial <= 5) creates the predicate function at run-time

didibus18:01:55

The docstring you're reading is referring to the arity (number of arguments) of filter

didibus18:01:32

Both your example call filter with 2 arguments though, so in your case you are using the 2-ary version of filter in both case

didibus18:01:36

The doc says return a transducer when no collection is provided, and in both cases you provide [pred coll]

roelof16:01:26

again stuck how I have to deal with this :

; Implement update-in.

(def my-update-in [m k f]
  
  )
I think at some way I have to find the part with the key and then apply the function at the value and put the whole m again together. Do I thinking the right way ?

andy.fingerhut16:01:55

Sounds like a good plan, yes. If by "put the whole m again together" you are thinking "create a new map in N steps if the original map m has N key/value pairs", then there are faster ways, e.g. "create a new map from m by replacing only the value of key k with the new value"

roelof16:01:04

hmmm, then I have to think again

roelof16:01:34

that could work but on some way I have to check where I want to check things

andy.fingerhut16:01:43

You could get a correct solution by creating a new map in N steps.

andy.fingerhut16:01:16

It probably just would not be among the fastest solutions.

andy.fingerhut16:01:47

Your original statement said "and put the whole m again together".

andy.fingerhut16:01:59

There are multiple things you might have meant when you said that, and I don't know what you were thinking.

roelof16:01:17

that is not the most important I think. This is a challenge from the brave book and I think the purpose is to understand how things work

andy.fingerhut16:01:33

One thing you might have meant was "for each of the N keys in the original input map, add that key to a new map to be returned"

roelof16:01:33

not find the fastest one

roelof16:01:56

Lets say we have this map : (def users [{:name "James" :age 26}  {:name "John" :age 43}])

andy.fingerhut16:01:09

That is a vector containing two maps.

roelof16:01:15

another one then : {:name "James" ;age 26}

andy.fingerhut16:01:20

That is one map 🙂

roelof16:01:46

now I want to change the age to 36 because I made a typo

roelof16:01:12

then my idea was to use for example `get to get the age out of it

roelof16:01:33

make a new age with 36

roelof16:01:55

and then I have to make a new map with the name part and the new age part

roelof16:01:41

or if I want to change the name a new map with the new name and the old age part

roelof16:01:08

but I think I do not see how the pieces comes together

andy.fingerhut16:01:15

My only point was that there is more than one way to do that last step, and I was making a comment about efficiency of a couple of different ways. If you aren't worried about efficiency at all, then by all means ignore that comment.

roelof16:01:31

I think I have to store the new part somewhere with a let ?

andy.fingerhut16:01:56

I wouldn't say "have to", but if it helps you write a solution, then let is perfectly reasonable.

roelof16:01:54

(def my-update-in [m k f] (let [new_value (f (get m k)]      )

andy.fingerhut16:01:04

If you want to use let, then taking your breaking the function up in to steps, you can first get the current value in the map for key k, and bind that result to some new name in the let, e.g. cur-val or whatever you would like to call it.

andy.fingerhut16:01:23

Sure, what you do there is doing your first two steps in one line, which is also good.

roelof16:01:47

oke , if I go to fast , say it please

andy.fingerhut16:01:03

I will. Don't worry, I'm following so far 🙂

roelof16:01:32

then I have to make a new map. I think I need to use assoc

roelof16:01:02

but how do I now from the old map what is then the old part ?

roelof16:01:26

and what is the new part?

andy.fingerhut16:01:12

So assoc always returns a new map, leaving the original one unchanged. The new map is almost the same as the input map, except for the value associated with the key you give to assoc, which in the returned map will be the value you give to assoc

roelof16:01:19

yep, so there im stuck

andy.fingerhut16:01:21

user=> (assoc {:a 1 :b 2 :c 3} :b -2)
{:a 1, :b -2, :c 3}

roelof16:01:43

oke, so I do not have to know

andy.fingerhut16:01:49

I didn't tell assoc to do anything with keys :a or :c, and the returned map still has them, associated with their original values.

andy.fingerhut16:01:10

That is what assoc is intended to do.

roelof16:01:44

oke, did not realize that

andy.fingerhut16:01:37

Has the book you are following described assoc before this exercise, I hope?

roelof16:01:46

so I can do just this :

(def my-update-in [m k f]
   (let [new_value (f (get m k)]
   (assoc m new_value) 
  )

roelof16:01:58

yep, I did

roelof16:01:20

but I think I misunderstood it as just for adding things

andy.fingerhut16:01:21

Your call to assoc doesn't have the correct number of parameters.

roelof16:01:09

(def my-update-in [m k f]
   (let [new_value (f (get m k)]
   (assoc m k new_value) 
  )

andy.fingerhut16:01:21

that looks better

andy.fingerhut16:01:54

Looks like a good time to try out your new function with some sample input parameters in a REPL and test if it works as you hope.

roelof16:01:10

there is still a error somehow

andy.fingerhut16:01:27

What editor are you using?

roelof16:01:32

calva is yelling me that the closing ] is on the wrong place

roelof16:01:47

I use vs code with calva

andy.fingerhut16:01:35

Most editors have the option to show you when parens, square brackets, or curly braces are matching or not. You definitely want to get familiar with how to understand what it is telling you there.

andy.fingerhut17:01:29

e.g. many editors will show you the matching paren, bracket, or brace when your cursor is on or just after one of them, highlighting it visually so you can see which one matches the one where the cursor is.

roelof17:01:41

yep, I was missing a )

andy.fingerhut17:01:02

Trying to write a Lisp-like language without such editor support is going to slow you down tremendously.

andy.fingerhut17:01:37

There is a lot of fancy 'structural editing' stuff you may have heard about that some people love, but I've never used that myself. But I do really, really miss it if I ever use an editor that doesn't show me matching parens and brackets.

roelof17:01:30

still somewhere a error :

; Execution error (ClassCastException) at chapter5/my-update-in (form-init95645667318631163.clj:51).
; 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')
code :
(defn my-update-in [m k f]
  (let [new_value (f (get m k))]
    (assoc m k new_value)))

(my-update-in p :age 35)

andy.fingerhut17:01:57

Is there anything else in the same file with that function? Looking at the function definition, nothing obviously wrong jumps out at me.

andy.fingerhut17:01:25

What is the 3rd parameter of my-update-in supposed to be?

roelof17:01:20

Im my first idea it was a function but later it changed to the new value

andy.fingerhut17:01:35

What does my-update-in do with the 3rd parameter?

roelof17:01:10

it uses it to make the new map with a updated key

roelof17:01:32

so it is not a function

andy.fingerhut17:01:33

The only place the 3rd parameter is used in my-update-in is this expression (f (get m k))

andy.fingerhut17:01:49

So it is trying to call f as a function.

roelof17:01:19

yep, I see it , thinking how tto solve it

andy.fingerhut17:01:53

That is what the exercise asked you to write, I think, yes?

andy.fingerhut17:01:32

Then when you are trying to use my-update-in, you gave it the number 35 as the 3rd parameter, and my-update-in tried calling the number 35 as if it were a function.

andy.fingerhut17:01:02

So it appears to me that your my-update-in is doing what the exercise says it should, but your attempt to call the function is passing a 3rd parameter that it was never intended to handle.

andy.fingerhut17:01:46

i.e. you are calling my-update-in incorrectly. You should try calling it where the 3rd parameter is some function.

andy.fingerhut17:01:00

If you want a quick try, replace 35 with inc, which is a function.

roelof17:01:00

yep, that works

andy.fingerhut17:01:34

Does it make sense why you got the error when you passed in 35?

roelof17:01:52

it makes sense

andy.fingerhut17:01:10

Do you know how to write a Clojure function that takes a number and adds 10 to it?

roelof17:01:57

yep

defn add-ten [number] (+ number 10))```

andy.fingerhut17:01:18

Good. If you define that function, you could pass add-ten as the 3rd parameter to my-update-in

roelof17:01:51

oke, so there is no way I could make it work without a function ?

roelof17:01:08

only given the right value

andy.fingerhut17:01:10

You could write a different function than my-update-in

andy.fingerhut17:01:27

that takes a new value for the 3rd parameter instead of a function.

andy.fingerhut17:01:45

If you did, I would call it my-assoc 🙂

roelof17:01:51

I was thinking to change my_update_in

andy.fingerhut17:01:26

If you change my-update-in to take a value instead of a function as the third parameter, you can certainly do that. Note that it would not be one that solves the exercise you were working on.

roelof17:01:18

?? the only that the challenge said it to implement update-in

andy.fingerhut17:01:29

Yes, I understand that.

andy.fingerhut17:01:43

YOu asked if you could change my-update-in to behave differently. You can do that if you want.

andy.fingerhut17:01:54

I am only saying that such a changed my-update-in does not solve the challenge.

roelof17:01:37

oke, I see it, update-in takes always a function

andy.fingerhut17:01:00

as the 3rd parameter, yes.

roelof17:01:03

so I wanted to make another function

andy.fingerhut17:01:22

And I encourage you to experiment to your heart's content 🙂

roelof17:01:30

thanks for thinking with me and teaching me some things

roelof17:01:43

clojure is a totally other beast then when I was learning ruby or haskell

roelof17:01:06

but I love the way repl helps me to see the results when I try something

andy.fingerhut17:01:07

You could write my-update-in in Haskell in about the same number of characters, I suspect. Clojure and Haskell share that they deal mostly with immutable values, but Haskell has far fewer "escape hatches" than Clojure does for mutating state.

andy.fingerhut17:01:27

And Clojure doesn't restrict you to writing code that passes Haskell's type checker.

roelof17:01:20

and clojure is much faster then a haskell programm is my feeling

roelof17:01:35

and I played with some web stuff and clojure has much more choices then haskell

roelof17:01:55

in haskell you have only yesod and servant as big players

andy.fingerhut17:01:26

I don't know a lot about Haskell performance to compare it to Clojure -- they both are not necessarily the language you want to use to get the best performance for heavy numerical computation, for example.

roelof17:01:41

clojure has for back-end duct, pedestral en some more

andy.fingerhut17:01:51

but both are perfectly fine for calling out to libraries written in other languages for doing that kind of thing.

roelof17:01:54

but much more to do for your own

roelof17:01:26

dinner time

roelof17:01:50

and tomorrow or later time to study the next chapter macro's 🙂

Ty16:01:41

Anyone using cider with Doom emacs? Trying to figure out the most appropriate way to evaluate the form under the cursor

👀 3
Max Deineko17:01:14

I'm using it with lispy[ville] and lispy-eval keybinding (iianm I had to add com.cemerick/pomegranate and com.billpiel/sayid to my clojure deps for it to work)

Max Deineko17:01:48

Apart from that and cider-eval-… bindings as well as cider-pprint-eval-last-sexp-to-comment I so far have found cider debugger with enlighten-mode quite useful. Seeing how flexible this all is I'd be also interested in seeing what others are using for quick & easy inspection of forms & values 🙂

benny18:01:31

I moved away from lispy because of le-clojure.clj. I use cider-eval-defun-at-point and cider-eval-last-sexp ... If I ever need to be more granular, I use er/expand-region and then cider-eval-region ... all these are bound to things like , e d , e e etc

Max Deineko18:01:25

@U014JFA9EP5 Are you're referring to the part of lispy provided by le-clojure.el ? Does it have some flaws one should be aware of?

Daniel Tobias18:01:20

it really doesnt look like cljs works well with a django backend 😛

Daniel Tobias18:01:25

i am really struggling to get it working

jmayaalv18:01:22

The backend and front end are orthogonal. If you return Json the backend technology is irrelevant for cljs. Do you have a more specific problem you can share?

Daniel Tobias18:01:12

i think it's more about the project.clj file, im doing a figwheel-main reagent template and I guess i need to hook it up so that the project itself isnt necessarily in the django folders but the produced html/js/css ends up in the correct places

Daniel Tobias18:01:20

there's just like 2 million directories specified in this thing

Daniel Tobias18:01:31

looks like the configuration doesnt like relative paths either

jmayaalv18:01:54

can you share your project.clj?

Daniel Tobias19:01:14

(defproject livematch "0.1.0-SNAPSHOT"
  :description "FIXME: write this!"
  :url ""
  :license {:name "Eclipse Public License"
            :url ""}

  :min-lein-version "2.7.1"

  :dependencies [[org.clojure/clojure "1.10.0"]
                 [org.clojure/clojurescript "1.10.773"]
                 [reagent "0.10.0" ]]

  :source-paths ["src"]

  :aliases {"fig"       ["run" "-m" "figwheel.main"]
            "fig:build" ["run" "-m" "figwheel.main" "-b" "dev" "-r"]
            "fig:min"   ["run" "-m" "figwheel.main" "-O" "advanced" "-bo" "dev"]
            "fig:test"  ["run" "-m" "figwheel.main" "-co" "test.cljs.edn" "-m" "livematch.test-runner"]}

  :profiles {:dev {:dependencies [[com.bhauman/figwheel-main "0.2.12"]]
                   
                   :resource-paths ["target"]
                   ;; need to add the compiled assets to the :clean-targets
                   :clean-targets ^{:protect false} ["target"]}})

Daniel Tobias19:01:46

the main issue im having i think is the dev-main.js can't find any of the deps it needs for some reason

Daniel Tobias19:01:07

if i look at dev-main.js i have lines like this:

Daniel Tobias19:01:10

document.write('<script src="..\..\paladeenz\website\static\livematch\public\cljs-out\dev/cljs_deps.js"></script>');

Daniel Tobias19:01:15

notice the escape characters..

Daniel Tobias19:01:40

it's just copy and pasting the target directory..but remember the src is in a different folder than my django app...so when the js gets put into the django app it needs to look in the correct place for the deps but isnt

jmayaalv19:01:05

what’s dev-main.js?

jmayaalv19:01:28

where is this file? and what’s suppose to do?

Daniel Tobias19:01:51

this is the output js from building...i fixed it by editing all of the urls the issue is that if recompiled it will overwrite and i have to change it over and over

Daniel Tobias19:01:50

the target-dir i set is so that it can create dev-main.js in the correct spot, but dev-main itself seems to be using the target-dir also to find the dependencies which completely breaks it

Daniel Tobias19:01:26

so i have a figwheel-main.edn file which says

:target-dir "../../paladeenz/website/static/livematch"

Daniel Tobias19:01:52

which puts dev-main.js where it needs to be..it's just now dev-main.js is trying to load files from target-dir + other stuff, which is wrong

jmayaalv19:01:40

it’s long time since i use fighweel.main but let me see if i can find something

Daniel Tobias19:01:27

im going to try adding in output-to and output-dir

Daniel Tobias19:01:31

NVM it didnt like those keywords haha

Daniel Tobias19:01:40

it's like this stuff is designed to be as cryptic as possible

jmayaalv19:01:25

did you try maybe asking in the #figwheel-main channel, i think somebody with more experience than me will be able to help you (and wife is screaming at me now 8:30 pm now :D)

Daniel Tobias19:01:52

ok ill ask there, ty

roelof20:01:21

What is here wrong ?

(defn my-update-in [m ks f args]
  (assoc m ks (apply f (get m ks) args)))

(my-update-in p [:1] :age inc)
error :
; Execution error (IllegalArgumentException) at chapter5/my-update-in (form-init95645667318631163.clj:51).
; Don't know how to create ISeq from: clojure.core$inc

noisesmith20:01:01

you specify f before args, then :age before inc

noisesmith20:01:39

also (get m ks) is weird - it's as if you expect the whole seq of keys to be one key

noisesmith20:01:10

neither :age nor inc are collections, so neither works as a last arg to apply

roelof20:01:39

I will sleep about this one :

(defn my-update-in [m ks f & args]
  (let [[[k & ks] ks  (assoc m k (apply f (get m ks) args))]])
error ;
; Syntax error macroexpanding clojure.core/let at (chapter5.clj:51:3).
; [[[k & ks] ks (assoc m k (apply f (get m k) args))]] - failed: even-number-of-forms? at: [:bindings] spec: :clojure.core.specs.alpha/bindings

andy.fingerhut20:01:13

When first learning, I would recommend avoiding the use of destructuring, until and unless you get a version of a function working without it.

andy.fingerhut20:01:44

Destructuring can be useful, but it is never necessary.

roelof20:01:00

hmm, still no luck

(defn my-update-in [m ks f & args]  
  (assoc m ks (apply f (get m ks) args)))



(my-update-in p [:0] assoc :name "James" :age 35)
gives this :
{:name "James", :age 26, [:0] {:name "James", :age 35}}

roelof20:01:27

maybe the apply is wrong here

roelof21:01:26

but I do not see any better one

andy.fingerhut21:01:59

It is almost always possible to take a Clojure function and evaluate it step by step in a REPL session.

andy.fingerhut21:01:14

Do you know which sub-expression of your my-update-in above must be evaluated first?

roelof21:01:46

I would say get m ks

andy.fingerhut21:01:06

And what parameters it should be passed in your example call, and what value it will return?

andy.fingerhut21:01:27

(If you are not sure, that is where the REPL can be handy)

roelof21:01:08

i think the old value is taken where then the function is applied to

andy.fingerhut21:01:26

It isn't clear to me what the value of p is in your call above

roelof21:01:58

sorry, I forget to paste that

roelof21:01:02

(def p {:name "James" :age 26})

roelof21:01:31

we solved that one earlier but I try to make it work that it also worked with (def users [{:name "James" :age 26} {:name "John" :age 43}])

andy.fingerhut21:01:32

So what does (get m ks) return?

roelof21:01:03

in the example of p it will return 26

andy.fingerhut21:01:23

Do you prefer to try to figure out why your current function is behaving like it is? Or do you prefer to start with the input/output behavior of the function you want to write, and try to write that?

andy.fingerhut21:01:47

Now, try eval'ing this in a REPL: (get p [:0]

andy.fingerhut21:01:52

and see what it returns.

roelof21:01:23

I think it schould be (get p p: [:0])

roelof21:01:13

that returns nil

roelof21:01:18

so not good

andy.fingerhut21:01:21

Does it make sense why it returns nil?

roelof21:01:07

yep, I think there is no key named [:0]

andy.fingerhut21:01:48

So we can keep looking at what happens with your current function, but maybe you might prefer starting with explaining what function behavior you are hoping to write, because I don't know what that is yet.

andy.fingerhut21:01:41

You say you want a function that takes a vector of maps like this: (def users [{:name "James" :age 26} {:name "John" :age 43}]) and does something with it, but I'm not sure what you want the behavior to be.

roelof21:01:54

I was hoping to get first a record from a map so in this example {:name "James" :age 26}

andy.fingerhut21:01:19

So in Clojure terms, that would be getting the first element from a vector or sequence.

roelof21:01:19

and then I schould find a way to update what I want to update

andy.fingerhut21:01:33

The value of users is not a map, but a vector of two elements (each of those elements is a map)

roelof21:01:41

yep, or the second if I want to update the second entry

andy.fingerhut21:01:39

So try to explain what the parameters of the function you want are, and see if you can give one or two examples of what you want the return value to be for those example inputs, along with a sentence explaining what the function ought to do.

roelof21:01:22

oke, in the example of p. Lets say I want to update the age to 45

roelof21:01:50

then m is still p so {:name "James" :age 26}

andy.fingerhut21:01:54

So p is a single Clojure map, right, meaning this one: (def p {:name "James" :age 26})

roelof21:01:23

I work from that example first

roelof21:01:32

later we can do more entries

andy.fingerhut21:01:36

So you wrote my-update-in earlier that could take m, a key k, and a function f, and do inc or add-ten, or whatever function you want, to the current value associated with that key.

andy.fingerhut21:01:39

That was working.

andy.fingerhut21:01:58

So now you are saying you want a function that can take a vector of maps, and do something with that.

andy.fingerhut21:01:33

My first comment would be: you could write a single function that does both, but maybe given that the first argument is a map in one case, but a vector of maps in the other, maybe a different function might be better.

roelof21:01:09

to be sure we are on the same page

andy.fingerhut21:01:14

i.e. try writing a function that only works if the first argument is a vector of maps, without trying to serve both purposes.

roelof21:01:56

I want to end at a function that can do this :

(def m {:1 {:value 0, :active false}, :2 {:value 0, :active false}})

(update-in m [:1] assoc :value 1 :active true)

outcome : 

{:1 {:value 1, :active true}, :2 {:value 0, :active false}}

andy.fingerhut21:01:06

Once you can see both functions on their own, and get them working, then maybe go back and see if it makes sense to try to write some combination of them that can do both behaviors. But I suspect it will only be more confusing to try to write that combined-behavior function first.

roelof21:01:33

oke, no problem to take one of more steps back

andy.fingerhut21:01:37

So in this example call (update-in m [:1] assoc :value 1 :active true) you have a vector containing :1. Do you want this proposed update-in function to be able to take any number of keys in that vector, 1 or more? Or do you want it to only work for 1 element in that vector?

andy.fingerhut21:01:03

If you only want it to work for 1, then why put it in a vector?

roelof21:01:05

maybe better to make it work with 1 element first

roelof21:01:35

and then look what needs to be changed for multiple entries in the vector

andy.fingerhut21:01:48

I guess perhaps from the name update-in, you might be thinking along the lines of how get-in works in Clojure?

andy.fingerhut21:01:20

Are you familiar with get-in?

roelof21:01:28

yep, that one is explained in this chapter as Whereas the function get-in` lets you look up values in nested maps, `

andy.fingerhut21:01:28

So if your update-in is intended to be similar to get-in, in that it can take a vector of keys, and update a value in a nested map, then it makes sense that it should be a vector. In fact, your update-in would likely want to use get-in to get the current value.

roelof21:01:43

it does not work as expected :

(def m {:1 {:value 0, :active false}, :2 {:value 0, :active false}})

(get-in m [:1][:active] )
gives
{:value 0, :active false}
I expect to see only false

andy.fingerhut21:01:28

How many keys to you give to get-in /

andy.fingerhut21:01:06

Oh, I see what you did. That is not how you give a sequence of multiple keys to get-in. Try this: (get-in m [:1 :active])

andy.fingerhut21:01:26

1 sequence of keys, not multiple separate sequences of keys.

roelof21:01:32

yep, that works

andy.fingerhut21:01:35

get-in takes an optional 3rd parameter, that if you give one, and if the sequence of keys you give are not found in the collection, it returns the 3rd parameter to indicate "not found", instead of nil

andy.fingerhut21:01:13

In this call: (get-in m [:1][:active]) there is a key :1, so the 3rd parameter is not used.

roelof21:01:36

yep, I see it : (get-in m [:3 :active] "not-found")

roelof21:01:45

gives not found

andy.fingerhut21:01:56

yep. as it is documented to do.

andy.fingerhut21:01:10

(once the documentation makes sense, that is 🙂

roelof21:01:23

yep, and I tried to see if I really understand things

andy.fingerhut21:01:47

experimentation at the REPL with different variations of an idea are definitely good ways to test your understanding.

roelof21:01:14

yep, I will , can we talk tomorrow about this further

roelof21:01:27

its late here, 22:36

andy.fingerhut21:01:01

sure. I will warn you that I expect update-in will be a bit more code to write than my-update you wrote earlier, but doable with patience.

roelof21:01:12

and I will experiment if I can make it work on a different keyword, so on value and active

andy.fingerhut21:01:06

It is definitely a good idea to have a good understanding of what Clojure built-in functions like get and get-in do, especially when relying on them to write your own functions.

roelof21:01:15

and then try to adapt it to make it also work on the different entries so the keyword and the number of entries (1 or 2)

roelof21:01:49

so first understand that

roelof21:01:11

and then look how I could change a value and make a new map

roelof21:01:16

thanks for the lessons

roelof21:01:41

you are the first one which do not give a answer but let me think about a solution

roelof21:01:45

for now GN

andy.fingerhut21:01:03

It usually takes longer to ask questions that let the learner think about a solution :-)

andy.fingerhut21:01:41

My hope is that you will learn questions to ask yourself, and habits of thought, that let you more often find the answers yourself.

roelof21:01:01

and got the code where the number and the keyword is a variable

(defn get-on-keyword [m keyword number]
  (get-in m  [number keyword] "not-found"))

roelof21:01:08

and now really sleep

roelof22:01:43

Maybe a idea to try now t change the get-in into update-in ? after some sleep naturlly

roelof09:01:03

grrr. now again a wierd error

(defn my-update-in [m ks f & args]
  (let [[k & ks] ks]
    (assoc m k (apply (get m k) ks f args))))


(my-update-in m [:1] assoc :value 1 :active true)
error:
; Execution error (ArityException) at chapter5/my-update-in (form-init6180925798125192449.clj:54).
; Wrong number of args (6) passed to: c
I always thought that & args was taking in all the arguments that were left So why still a argument error

roelof09:01:00

and when im using our code from yesterday

(defn my-update-in [m keyword number]
  (assoc m  [number keyword] "not-found"))
it added a new value instead of chancing it

roelof10:01:10

This works with the m variable but not with the p variable

roelof10:01:14

(defn my-update-in [m keyword number function]
  (function (get-in m  [number keyword]) ))

(update-in m [:1] assoc :value 1 :active true)

(def p {:name "James" :age 26})

(defn add-ten [number]
    (+ number 10))

(my-update-in p add-ten)

roelof10:01:34

I see a arityException 😞

roelof12:01:47

or when I do this :

(defn my-update-in2 [m keyword function]
  (function (get-in m keyword)))

(my-update-in2 p :age add-ten)
error :
; Execution error (IllegalArgumentException) at chapter5/my-update-in2 (form-init4877412091744917773.clj:57).
; Don't know how to create ISeq from: clojure.lang.Keyword

andy.fingerhut15:01:03

get and get-in are different. You are giving get-in a single keyword, when it expects a sequence of key values.

andy.fingerhut15:01:39

That comment was for my-update-in2

andy.fingerhut15:01:02

Remember, one way to debug these things is to evaluate expressions step-by-step, and see which one goes wrong, or returns a value you do not expect.

andy.fingerhut15:01:58

For the call to (my-update-in p add-ten) giving an arityException, well, count the number of args in this definition, and how many you gave in your call: (defn my-update-in [m keyword number function]

andy.fingerhut15:01:52

In experimentation like this, it might be less confusing to leave earlier experimental functions that you consider working unchanged, and create new names for new experiments. I am not sure if that is part of the difficulty here for you or not, but thought I would suggest it.

andy.fingerhut15:01:40

Regarding your earlier question today: "I always thought that `& args` was taking in all the arguments that were left So why still a argument error". Because some other function besides my-update-in is being called with the wrong number of args. argument errors can happen for any function called with the wrong number of arguments, not only for ones that you define yourself.

andy.fingerhut15:01:34

Look for another function that is being called with the wrong number of arguments. Again, I would recommend trying to evaluate sub-expressions step by step, seeing what values they return, and continue step-by-step, and you should be able to get to the answer of which function call is throwing the exception because of wrong number of args.

andy.fingerhut15:01:57

If you never try to break down these things step by step, I don't know how else to help you learn how to discover what is going wrong.

roelof16:01:16

oke, I will then try to debug this :

(defn my-update-in2 [m keyword function]
  (function (get-in m keyword)))
where I see this error :
; Execution error (ClassCastException) at chapter5/add-ten (form-init4877412091744917773.clj:62).
; class clojure.lang.Keyword cannot be cast to class java.lang.Number (clojure.lang.Keyword is in unnamed module of loader 'app'; java.lang.Number is in module java.base of loader 'bootstrap')
clj꞉chapter5꞉> 

roelof16:01:39

my idea is that there is a argument missing in the get-in part

roelof16:01:44

yep, when I do (get-in p :name) I see the same error

roelof16:01:07

where p is (def p {:name "James" :age 26})

andy.fingerhut16:01:18

So, what do you know about the arguments that get-in should be given?

roelof16:01:31

but no idea was should be missing

andy.fingerhut16:01:47

What is the difference between get and get-in ?

roelof16:01:03

I do not have a key before the :name`

andy.fingerhut16:01:33

If it helps you remember, create working examples of both get and get-in that you put in some personal notes for each function, maybe plus a sentence or two explaining the difference between them.

roelof16:01:20

both are working

(defn my-update-in [m keyword number function]
  (function (get-in m  [(number keyword) keyword]) ))

(defn my-update-in2 [m keyword function]
  (function (get-in m keyword)))
so I now know if there is a number given I need get-in and otherwise I need get

andy.fingerhut16:01:34

In case it helps, my personal thinking on this to explain their difference would be like so: "get takes a map and a single key to be looked up, or a vector and a key (integer index) to be looked up, and returns the value associated with that key, or nil if the key is not found"

andy.fingerhut16:01:15

"get-in takes a sequence of keys to be looked up, and if that sequence is N elements long, it behaves like N repeated applications of the function get where each repeated application of get is applied to the collection returned by the previous call to get."

roelof16:01:14

so it looks I need a function with 2 things in it one for 4 arguments and one for 3

andy.fingerhut16:01:15

Thus (get m k) and (get-in m [k]) behave exactly the same as each other.

roelof16:01:01

BRB, wife needs the computer

andy.fingerhut16:01:23

In what way does the my-update-in2 function you show above "work"?

andy.fingerhut16:01:43

As in, can you give an example call where it does what you want?

roelof17:01:13

of course :

(def p {:name "James" :age 26})


(defn add-ten [number]
    (+ number 10))

(my-update-in2 p :age add-ten)

roelof17:01:47

gives : 36 chips the old one needs to be added

roelof17:01:42

the other one :

(def m {:1 {:value 0, :active false}, :2 {:value 0, :active false}})


(defn my-update-in [m keyword number function]
  (function (get-in m  [(number keyword) keyword]) ))

(defn my-update-in2 [m keyword function]
  (function (get-in m keyword)))

(update-in m [:1] assoc :value 1 :active true)
gives :
{:1 {:value 1, :active true}, :2 {:value 0, :active false}}

roelof17:01:49

which is right

roelof17:01:14

so for the second one back to the drawing board

andy.fingerhut17:01:07

Is your goal to write my-update-in so that it can take a sequence of keys, like get-in does? Or do you want to make it so it can take one key, or two keys, only?

roelof17:01:02

first one key, later maybe more keys

andy.fingerhut17:01:38

So yesterday you had a working my-update function that took only 1 key, and I thought worked, true?

roelof17:01:27

my goal is still to make it work with this two :

(def p {:name "James" :age 26})
(def m {:1 {:value 0, :active false}, :2 {:value 0, :active false}})

roelof17:01:47

yep, and also for the m one we have a working version

roelof17:01:09

for the p as far as I know we have not

roelof17:01:16

or I have deleted it

andy.fingerhut17:01:14

So sorry if you've already said it clearly and I've forgotten, but what do you mean when you say "my goal is still to make it work with this two ...". What do you want the behavior to be? Stating what you want the behavior to be with inputs and desired return values can help clarify this (at least for me).

andy.fingerhut17:01:38

One way to clarify this would be to say: "I want to write my own version of Clojure's built-in update-in function, that always returns the same values it does, given the same inputs."

roelof17:01:45

we have two working versions

(defn my-update-in [m keyword number function]
  (function (get-in m  [(number keyword) keyword]) ))

(defn my-update-in2 [m k f]
  (let [new_value (f (get m k))]
    (assoc m k new_value)))

andy.fingerhut17:01:13

but I'm not sure if that is what you are trying to do, or not.

roelof17:01:22

update-in works on this case : (def m {:1 {:value 0, :active false}, :2 {:value 0, :active false}})

roelof17:01:53

and update-in2 works in this case : (def p {:name "James" :age 26})

andy.fingerhut17:01:57

Do you mean Clojure's built-in function udpate-in works on that case, or do you mean one of your my-update-in functions works in that case.

andy.fingerhut17:01:11

Because if you use different names, I get confused which function you mean.

roelof17:01:19

the code we wrote seems to work

andy.fingerhut17:01:30

The code you wrote is not called update-in

roelof17:01:42

im talking about my written versions of update-in

andy.fingerhut17:01:08

So please use my-update-in to refer to my-update-in, because there is a Clojure built-in function named update-in

roelof17:01:48

sorry, I named it so because it otherwise make problems with the built in one

andy.fingerhut17:01:02

When you say my-update-in "works" in this case, you are only giving one of the parameter values, not all of them. It doesn't make any sense to me to only give one of the parameters of a function that takes 2 or 3 parameters, and say it works.

andy.fingerhut17:01:21

Its behavior depends upon all of the parameter values, not just one.

roelof17:01:30

sorry for the confusion

andy.fingerhut17:01:21

Please give complete calls to those functions that you consider working.

roelof17:01:05

I know have this :

(def m {:1 {:value 0, :active false}, :2 {:value 0, :active false}})


(defn my-update-in [m keyword number function]
  (function (get-in m  [(number keyword) keyword]) ))

(defn my-update-in2 [m k f]
  (let [new_value (f (get m k))]
    (assoc m k new_value)))


(my-update-in m [:1] assoc :value 1 :active true)

roelof17:01:29

and get a argument error . I have to give 4 arguments and I provide 7

andy.fingerhut17:01:22

So what function do you want to write? Please try to state that clearly, otherwise the goal keeps changing.

andy.fingerhut17:01:41

You say that these functions do what you want, then you say that they don't do what you want.

roelof17:01:15

yep, I see now that I use the built-in function not my own written one

roelof17:01:21

stupid mistake

roelof17:01:23

pfff, two days busy and not closer to the solution. Wonder if clojure is for me

andy.fingerhut17:01:15

I can't answer that for you. For solving a problem of writing a particular function, I may be able to help if I know what function you want to write 🙂

roelof17:01:36

for now I want to make my own written update-in working

andy.fingerhut17:01:10

And you want it to behave the same way that Clojure's built-in function update-in works?

roelof17:01:34

yep, that is I think the purpose of the challenge

andy.fingerhut17:01:48

ok, so that makes the goal of what function to write pretty clear, because we have an existing function that we can make calls to, see what the return value is, and know that is what this new function should return. (or we can just read the code of update-in to see how it works, of course, but maybe not yet for this challenge).

andy.fingerhut17:01:47

It is OK to write multiple functions along the way that only do part of what update-in does, of course, and there are multiple ways to break that down into steps.

roelof17:01:59

we can even peek how it written but that feels to me as cheating

andy.fingerhut17:01:48

One way would be to first write a function that doesn't take the & args part of the arguments that update-in does, only the function f as the last parameter, and see if we can make something that works like that part of update-in. Only if you get that working first, then try to add the & args part.

andy.fingerhut17:01:35

That is, try writing a function my-update-in that takes only the arguments [m ks f] and no others, and make it behave the same way as Clojure's built-in update-in when given those arguments only.

roelof17:01:53

We had that I think with this version

`(defn my-update-in2 [m k f]
  (let [new_value (f (get m k))]
    (assoc m k new_value)))
or do I misunderstood you now

roelof17:01:15

we wrote that yesterday

andy.fingerhut17:01:54

I am pretty sure that behaves like Clojure's built-in function update, but it does not behave the same way as Clojure's built-in function update-in

andy.fingerhut17:01:03

Here are some examples to demonstrate why I think so:

andy.fingerhut17:01:12

(defn my-update-in2 [m k f]
  (let [new_value (f (get m k))]
    (assoc m k new_value)))

(def m1 {:a 17 :b 18})

(update m1 :a inc)
;; {:a 18, :b 18}
(my-update-in2 m1 :a inc)
;; {:a 18, :b 18}

(update-in m1 [:a] inc)
;; {:a 18, :b 18}
(my-update-in2 m1 [:a] inc)
;; Execution error (NullPointerException) at user/my-update-in2 (REPL:63).
;; Cannot invoke "Object.getClass()" because "x" is null

andy.fingerhut17:01:54

The function that you have called my-update-in2 behaves the same as Clojure's built-in update for the example parameters shown above. It does not behavior the same as Clojure's built-in update-in for the example parameters shown above.

andy.fingerhut17:01:19

If I were you, I would rename my-update-in2 to my-update, since we know it doesn't behave like update-in

andy.fingerhut17:01:28

and at least in some cases, it does behave like update

roelof17:01:03

we did yesterday only check it on this : (def p {:name "James" :age 26})

andy.fingerhut17:01:23

The map I give is the same in all calls.

andy.fingerhut17:01:26

That is not the difference.

andy.fingerhut17:01:38

Do you see the difference in the parameters given to update vs. update-in?

roelof17:01:26

I think so we gave it a key and on update-in2 fails when its a vector of keys

andy.fingerhut17:01:07

As I wrote above, the difference between get and get-in is that get takes a single key but get-intakes a vector (or sequence) of keys.

andy.fingerhut17:01:41

That is one of the main differences between Clojure's built-in update and update-in, too. update-intakes a vector (or sequence) of keys, updatetakes only a single key

roelof17:01:09

hmm, I have to think how to solve that one

andy.fingerhut17:01:22

Your function my-update-in2 works for a single key. It does not work for a vector (or sequence) of keys.

roelof17:01:22

idea is to use apply somewhere

roelof17:01:45

or maybe recursion

andy.fingerhut17:01:49

I can see how apply can help if you are trying to implement the & args part of the problem.

roelof17:01:06

yep, but that is not the problem now

andy.fingerhut17:01:10

But I do not see how apply can help if you are trying to implement the part of handling a vector of keys.

andy.fingerhut17:01:19

Recursion or a loop sounds more promising to me.

roelof17:01:39

BRB dinnner and I have to take some time to think how this could work

roelof17:01:22

because yesterday you said destruction is almost never needed

roelof17:01:31

and now I need to get the first key

andy.fingerhut17:01:19

destructuring is never necessary to write a correct Clojure program. It can be a nice convenience in some cases, but you can always write an equivalent Clojure program that does not use destructuring, using functions like first and rest or nth

andy.fingerhut17:01:20

(first [:a :b :c])
;; :a
(rest [:a :b :c])
;; (:b :c)

andy.fingerhut17:01:37

I would hope that those functions were described early in the book you are using

andy.fingerhut17:01:27

They work on any vectors or sequences, no matter whether they contain numbers, keywords, other collections, etc.

roelof18:01:57

yep, there are also explained

roelof18:01:16

dinner ready and time to think about this one

roelof18:01:27

am I right I need a anymous function here

andy.fingerhut18:01:27

I would also put anonymous functions into the category of "often convenient, but not absolutely required", like destructuring.

andy.fingerhut18:01:49

I do often use them when they are short and thus convenient to use, so I don't have to make up a name for some tiny one-use function.

andy.fingerhut18:01:42

The only reason I say these things is because you ask if you "need" an anonymous function. When you use that word, it makes me think "there is no other way to write the function I want, other than to use an anonymous function", and that is never true. You can always write a small function with a name and call that instead.

andy.fingerhut18:01:19

Note: I am not saying that destructuring or anonymous functions are bad. I am only saying they are not needed.

andy.fingerhut18:01:21

There are some Clojure language constructs that are pretty much needed, or else you cannot write useful programs. E.g. being able to define functions, and call them, is needed for writing practical programs.

roelof18:01:42

and im now lost in parentheses here :

(defn my-update-in [m [ks] f]
  (let [up (fn up [m ks f ]
             (let [k (first ks)
                   rest (rest ks)
                   new_value (f (get m k))
                   (assoc m k new_value)]))]))

roelof18:01:51

why makes clojuer things so difficult

andy.fingerhut18:01:55

Does your editor automatically show you the matching parenthesis or bracket for the one that your cursor is next to?

roelof18:01:33

got it after some fiddeling

roelof18:01:37

(defn my-update-in [m [ks] f]
  (let [up (fn up [m ks f]
             (let [k (first ks)
                   rest (rest ks)
                   new_value (f (get m k))]
               (assoc m k new_value)))]))

andy.fingerhut18:01:03

Some fiddling is pretty normal in such situations. Practice makes the fiddling easier.

roelof18:01:03

now time to think where the recursive call wiill be if this a good code

andy.fingerhut18:01:23

Why did you put brackets around [ks] in the arguments to my-update-in?

roelof18:01:02

I thought that was needed

andy.fingerhut18:01:16

Needed for what?

roelof18:01:46

needed to make it work and tell a user ks needs to be a vector

andy.fingerhut18:01:06

There are no type declarations on Clojure parameters to tell you they need to be a vector, or a map, or any other kind of thing, the way some languages have.

andy.fingerhut18:01:02

What Clojure actually does when you write a parameter like that is destructuring. It will expect that second argument to be a vector or sequence, take the first element of that vector or sequence, bind it to the name ks, ignore the rest of that vector or sequence, and execute the body of the function.

andy.fingerhut18:01:45

If you write ks instead of [ks] there, it will take whatever the second parameter is, a vector, a sequence, a number, whatever, and bind it to ks, and discard none of that value.

roelof18:01:46

oke, this seems to be complimng

(defn my-update-in [m ks f]
  (let [up (fn up [m ks f]
             (let [k (first ks)
                   rest (rest ks)
                   new_value (f (get m k))]
               (assoc m k new_value)))]
    (up m ks f)))
I only have to figure out where the recursive call needs to be 😞

andy.fingerhut18:01:46

Looks like a good start to me.

roelof18:01:59

yep me too

andy.fingerhut18:01:01

Where you now have (assoc m k new_value), you need to have (at least) two cases.

andy.fingerhut18:01:06

Warning: rest (rest ks) using the name rest there means that in the body of that let, it will be impossible to call the built-in function rest because you have re-bound the name rest to a new value.

andy.fingerhut18:01:36

If you never do that, then no problem, but if you do try to call the built-in function rest inside of that let, it can be very confusing what is going wrong.

roelof18:01:48

oops, what is then a better name for all the other entries in ks

roelof18:01:24

mayeb again ks ?

andy.fingerhut18:01:27

If the inner let is only a small amount of code, as I expect it will be, a single letter like r is probably ok.

andy.fingerhut18:01:48

ks looks fine, too, as long as you know it will be all but the first element of the original ks inside of the let body.

andy.fingerhut18:01:00

I will be AFK for a few minutes at least.

roelof18:01:13

oke, no problem

roelof18:01:19

I have called it r

roelof18:01:30

still thinking where I have to put the call to up

roelof18:01:08

Still thinking before the (get m k) part but then the name is not right anymore

roelof18:01:34

so something like this :

(defn my-update-in [m ks f]
  (let [up (fn up [m ks f]
             (let [k (first ks)
                   r (rest ks)]
               (assoc m k (up (get m k) r f))))]
    (up m ks f)

roelof18:01:32

and I do not work on both cases one still give a error that I have given 7 arguments where 4 are given

roelof18:01:53

and the other one :

(my-update-in p :age inc)

roelof18:01:33

give this :

; Execution error (IllegalArgumentException) at chapter5/my-update-in$up (form-init12940824632450433692.clj:55).
; Don't know how to create ISeq from: clojure.lang.Keyword

andy.fingerhut18:01:11

Currently you are working on implementing the case without & args

andy.fingerhut18:01:23

If you are doing that, then stick to it, and never try to call it with 7 args

andy.fingerhut18:01:39

Pick a single function to call it with, and test it with that, with no extra args.

roelof18:01:07

oke, I did with the p one and it also fails

roelof19:01:10

or is this not a single one : (my-update-in p :age inc)

andy.fingerhut19:01:12

Earlier I said: "Where you now have `(assoc m k new_value)`, you need to have (at least) two cases." and you answered "Do I?"

andy.fingerhut19:01:17

Have you written recursive functions before?

roelof19:01:28

yep, in haskell a lot

andy.fingerhut19:01:45

Have you tried writing a recursive function that did not have two or more cases in its body?

roelof19:01:02

there you had to write a base case when the recursion has to quit and one for the rest

roelof19:01:17

so no, that will cause a infinite loop

roelof19:01:45

the base case is here when ks is empty I think

andy.fingerhut19:01:51

Same here. You need a base case that does not make a recursive call, and you need a recursive call that does part of the work, but also calls itself recurively on a smaller problem.

andy.fingerhut19:01:36

The assoc you have in the inner let seems like it might be reasonable for a base case.

roelof19:01:40

hmm, so here when r is empty , have to think what can be done then

andy.fingerhut19:01:21

When you have only one key left k which is (first ks), you want to do that assoc call I believe.

andy.fingerhut19:01:11

The base case is "do the same thing update would do given that one key"

roelof19:01:20

oke, that way

roelof19:01:39

oke, we need a if , I believe

andy.fingerhut19:01:12

An if would be one way. cond is another, but if is good enough for here.

roelof19:01:31

maybe if (== (count r) 1) ???

andy.fingerhut19:01:54

Do you want the base case to be when r has 1 key left in it?

roelof19:01:25

if (== (count first) 1)`

andy.fingerhut19:01:29

Earlier you suggested "the base case is here when ks is empty I think"

roelof19:01:54

if (empty? r)

andy.fingerhut19:01:51

That seems to me to match the condition "`k` is the last key of the original input sequence, and there are no more"

andy.fingerhut19:01:24

Looks reasonable to me. Even as written, that should hopefully work if you give a sequence of one key as ks argument.

roelof19:01:34

am I here right

(defn my-update-in [m ks f]
  (let [up (fn up [m ks f]
             (let [k (first ks)
                   r (rest ks)]
               (if (empty? r)
                 (assoc m k (up (get m k) r f))
                 (assoc m k (apply (get m k) r f))
                 )))]
    (up m ks f)))

andy.fingerhut19:01:45

I was just about to suggest leaving out & args again 🙂

andy.fingerhut19:01:01

The else case of your if is not recursive.

andy.fingerhut19:01:27

The then case is recursive, when it should be the base case.

roelof19:01:10

moment, I have to switch them ?

andy.fingerhut19:01:10

You tell me. In what condition should you make a recursive call, and which one should you NOT make a recursive call?

roelof19:01:18

I have now this :

(defn my-update-in [m ks f]
  (let [up (fn up [m ks f]
             (let [k (first ks)
                   r (rest ks)]
               (if (empty? r)
                 (assoc m k (up (get m k) r f))
                 (assoc m k (apply (get m k) f))
                 )))]
    (up m ks f)))
         


(my-update-in p :age inc)

roelof19:01:33

; Execution error (IllegalArgumentException) at chapter5/my-update-in$up (form-init12940824632450433692.clj:55).
; Don't know how to create ISeq from: clojure.lang.Keyword
clj꞉chapter5꞉> 

andy.fingerhut19:01:51

You have the recursive call when (empty? r) is true. I don't think that is correct. There is no reason to keep going with more keys, is there?

roelof19:01:58

when I do [my-update-in p [:age] inc) I get a stack overflow

roelof19:01:16

here still the same IlligalArgumentException

roelof19:01:22

(defn my-update-in [m ks f]
  (let [up (fn up [m ks f]
             (let [k (first ks)
                   r (rest ks)]
               (if (empty? r)
                (assoc m k (apply f (get m k)))
                (assoc m k (up (get m k) r f))
                 )))]
    (up m ks f)))
         


(my-update-in p :age inc)

roelof19:01:12

; Execution error (IllegalArgumentException) at chapter5/my-update-in$up (form-init12940824632450433692.clj:57).
; Don't know how to create ISeq from: clojure.lang.Keyword

roelof19:01:29

got the feelimg im very close but I oversee something

andy.fingerhut19:01:31

I don't know your current definition of p

roelof19:01:10

(def p {:name "James" :age 26})

roelof19:01:59

be back in 10 - 15 min. Going for a walk to get a fresh head

andy.fingerhut19:01:35

What should be the second argument to my-update-in?

roelof19:01:58

a vector ?

roelof19:01:33

but also then I see that annoying error

andy.fingerhut19:01:44

a vector or a sequence is what update-in is supposed to take, not a single key.

roelof19:01:52

(my-update-in p [:age] inc)

andy.fingerhut19:01:53

You are trying to write update-in, so you should test it like update-in

roelof19:01:31

I got these examples from the page of update-in

andy.fingerhut19:01:53

Your current goal, if I understand it, is to write a function that works like update-in. update-in would give an error if you gave it a single key, not a vector or sequence of keys as the second parameter. So don't test your function in a way that update-in cannot work.

andy.fingerhut19:01:59

Which example page of update-in?

andy.fingerhut19:01:29

Show me an example page of update-in calling it like this: (my-update-in p :age inc) and I will show you a page with a mistake.

roelof19:01:17

this page(https://clojuredocs.org/clojure.core/update-in) but also when I change it to (my-update-in p [:age] inc)

roelof19:01:35

I see this error :

; Execution error (IllegalArgumentException) at chapter5/my-update-in$up (form-init12940824632450433692.clj:57).
; Don't know how to create ISeq from: clojure.lang.Keyword

andy.fingerhut19:01:52

Every example on that page has a vector of keys that I can see.

roelof19:01:23

oke, but why if I do that I still see the argumentError

roelof19:01:42

maybe because we do not use &args

andy.fingerhut19:01:45

I don't think & args can cause that error.

andy.fingerhut19:01:57

Playing a board game at the moment, so dividing my attention and may be slow here.

roelof19:01:15

we can discuss this later if you have more time

roelof19:01:43

but I see that it chrashing on the (empty? .._) line

roelof19:01:00

and I do not make any difference if I use r or k here

andy.fingerhut19:01:36

I don't see why it would crash on that line: (empty? (rest [:a]))

andy.fingerhut19:01:49

returns true as I would expect. As long as you put r there, not k

roelof19:01:34

hmm, here it chrashes

(defn my-update-in [m ks f]
  (let [up (fn up [m ks f]
             (let [k (first ks)
                   r (rest ks)]
               (if (empty? r)
                 (assoc m k (apply f (get m k)))
                 (assoc m k (up (get m k) r f)))))]
    (up m ks f)))

andy.fingerhut19:01:24

Take out the apply. We are not doing the & args version yet.

andy.fingerhut19:01:35

This function call gives the same error, and is the reason you are getting the exception: (apply inc (get p :age))

roelof20:01:24

yes, it working 🎉

roelof20:01:44

so now to make ti work with & args ?

andy.fingerhut20:01:20

Before that, have you tested it with a sequence of multiple keys?

andy.fingerhut20:01:12

I would recommend testing to see if that case works before & args

roelof20:01:42

it seems to work

(defn my-update-in [m ks f ]
  (let [up (fn up [m ks f ]
             (let [k (first ks)
                   r (rest ks)]
               (if (empty? r)
                 (assoc m k (f (get m k )))
                 (assoc m k (up (get m k) r f )))))]
    (up m ks f )))

roelof20:01:18

(my-update-in p [:age] inc)

roelof20:01:40

answer : {:name "James", :age 27}

andy.fingerhut20:01:13

That does not test it with a sequence of 2 or more keys.

andy.fingerhut20:01:27

You are still testing the case with a sequence of one key, so the recursive case is not being exercised at all.

roelof20:01:33

(def m {:1 {:value 0, :active false}, :2 {:value 0, :active false}})

(update-in m [:1] assoc :value 1 :active true)

answer : {:1 {:value 1, :active true}, :2 {:value 0, :active false}}

roelof20:01:52

which is also well

roelof20:01:21

satisfied ?

roelof20:01:51

2 days struggeling and both cases solved 🎉

roelof20:01:37

if you do not have feedback then I would thank you for a lot of patience with me

roelof20:01:49

ennjoy the weekend and your family

andy.fingerhut20:01:45

no further feedback 🙂. May you also enjoy your weekend.

andy.fingerhut20:01:18

The last case you showed still has a vector of only 1 key, so not testing recursion, though 🙂

roelof20:01:17

hmm, then I have to look for a example for that case

roelof20:01:34

maybe this one :

(def m {:1 {:value 0, :active false}, :2 {:value 0, :active false}, :3 {:value 0, :active false}})

roelof20:01:49

(update-in m [:1 :3] assoc :value 1 :active true)

roelof20:01:05

does not give the right answer 😞

roelof20:01:27

also not (update-in m [:[1 ][:3]] assoc :value 1 :active true)

roelof21:01:35

or do you more like this :

(def player1 {:name "Player 1" :attribs {:str 10 :int 11 :wis 9}})

(update-in player1 [:attribs :str] inc)
;; {:name "Player 1", :attribs {:str 11, :int 11, :wis 9}}

roelof21:01:58

where a nested one is done

roelof21:01:22

that one works 🎉

roelof21:01:25

time to sleep here. Again many mny thanks for all the patience with me

roelof21:01:45

a lot to learn how to approach "complex" problems like this

andy.fingerhut22:01:23

The example with player1 looks like a good test that actually exercises the recursive code.

roelof22:01:39

thanks, it worked also

roelof22:01:00

I get the same answer as the page said

roelof22:01:08

then tomorrow or Monday this page on the menu : https://www.braveclojure.com/read-and-eval/#Summary

noisesmith20:01:39

that last call needs to be outside the [] binding block

noisesmith20:01:13

the syntax of let is (let [binding value ... ...] body) - it's usually a logic error not to provide a body, and it's a compilation error to have a binding without a value (or visa versa)

roelof20:01:51

you mean like this :

(defn my-update-in [m ks f & args]
  (let [[k & ks] ks]
  (assoc m k (apply f (get m ks) args))))



(my-update-in p [:1] assoc :name "James" :age 35)

roelof20:01:16

that does this :

{:name "James", :age 26, :1 {:name "James", :age 35}}

roelof20:01:24

and not updating the value

caumond21:01:24

Im not getting what you want to achieve there. Update-in work with nested maps. And ks is supposed to be a list a key of your map. What do you want to specify with :1 ?