This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-01-08
Channels
- # asami (7)
- # aws (2)
- # babashka (21)
- # beginners (602)
- # calva (8)
- # cider (11)
- # clj-kondo (10)
- # cljfx (1)
- # clojure (177)
- # clojure-europe (43)
- # clojure-nl (3)
- # clojure-taiwan (2)
- # clojure-uk (45)
- # clojurescript (31)
- # depstar (8)
- # figwheel-main (2)
- # fulcro (12)
- # hugsql (2)
- # java (1)
- # jobs (2)
- # meander (1)
- # missionary (1)
- # off-topic (67)
- # other-languages (1)
- # pathom (212)
- # polylith (4)
- # rdf (1)
- # re-frame (10)
- # reagent (12)
- # reitit (28)
- # reveal (3)
- # shadow-cljs (21)
- # spacemacs (7)
- # sql (5)
- # tools-deps (12)
- # vim (1)
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?
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-kF25fWTO8It comes from here: https://www.infoq.com/presentations/datomic-functional-database/
Transcript is here if you prefer reading: https://github.com/matthiasn/talk-transcripts/blob/master/Hickey_Rich/FunctionalDatabase.md
@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
doesnt figwheel or whatever spin up a little development server?
i dont want to have to compile every time i make a change etc
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
im a bit confused here, the doc linked doesnt say anything about it
do i need to change the port of the ring server to match as well?
I am so confused, am i running fig:build or what?
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
there is no html file produced though?
oof this is not working at all, i made an html and loaded the js there but
@U65FN6WL9 it looks like how the js is trying to load files is bad...it's using escape charatcers in strings?
https://github.com/jstaffans/django-cljs-loader i got it working using this
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
@zackteo Change http://
to https://
?
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.
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
You might also want to look at https://www.4clojure.com/ which has problems designed to help you learn the language.
@seancorfield thought about that. Guess I could give that a try. Was thinking if the link was meant to be inaccessible
@titanroark a quick thing to try would be to replace recursive calls to getFactor
with recur
: https://clojuredocs.org/clojure.core/recur
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?
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
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
It's possible the tests are non-deterministic or something in the tests is not compatible with the GitHub Actions environment...
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
Computers are deterministic by definition 🙂
My own theory is that the github secrets might be having issues when I am the one pushing the commit
Tho I am not sure if that theory checks out with the github actions history :thinking_face:
In which it should be intermittent and kicking off the process again should succeed...
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
I do know that this exists tho https://github.blog/2020-08-03-github-actions-improvements-for-fork-and-pull-request-workflows/
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 {})
You have a typo, it's called pedestal, not pedestral. (Remove the r
)
FYI there's #pedestal
See that I have to learn a lot more about clojure to make my own sites and really understand how things really work
You don't need interceptors to make a site
Start simple
Pedestal bundles a few things together
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
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.
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"]}
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.
im following this tutorial now : http://pedestal.io/guides/hello-world
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?You probably need to replace (concat)
by (apply concat)
in your method.
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
So what exactly are the differences between `(filter #(<= 5 %) coll)` and `(filter (partial <= 5) coll)` ? Is that second example using a "transducer?"
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.
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.
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
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.
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
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
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
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?
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
The only difference is that #(<= 5 %) creates the predicate function at read time, and (partial <= 5) creates the predicate function at run-time
The docstring you're reading is referring to the arity (number of arguments) of filter
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
The doc says return a transducer when no collection is provided, and in both cases you provide [pred coll]
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 ?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"
You could get a correct solution by creating a new map in N steps.
It probably just would not be among the fastest solutions.
Your original statement said "and put the whole m again together".
There are multiple things you might have meant when you said that, and I don't know what you were thinking.
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
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"
Lets say we have this map : (def users [{:name "James" :age 26} {:name "John" :age 43}])
That is a vector containing two maps.
That is one map 🙂
sounds good
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.
I wouldn't say "have to", but if it helps you write a solution, then let
is perfectly reasonable.
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.
Sure, what you do there is doing your first two steps in one line, which is also good.
I will. Don't worry, I'm following so far 🙂
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
user=> (assoc {:a 1 :b 2 :c 3} :b -2)
{:a 1, :b -2, :c 3}
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.
That is what assoc
is intended to do.
Has the book you are following described assoc
before this exercise, I hope?
so I can do just this :
(def my-update-in [m k f]
(let [new_value (f (get m k)]
(assoc m new_value)
)
Your call to assoc
doesn't have the correct number of parameters.
that looks better
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.
What editor are you using?
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.
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.
Trying to write a Lisp-like language without such editor support is going to slow you down tremendously.
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.
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)
Is there anything else in the same file with that function? Looking at the function definition, nothing obviously wrong jumps out at me.
What is the 3rd parameter of my-update-in supposed to be?
What does my-update-in
do with the 3rd parameter?
The only place the 3rd parameter is used in my-update-in
is this expression (f (get m k))
So it is trying to call f
as a function.
That is what the exercise asked you to write, I think, yes?
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.
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.
i.e. you are calling my-update-in
incorrectly. You should try calling it where the 3rd parameter is some function.
If you want a quick try, replace 35 with inc
, which is a function.
Does it make sense why you got the error when you passed in 35?
Do you know how to write a Clojure function that takes a number and adds 10 to it?
Good. If you define that function, you could pass add-ten
as the 3rd parameter to my-update-in
You could write a different function than my-update-in
that takes a new value for the 3rd parameter instead of a function.
If you did, I would call it my-assoc
🙂
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.
Yes, I understand that.
YOu asked if you could change my-update-in
to behave differently. You can do that if you want.
I am only saying that such a changed my-update-in
does not solve the challenge.
as the 3rd parameter, yes.
And I encourage you to experiment to your heart's content 🙂
no problem
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.
And Clojure doesn't restrict you to writing code that passes Haskell's type checker.
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.
but both are perfectly fine for calling out to libraries written in other languages for doing that kind of thing.
Anyone using cider with Doom emacs? Trying to figure out the most appropriate way to evaluate the form under the cursor
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)
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 🙂
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
@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?
it really doesnt look like cljs works well with a django backend 😛
i am really struggling to get it working
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?
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
there's just like 2 million directories specified in this thing
looks like the configuration doesnt like relative paths either
(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"]}})
the main issue im having i think is the dev-main.js can't find any of the deps it needs for some reason
if i look at dev-main.js i have lines like this:
document.write('<script src="..\..\paladeenz\website\static\livematch\public\cljs-out\dev/cljs_deps.js"></script>');
notice the escape characters..
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
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
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
so i have a figwheel-main.edn file which says
:target-dir "../../paladeenz/website/static/livematch"
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
https://figwheel.org/config-options#target-dir idk if there's anything in here to fix that
im going to try adding in output-to and output-dir
NVM it didnt like those keywords haha
it's like this stuff is designed to be as cryptic as possible
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)
ok ill ask there, ty
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
you specify f before args, then :age before inc
also (get m ks)
is weird - it's as if you expect the whole seq of keys to be one key
neither :age nor inc are collections, so neither works as a last arg to apply
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
When first learning, I would recommend avoiding the use of destructuring, until and unless you get a version of a function working without it.
Destructuring can be useful, but it is never necessary.
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}}
It is almost always possible to take a Clojure function and evaluate it step by step in a REPL session.
Do you know which sub-expression of your my-update-in
above must be evaluated first?
yes, good.
And what parameters it should be passed in your example call, and what value it will return?
(If you are not sure, that is where the REPL can be handy)
It isn't clear to me what the value of p
is in your call above
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}])
So what does (get m ks)
return?
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?
Now, try eval'ing this in a REPL: (get p [:0]
and see what it returns.
Does it make sense why it returns nil?
correct.
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.
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.
I was hoping to get first a record from a map
so in this example {:name "James" :age 26}
So in Clojure terms, that would be getting the first element from a vector or sequence.
The value of users
is not a map, but a vector of two elements (each of those elements is a map)
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.
So p
is a single Clojure map, right, meaning this one: (def p {:name "James" :age 26})
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.
That was working.
So now you are saying you want a function that can take a vector of maps, and do something with that.
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.
i.e. try writing a function that only works if the first argument is a vector of maps, without trying to serve both purposes.
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}}
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.
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?
If you only want it to work for 1, then why put it in a vector?
I guess perhaps from the name update-in
, you might be thinking along the lines of how get-in
works in Clojure?
Are you familiar with get-in
?
yep, that one is explained in this chapter as Whereas the function
get-in` lets you look up values in nested maps, `
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.
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 falseHow many keys to you give to get-in
/
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])
1 sequence of keys, not multiple separate sequences of keys.
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
In this call: (get-in m [:1][:active])
there is a key :1
, so the 3rd parameter is not used.
yep. as it is documented to do.
(once the documentation makes sense, that is 🙂
experimentation at the REPL with different variations of an idea are definitely good ways to test your understanding.
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.
and I will experiment if I can make it work on a different keyword, so on value
and active
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.
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)
It usually takes longer to ask questions that let the learner think about a solution :-)
My hope is that you will learn questions to ask yourself, and habits of thought, that let you more often find the answers yourself.
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"))
Maybe a idea to try now t change the get-in
into update-in
? after some sleep naturlly
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 errorand 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(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)
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
get
and get-in
are different. You are giving get-in
a single keyword, when it expects a sequence of key values.
That comment was for my-update-in2
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.
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]
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.
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.
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.
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.
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꞉>
So, what do you know about the arguments that get-in
should be given?
What is the difference between get
and get-in
?
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.
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 getIn 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"
"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
."
Thus (get m k)
and (get-in m [k])
behave exactly the same as each other.
In what way does the my-update-in2
function you show above "work"?
As in, can you give an example call where it does what you want?
of course :
(def p {:name "James" :age 26})
(defn add-ten [number]
(+ number 10))
(my-update-in2 p :age add-ten)
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}}
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?
So yesterday you had a working my-update
function that took only 1 key, and I thought worked, true?
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}})
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).
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."
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)))
but I'm not sure if that is what you are trying to do, or not.
update-in works on this case : (def m {:1 {:value 0, :active false}, :2 {:value 0, :active false}})
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.
Because if you use different names, I get confused which function you mean.
The code you wrote is not called update-in
So please use my-update-in to refer to my-update-in, because there is a Clojure built-in function named update-in
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.
Its behavior depends upon all of the parameter values, not just one.
Please give complete calls to those functions that you consider working.
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)
So what function do you want to write? Please try to state that clearly, otherwise the goal keeps changing.
You say that these functions do what you want, then you say that they don't do what you want.
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 🙂
And you want it to behave the same way that Clojure's built-in function update-in
works?
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).
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.
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.
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.
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 nowI 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
Here are some examples to demonstrate why I think so:
(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
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.
If I were you, I would rename my-update-in2
to my-update
, since we know it doesn't behave like update-in
and at least in some cases, it does behave like update
The map I give is the same in all calls.
That is not the difference.
Do you see the difference in the parameters given to update
vs. update-in
?
As I wrote above, the difference between get
and get-in
is that get
takes a single key but get-in
takes a vector (or sequence) of keys.
That is one of the main differences between Clojure's built-in update
and update-in
, too. update-in
takes a vector (or sequence) of keys, update
takes only a single key
Your function my-update-in2
works for a single key. It does not work for a vector (or sequence) of keys.
I can see how apply
can help if you are trying to implement the & args
part of the problem.
But I do not see how apply
can help if you are trying to implement the part of handling a vector of keys.
Recursion or a loop sounds more promising to me.
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
(first [:a :b :c])
;; :a
(rest [:a :b :c])
;; (:b :c)
I would hope that those functions were described early in the book you are using
They work on any vectors or sequences, no matter whether they contain numbers, keywords, other collections, etc.
I would also put anonymous functions into the category of "often convenient, but not absolutely required", like destructuring.
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.
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.
Note: I am not saying that destructuring or anonymous functions are bad. I am only saying they are not needed.
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.
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)]))]))
Does your editor automatically show you the matching parenthesis or bracket for the one that your cursor is next to?
(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)))]))
Some fiddling is pretty normal in such situations. Practice makes the fiddling easier.
Why did you put brackets around [ks]
in the arguments to my-update-in
?
Needed for what?
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.
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.
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.
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 😞Looks like a good start to me.
Where you now have (assoc m k new_value)
, you need to have (at least) two cases.
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.
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.
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.
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.
I will be AFK for a few minutes at least.
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)
and I do not work on both cases one still give a error that I have given 7 arguments where 4 are given
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
Currently you are working on implementing the case without & args
If you are doing that, then stick to it, and never try to call it with 7 args
Pick a single function to call it with, and test it with that, with no extra args.
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?"
Have you written recursive functions before?
Have you tried writing a recursive function that did not have two or more cases in its body?
there you had to write a base case when the recursion has to quit and one for the rest
right.
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.
The assoc
you have in the inner let
seems like it might be reasonable for a base case.
When you have only one key left k
which is (first ks)
, you want to do that assoc
call I believe.
The base case is "do the same thing update
would do given that one key"
An if
would be one way. cond
is another, but if
is good enough for here.
Do you want the base case to be when r
has 1 key left in it?
Earlier you suggested "the base case is here when ks is empty I think"
That seems to me to match the condition "`k` is the last key of the original input sequence, and there are no more"
Looks reasonable to me. Even as written, that should hopefully work if you give a sequence of one key as ks
argument.
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)))
I was just about to suggest leaving out & args
again 🙂
The else case of your if
is not recursive.
The then case is recursive, when it should be the base case.
You tell me. In what condition should you make a recursive call, and which one should you NOT make a recursive call?
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)
; 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꞉>
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?
(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)
; Execution error (IllegalArgumentException) at chapter5/my-update-in$up (form-init12940824632450433692.clj:57).
; Don't know how to create ISeq from: clojure.lang.Keyword
I don't know your current definition of p
What should be the second argument to my-update-in
?
a vector or a sequence is what update-in
is supposed to take, not a single key.
You are trying to write update-in
, so you should test it like update-in
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.
Which example page of update-in
?
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.
this page(https://clojuredocs.org/clojure.core/update-in) but also when I change it to (my-update-in p [:age] inc)
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
Every example on that page has a vector of keys that I can see.
I don't think & args
can cause that error.
Playing a board game at the moment, so dividing my attention and may be slow here.
I don't see why it would crash on that line: (empty? (rest [:a]))
returns true
as I would expect. As long as you put r
there, not k
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)))
Take out the apply
. We are not doing the & args
version yet.
This function call gives the same error, and is the reason you are getting the exception: (apply inc (get p :age))
Before that, have you tested it with a sequence of multiple keys?
I would recommend testing to see if that case works before & args
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 )))
That does not test it with a sequence of 2 or more keys.
You are still testing the case with a sequence of one key, so the recursive case is not being exercised at all.
(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}}
no further feedback 🙂. May you also enjoy your weekend.
The last case you showed still has a vector of only 1 key, so not testing recursion, though 🙂
or make one up
maybe this one :
(def m {:1 {:value 0, :active false}, :2 {:value 0, :active false}, :3 {:value 0, :active false}})
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}}
The example with player1
looks like a good test that actually exercises the recursive code.
then tomorrow or Monday this page on the menu : https://www.braveclojure.com/read-and-eval/#Summary
that last call needs to be outside the [] binding block
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)