This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-09-21
Channels
- # admin-announcements (1)
- # announcements (6)
- # babashka (8)
- # beginners (134)
- # calva (18)
- # chlorine-clover (1)
- # cider (6)
- # circleci (6)
- # clj-commons (111)
- # cljsrn (13)
- # clojure (95)
- # clojure-australia (2)
- # clojure-europe (15)
- # clojure-nl (1)
- # clojure-spec (52)
- # clojure-uk (17)
- # clojurescript (4)
- # datavis (9)
- # datomic (8)
- # docker (2)
- # emacs (15)
- # events (7)
- # fulcro (6)
- # graphql (1)
- # gratitude (1)
- # introduce-yourself (2)
- # kaocha (8)
- # meander (87)
- # minecraft (2)
- # music (2)
- # off-topic (20)
- # portal (119)
- # releases (1)
- # reveal (55)
- # shadow-cljs (34)
- # sql (36)
- # tools-deps (9)
- # vim (8)
- # xtdb (39)
Has anyone worked through the exercises at the end of the book in Web Development with Clojure Third Edition by Dmitri Sotnikov and Scot Brown? I'm trying to work through them and would love to be able to compare my code to something when I'm having trouble figuring out what I'm doing wrong.
There's a #code-reviews channel if you're specifically looking for feedback on your code -- but if it's just failing and you need help, maybe #beginners is a good place to ask, and paste your code into a thread on your initial post.
I have the book but haven't worked through all the examples...
Sorry if I should have posted this in the beginners channel
Does anyone know why the second line fails here?
(defprotocol IWork (foo [this {:keys [a b c] :as m}]))
(defprotocol IFail (bar [this {:some-ns/keys [a b c] :as m}]))
(defprotocol IAlsoWork (bar [this {:some-ns/keys [a b c]}]))
i get this error when i eval the IFail definition:
#:clojure.error{:phase :macro-syntax-check,
:line 190,
:column 1,...
:symbol clojure.core/fn}
Call to clojure.core/fn did not conform to spec.
#:clojure.spec.alpha{:problems
({:path [:fn-tail :arity-1 :params],
:pred clojure.core/vector?,
:val
([gf__this__85111
gf__{:some-ns/keys [a b c], :as m}__85112]
(.
gf__this__85111
(bar
gf__{:some-ns/keys [a b c], :as m}__85112))),
:via
[:clojure.core.specs.alpha/params+body
:clojure.core.specs.alpha/param-list
:clojure.core.specs.alpha/param-list],
:in [0]}
{:path [:fn-tail :arity-n :params],
:reason "Extra input",
:pred
(clojure.spec.alpha/cat
:params
(clojure.spec.alpha/*
:clojure.core.specs.alpha/binding-form)
:var-params
(clojure.spec.alpha/?
(clojure.spec.alpha/cat
:ampersand
#{'&}
:var-form
:clojure.core.specs.alpha/binding-form))),
:val
(gf__{:some-ns/keys [a b c], :as m}__85112),
:via
[:clojure.core.specs.alpha/params+body
:clojure.core.specs.alpha/params+body
:clojure.core.specs.alpha/params+body
:clojure.core.specs.alpha/param-list
:clojure.core.specs.alpha/param-list],
:in [0 0 1]}),
:spec
#object[clojure.spec.alpha$regex_spec_impl$reify__2509 0x26a99cc4 "clojure.spec.alpha$regex_spec_impl$reify__2509@26a99cc4"],
:value
(([gf__this__85111
gf__{:some-ns/keys [a b c], :as m}__85112]
(.
gf__this__85111
(bar
gf__{:some-ns/keys [a b c], :as m}__85112)))),
:args
(([gf__this__85111
gf__{:some-ns/keys [a b c], :as m}__85112]
(.
gf__this__85111
(bar
gf__{:some-ns/keys [a b c], :as m}__85112))))}
The strange thing is that if I macroexpand the IFail example manually using M-x cider-macroexpand-1
and eval the result, it does not throw any errors
i'm guessing on off the specs used to check macro expansion does not support both namespaced key destructuring ( :some-ns/keys []
) in combination with :as
defprotocol takes just a signature of the methods. You should not add destructuring there
You cannot.
Notice gf__{:some-ns/keys [a b c], :as m}__85112
in your macroexpand output.
protocol is a high level abstraction. real documentation comes from implementation and it can be different for different implementations
for some specific cases you can implement a macro that will wrap around defprotocol
my point is that defprotocol works with sig like [this {:keys [a b c] :as d}}
and [this {:some-ns/keys [a b c]}]
to why not [this {:some-ns/keys [a b c] :as d}}
that is just coincidence
and also you can use http://ask.clojure.org to file your desired behaviour
that is just coincidence
because after making a string from destructuring form there are no commas
for example this will njot work
(defprotocol IFail (bar [this {:some-ns/keys [a b c] :another-ns/keys [d e]}]))
no it is exactly because of commas
Clojure reader ignores commas. Can you elaborate why they are important in this case?
{:some-ns/keys [a b c] :another-ns/keys [d e]}
this form treated as a symbol in the context of gensymit still seems inconsistent to me, for example
(defprotocol IWork (foo [this {:keys [a b c] :strs [e f g] :syms [h i j] :as m}]))
does work
wait please, I’m trying to make an example )
I figured, yes, it is not because of commas but because of the namespaced maps syntax.
gf__#:some-ns{:keys [a b c]}__111008
forms like that injected after macro expansion
clojure stops reading such symbols after closing } and starts reading next symbol 111008 as a new symbol. So one symbol becomes two and it violates the spec for fn
Even if a protocol definition with destructuring works, it actually doesn't produce the right code specifically because of that reason - the signatures become wrong.
but it won’t work in case of :keys [a b c] :strs [d e]
did you try call the implementation?
(defprotocol X (foo [this {:keys [a b] :strs [c d]}]))
(foo (reify X (foo [_ _] 42))) ;; => No single method: foo of interface: user.X found for function: foo of protocol: X
I did
(foo [_ _])
ah, I got you ) sorry
(defprotocol IWork
(f [this {:keys [a b c] :str [d e] :as m}]))
(defrecord Work []
IWork
(f [this b] b))
(f (->Work) {:a 1})
(defprotocol IWorkNamespaced
(g [this {:some-na/keys [a b c] }]))
(defrecord WorkNamespaced []
IWorkNamespaced
(g [this b] b))
(g (->WorkNamespaced) {:a 1})
(defprotocol IDontWork (h [this {:some-na/keys [a b c] :as m}]))
;; Fails:
;; #:clojure.error{:phase :macro-syntax-check,
;; :line 198,
;; :column 1,
;; :source
;; "example.clj",
;; :symbol clojure.core/fn}
I'm not convinced yet, because passing {a :a}
into gensym
(exactly what defprotocol
does) is not a valid thing to do. But I can't come up with an example that would break it.
gensym is used to produce unique symbols for fn
’s arguments
but if you have /
somewhere inside of the prefix for gensym result won’t be a simple symbol anymore
{:some-na/keys [a b c]} ;; works because it "printed" as #:some-ns{:keys [a b c]}
(set! *print-namespace-maps* false)
{:some-na/keys [a b c]} ;; doesn't work because the way the map is "printed" affected by *print-namespace-maps*
What does printing have to do with anything? The form that's fed into the macro is not with #
.
Apart from that, there's still a suffix produced by gensym
.
https://github.com/clojure/clojure/blob/master/src/clj/clojure/core_deftype.clj#L596
this line
Not sure what's going on with defprotocol
and why it works with {a :a}
.
If I macroexpand it and use the result in its place, it fails with the expected Wrong number of args (2) passed to [...]
- I'm pretty sure exactly because {a :a}
is turned into something like gf__ {a :a} __5667
.
ah ok, that was why you were talking about commas: the destructuring binding is passed to gensym
so invalid symbols end up in the generated form, which then cause the spec check to fail
I've created a ticket: https://clojure.atlassian.net/browse/CLJ-2657
Perhaps worth adding {:some-ns/keys [a]}
to the test - exactly since it becomes a symbol without /
even though the original form includes /
.
TIL that you can't just copy and paste the result of macroexpand
- exactly because gensym
will become a single symbol, regardless of its contents.
@chris.blom thx for the ticket
I’ve created the following patch to support :as-alias
in tools.namespace but seems like my Jira account is lacking the permissions to submit it. Any other means for me to submit it? (I’ve previously signed the contributors agreement.)
file a request at https://clojure.atlassian.net/servicedesk/customer/portal/1 for access
(btw, #clojure-dev is a better place for stuff like this and I'm more likely to see stuff there)
(def a (atom {:x 0 :y 0}))
(defn inner [x]
(swap! a update :y inc)
(inc x))
(defn outer []
(swap! a update :x inner))
(outer)
Is this undefined behaviour? I was helping a beginner who ran into quite a subtle bug trying to do nested mutations in this manner.
I realise it's ill-advised to do this at all, and the documentation for swap!
says that f should be side effect free - just wondering if the semantics of this even make any sense.it does not make sense
Clojurescript simply discards the inner swap!
operation (resulting in @a = {:x 1 :y 0}
, whereas running this on JVM Clojure does not terminate, applying inner
endlessly.
as you have violated the constraint on side effects
and to recap how swap! on atoms work: • get the value of the atom as it is right now • compute the new value it will be after applying the function • if the underlying atom's value is still the same as when we started, "commit" the new value to it. If the underlying value has changed, need to rerun the function on the new state The infinite loop should be apparent from this
the outer
swap can never finish, because it's always changed by calling inner
within it's "transaction" ... so swap!
will retry, assuming that you've not tried to do exactly what you've done ... I think it's exactly the defined behaviour of atoms
https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Atom.java#L33-L45
I think I understand that better now after reading https://clojure.org/reference/atoms, but what about the CLJS behaviour? Does it skip the compare step because of assumptions about the single-threaded runtime? (and is that acceptable because it's undefined in the first place?)
yes. things can't change underneath because there's only a single thread of execution
just adding this cos I went away and typed it while trying to explain ...
(let [a (atom {:x 0 :y 0})
swap1 @a
swap2 @a]
(compare-and-set! a swap2 update :x inc)
(compare-and-set! a swap1 update :y inc) ;; will always fail because of the previous line
)
I think that's equivalent to the code you pasted ... but it sounds like you got it ... so feel free to ignore me 😉This is the prepl I'm referring to: https://clojuredocs.org/clojure.core.server/prepl
but using https://clojuredocs.org/clojure.core.server/io-prepl you can turn any clojure.main/repl into a prepl
so it would not surprise me if editors just say they use clojure's built in socket repl (based on clojure.main/repl) and turn it into a prepl as needed
https://github.com/jebberjeb/clojure-socketrepl.nvim is some kind of neovim plugin or something for it
if you are just looking for an example of prepl usage, I don't know if any of those editors use prepl. or just use the clojure.main/repl (which is what https://clojuredocs.org/clojure.core.server/repl runs)
https://github.com/eerohele/Tutkain/ supports the socket REPL exclusively. FWIW, it actually starts a prepl-like REPL on top of the socket REPL, but that's a bit of an implementation detail.