This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-12-16
Channels
- # adventofcode (36)
- # announcements (30)
- # babashka (1)
- # beginners (161)
- # boot-dev (2)
- # bristol-clojurians (3)
- # clj-kondo (10)
- # clojure (125)
- # clojure-europe (10)
- # clojure-italy (4)
- # clojure-nl (7)
- # clojure-spec (1)
- # clojure-uk (26)
- # clojuredesign-podcast (3)
- # clojurescript (13)
- # core-async (5)
- # cryogen (3)
- # cursive (134)
- # data-science (8)
- # datascript (3)
- # datomic (32)
- # fulcro (24)
- # graalvm (2)
- # joker (5)
- # leiningen (5)
- # malli (18)
- # off-topic (14)
- # pathom (4)
- # re-frame (3)
- # reagent (11)
- # remote-jobs (3)
- # rewrite-clj (8)
- # shadow-cljs (47)
- # spacemacs (3)
- # sql (12)
- # vim (6)
In defmulti/defmethod
, can we capture more than one dispatch values in one defmethod
? If not, is there a way to call another defmethod
from a defmethod
?
The value returned by the dispatch function can be a vector of multiple elements. That is still a single return value from the dispatch function, not more than one. Not sure if that would achieve your goal.
Sorry, my question was confusing. I have a multimethod that dispatches on the URL scheme. For example, for HTTP URLs, the value can be :http or :https. I want a single defmethod to handle both :http and :https.
You could write a function foo
that does the common thing, and call it from more than one method defined via defmethod
, one for value :http
the other for value :https
Yeah, that works too. I just wish there was a (defmethod connect #{:http :https} ...)
.
Why keep them separate if they will always do the same thing?
You can use derive
, with the caveat that (as far as I know), you need to use namespaced keywords
(derive ::https ::http)
(isa? ::https ::http)
;; => true
(defmulti request-router ( fn [req] (:method req)))
(defmethod request-router ::http [_] "http")
(defmethod request-router ::ssh [_] "ssh")
(request-router {:method ::http})
;; => "http"
(request-router {:method ::ssh})
;; => "ssh"
(request-router {:method ::https})
;; => "http"
Actually, it doesn't have to be a namespace keyword if you use a custom hierarchy:
(def h (-> (make-hierarchy)
(derive :https :http)))
(isa? h :https :http)
;; => true
(defmulti req-router (fn [req] (:method req)) :hierarchy #'h)
(defmethod req-router :http [_] "http")
(defmethod req-router :ssh [_] "ssh")
(req-router {:method :http})
;; => "http"
(req-router {:method :ssh})
;; => "ssh"
(req-router {:method :https})
;; => "http"
Greetings! I was wondering if there was a way to integrate a nicer pretty printer like fipp
into clojure.tools.trace
? https://github.com/clojure/tools.trace/blob/e6bc29e4c1d9d69d6d9d7cce62ee8da02662d284/src/main/clojure/clojure/tools/trace.clj#L71 This mentions "may be rebound" but I'm not too sure what this means. Should I alter-var-root
another fn that uses fipp
?
that code seems to be expecting you to do something like with-redefs
, but that's generally problematic (particularly with multi-threaded code)
the changelog note "replaced *trace-out* with tracer"
makes it sound like it was previously using a dynamic var, which is what I would expect for something like this
Hello Alex and thanks for the explanation 🙂 My guess is that *trace-out*
used to be a way to redirect from stdout to something else - which is often a concern when having concurrent output. Back to my eye candy issue, using alter-var-root
feels promising at this point.
yeah, that will work in most cases (won't work if everything has been aot compiled with direct linking)
Thanks for that remark. I'll make a comment out of it and bear that in mind if I do aot-compile+direct-linking
Here's my result. It works! https://gist.github.com/mrzor/7aab0b628d73d7d2d87e0dfb02030e99 On a sidenote, it prettifies alright but on second thought, it'd be better if CIDER was doing it. Which will be a distraction for another day 🙂
is it possible to have a running program reload certain files on update?
@sova You could definitely construct code that would do that but when reloading namespaces there are several caveats/gotchas to be wary of.
A running program is no different to a running REPL in Clojure so you can call (require 'my.namespace :reload)
inside the program to reload a namespace -- but all the usual caveats about code reloading apply: def
(and defn
) get reevaluated so any global state you have may be recreated (`defonce` can help you there); any already compiled-in references to def
s won't change unless you reload/recompile that code too; changes in records, protocols, types etc can cause problems (because you can get new instances of existing classes, causing class not found exceptions etc; multimethods can also be a pain point unless you handle them correctly.
In the REPL, some folks resort to full namespace reloading through tooling (and often don't even realize what is going on behind the scenes) and you cannot safely do that inside a running program (even the Component Reloaded workflow assumes you stop the program, reload, then restart the program).
(this is all part of why I don't use a "reloaded" workflow even in the REPL -- so I develop better habits about REPL-driven development, constantly re-evaluating code as I change it, from the bottom up -- and that workflow transfers over to applying changes to live, running programs as well)
Anyone familiar with running and connecting to a REPL inside a docker image? I was trying to follow the steps here https://stackoverflow.com/questions/50018490/how-to-repl-uberjar-inside-a-docker-container and option to update the project.clj doesn't appear to have any effect.
@kevin.byrne It's unfortunate that no one answered the last question in comments by the OP there: the JVM opts need to be provided as part of the java
command that starts the uberjar inside the Docker container
The person who answered did say that but the OP missed it and, instead, focused on putting those options into their project.clj
(locally) -- which only has the effect of starting a Socket Server in the local process and very likely blocking the Docker container from attaching to port 5555.
What "steps" from that SO answer have you followed?
I've updated my project.clj with `
:jvm-opts ["-Dclojure.server.repl={:port 3001 :accept clojure.core.server/repl}"]
That won't affect the Docker container -- since it's running the uberjar via the java
command, right?
So I need to add `
-Dclojure.server.repl="{:port 3001 :accept clojure.core.server/repl}"
as well?Don't put it in project.clj
-- unless you want a Socket REPL running locally.
Whatever java
command you use inside the container is where the -Dclojure.server.repl=...
option needs to go.
If you want a Socket REPL running in your local process as well you need to use a different port number, otherwise it will conflict with the Docker container's exported port.
So does `
CMD ["java", "-Dclojure.server.repl=\"{:port 3001 :accept clojure.core.server/repl}\"", "-jar", "app-standalone.jar"]
Look right?Yeah, modulo the "
escaping being correct which I'm not familiar enough with Docker CMD
to tell off-hand.
fun trick for tricky string escapes: with gnu shell printf
you can use %q
to get the escaped / quoted version of the argument - it's just too bad it doesn't work on OSX
$ printf '%q\n' '"{:foo :bar}"'
\"\{:foo\ :bar\}\"
it errs on the side of being overcareful
Docker will need to export 3001:3001
to expose that outside and then you can use telnet
to connect to that Socket REPL.
I'd just throw '
in there instead of nested/escaped "
But you cannot have Leiningen running locally unless you have also removed the :jvm-opts
you showed above, or you changed it to a different port (and restarted any existing Leiningen processes).
I believe I still have something wrong in the docker command `
CMD ['java', '-Dclojure.server.repl="{:port 3001 :accept clojure.core.server/repl}"', '-jar', 'app-standalone.jar']
I ran the uberjar at the command line via java -Dclojure.server.repl="{:port 3001 :accept clojure.core.server/repl}" -jar app-standalone.jar
and I was able to attach the repl via telnet 127.0.0.1 3001
but I get rejected by the Docker image.
I assume you opened 3001 outside the image?
`
EXPOSE 3001
the other possibility is if the repl makes a localhost only port (which is a sane choice - a repl is effectiviely a root hole)
'-Dclojure.server.repl="{:address "0.0.0.0" :port 3001 :accept clojure.core.server/repl}"'
by default it listens on 127.0.0.1 (localhost), so only visible within the docker container
So adding the double quotes to the docker command is causing issues.
`
CMD ['java', '-Dclojure.server.repl="{:port 3001 :accept clojure.core.server/repl :address "0.0.0.0"}"', '-jar', 'app-standalone.jar']
0.0.0.0 doesn't need quotes
or wait, for clj it does - so you need \ escape
CMD ['java', '-Dclojure.server.repl="{:port 3001 :accept clojure.core.server/repl :address \"0.0.0.0\"}"', '-jar', 'app-standalone.jar']
Is that what you meant? I get error with that saying [java, : not found
Which appears to be a Docker parse issue
OK - I don't know what Docker expects here - hopefully someone else does
I would expect Docker to want "..."
style strings. When @noisesmith suggested using '
I think he meant just inside the clojure.server.repl=...
argument...
oh - I didn't notice that change, yeah
CMD ["java", "-Dclojure.server.repl='{:port 3001 :accept clojure.core.server/repl :address \"0.0.0.0\"}'", "-jar", "app-standalone.jar"]
So here's where I'm at:
CMD ["java", "-Dclojure.server.repl='{:port 3001 :accept clojure.core.server/repl :address \"0.0.0.0\"}'", "-jar", "app-standalone.jar"]
This is now getting accepted by Java, but I think the params are not quite right for Clojure, since I'm getting this error: Exception in thread "main" java.lang.ExceptionInInitializerError at clojure.lang.Namespace.<init>(Namespace.java:34) at clojure.lang.Namespace.findOrCreate(Namespace.java:176) at clojure.lang.Var.internPrivate(Var.java:156) at com.markmonitor.versionmanager.server.core.<clinit>(Unknown Source) Caused by: java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol
Oh, you know what... you don't need those '
quotes in there I think...
CMD ["java", "-Dclojure.server.repl={:port 3001 :accept clojure.core.server/repl :address \"0.0.0.0\"}", "-jar", "app-standalone.jar"]
If that fails to parse, replace the spaces with commas in the {...}
part.
I thought they were needed in order to make the {} into a single arg... but I guess the way Docker handles it does that for you already?
Shell quoting is just monstrous 😞
It appears to have worked
Thank you 🙂
I forgot to mention that the reason why we do it in a script, is that in the script you have far more control over how you initialise the JVM, like choosing the GC, or memory sizes, or adding in agents for metrics. Something that just becomes cumbersome in Dockerfiles to do...
(Summary: no, but I'm gonna ramble about my experiments with lens) When I first introduced myself to Clojure, I purely wanted to use it to experiment, and was valuing it more as a lisp than as Clojure (where I can express perhaps anything), and one of the things I did was implement this unusual .. functional object oriented-ish frankenstein system built on lenses, where possibly all forms of polymorphism were themselves sort of based on lenses. Plus, I was coming from Haskell. (Disclaimer: again, this was for experimentation; don't do this) Meaning, instead of subtyping, if you have the following (excuse me, I used records and protocols instead of, say, maps and multimethods I believe. You don't generally use records in Clojure afaik)
(defrecord Cat [ ... ] )
You would give the cat an 'Animal' lens
;; I forget how I did it, but I think it was all using protocols and structs, instead of maps
;; and multimethods
(extend-protocol HasAnimal Cat
(get-Animal ...)
(set-Animal ...))
Once this was defined, Cat would work with all Animal lenses -- and because of the transitive nature of lenses, any lenses Animal extended.
So if animals were expected to themselves have a sound
lens, which would fetch the sound they'd make, then you could make this function for anything to extend the Animal lens
(defn make-sound [our-animal]
(println (l-get-in our-animal [animal sound])))
Here animal
and sound
are lenses being composed, and note you included the animal
first to ensure that whatever you're looking at is attempted to be converted into an animal first (and if that succeeds, now it transitively will work with all of animal's lenses; as we know, lens compose).
This would work for any sort of behavior --if you wanted to give something 'battle behavior', you'd ask "what data can express the change battle has to cause? Well, how about {:hp :strength :defense} (except I think, again, I used records). Ok, now let's extend this type we have to allow battle
;; Interface for battle
(defrecord BattleBody [hp strength defense])
(make-lenses BattleBody)
(defrecord ComplexType [ health initial-strength magic-strength status-effects ...])
(extend-protocol HasBattleBody
ComplexType
(get-BattleBody [obj]
{ :hp (:health obj)
:strength (+ (:initial-strength obj)
(magic-strength obj)
(get-status-effect-strength-multiplier obj)})
(set-BattleBody [obj body]
(assoc obj :health (:hp body)))))
;; Perhaps this 'BattleBody' only can change health -- not strength or defense, which as it is
;; may have lost information in what individual stats they were derived from
Then
(def battle [attacker defender]
(l-update-in defender [body health] - (l-get attacker strength)))
Where body
and health
and strength
are lenses.
I may be violating some lens laws there, I forget -- this is the first time I had an example where I realize I might not want it to perfectly, symmetrically, get and set
I would never do real Clojure this way, but it is useful for looking at things in different ways. However, there has finally been a recent use where I've considered for a personal project (never otherwise) using a watered down map version, where I have registers on a cpu that can be accessed like this
AF
, which will will view the registers A
and F
together as one 16 bit integer, if I remember
A
, which will just view the register A
as an 8 bit register
F
, which will do the same
I still like the ability to do things like
(l-set cpu :a 0xff)
(l-set cpu af 0x11ff)
Although now that I'm away from the problem, I forget why I wanted to do that in this case, and not have some other functions for it (perhaps it was for fun again, but I feel like there was something I wanted to actually accomplish)
Anyways, unlike Haskell many of the nice features you might be trying to accomplish with lenses are not exactly a problem to begin with, and already have simple implementationsInteresting, thanks for sharing. Using protocols and structs didn’t immediately come to mind. Just fns and paths (vectors)
I like the ability to abstract data location, but it’s fine enough to have a central reference for all needed paths
As long as its clear I am not advocating for this ahahaha and if you are going to mess with this, I would probably use maps and multimethods, but even then I need to think about this for a moment as its been a while since I messed with that
The main advantage I was looking for was abstracting and composing paths, which is possible with Clojure data structures.
and for the other 10%, I don't think the logical overhead of introducing something new like lenses is worth it
not to mention depending on how its done it can be a long winded way of describing something
there were other issues -- I think, for instance, you can't just extend something with the protocol to use a lens, and then immediately use the lens, which was the point -- you have to recompile the lens each time, and so then there's another mess to think about. Something like that anyways, as mentioned its been a while
using just maps and functions and specs is simple and seems to go a long way, although like everything else I'm still exploring it and exploring it
I'm surprised the docs don't mention this, but FYI defstruct is no longer recommended. You can use defrecord for things that should act like maps or deftype for immutable data with defined updatable keys
or you know, just use hash-maps and functions like normal code :D
the code sample above uses defstruct
Yeah but that's what I'm saying, now that you mention it I think the original experiment used defrecord , not defstruct. This wasn't copy and pasted from that, but me trying to recall how it was written, and since I never use those now, I think I got defrecord
mixed up with perhaps Common Lisp's defstruct
that's fair - I think ComplexType would be defprotocol in that case
I'm making some edits right now. The ComplexType
part is correct, in that it should be a record; the ComplexType
represents some type that should inherit, in this case, battle behavior. Perhaps I'll add an edit with two types that can be imagined; Player
and Sword
, two things that might respond to being attacked, but might not look the same at all internally. One line I just added is there's a (make-lenses BattleBody)
that should be below BattleBody
; this generates a lens for that BattleBody
, and the protocol HasBattleBody
as well as lens and protocols for hp
and strength
and defense
I believe. Anything now that extends the HasBattleBody
protocol can use the BattleBody
lens, and will work with functions defined on top of that interface
OK - in that case HasBattleBody can't extend ComplexType
OH! I read it backward, never mind
Aha yeah I think that can be easy to do since there's both
(extend-type Type
Protocol)
and
(extend-protocol Protocol
Type)
For me anyways, this threw me for a loop once as I had gotten used to one order, only to one day see the other macro used without realizing the switcheroothanks to less type enforcement, a big part of their utility disappears
no, they are not common at all (though there are libraries that provide some of their features)
we have get-in
and update-in
and assoc-in
which provide some overlap but definitely are not the same thing
those functions all take path as a data structure, that data structure can be a top level shared binding
or an arg to some other function
or constructed in whatever manner
also we try to avoid nesting - we merge maps when apropriate
once again, without strong type enforcement that becomes an option
Sure, do you have a general resource for the advantages of Clojure’s lack of strong type checking? I know that’s a huge question, and I’ve seen many of Hickey’s talks, but I wondered if there were a blog post or docs that explain examples like these.
“In the typed functional world we do…but in clojure it’s just <this> because of <this>”
Rich Hickey's talks about clojure.spec might be particularly interesting here
they tend to make Haskell fans angry though haha
I wouldn’t say I’m a Haskell purist, more so interested in various solutions and why and how to use them
the advantage is that you don't need to spend any time trying to prove something to the compiler
either during initial development or as the system evolves
that's a good point - there are things that are easy and effective to use but hard to formally prove
hopefully somewhere in there, yes :)
yeah, I really miss OCaml refactoring sometimes - I could just change the type, and the compiler would lead me to all the relevant code that needs updating until the refactor was done
the assertion here is not that types are bad or don't have benefits, but that types have (often ignored) costs
and Clojure is the hypothesis that you can make robust systems that evolve gracefully over time without types
you can certainly make robust systems with types (and things like compilers are probably better that way) but I think the evidence for "graceful evolution" are, at best, mixed
spec.alpha2 - no
but the end is in sight
I’ve actually been wanting to play with bringing spec to JS now that we have Babel macros
there are a couple ports out there
ah yeah, that's the main one I was thinking of
well, function signature stuff is going to change a lot in spec 2, tbd
Yeah, I remember seeing the talk, I remember being excited, but I’ll have to rewatch it
no talk about this :) it's in design right now
unless you mean the schema/select stuff, which is out there now
I believe this is the working doc for that https://github.com/clojure/spec-alpha2/wiki/Differences-from-spec.alpha
that's more of the retroactive part :) the design edge is not in public.
(Summary: no, but I'm gonna ramble about my experiments with lens) When I first introduced myself to Clojure, I purely wanted to use it to experiment, and was valuing it more as a lisp than as Clojure (where I can express perhaps anything), and one of the things I did was implement this unusual .. functional object oriented-ish frankenstein system built on lenses, where possibly all forms of polymorphism were themselves sort of based on lenses. Plus, I was coming from Haskell. (Disclaimer: again, this was for experimentation; don't do this) Meaning, instead of subtyping, if you have the following (excuse me, I used records and protocols instead of, say, maps and multimethods I believe. You don't generally use records in Clojure afaik)
(defrecord Cat [ ... ] )
You would give the cat an 'Animal' lens
;; I forget how I did it, but I think it was all using protocols and structs, instead of maps
;; and multimethods
(extend-protocol HasAnimal Cat
(get-Animal ...)
(set-Animal ...))
Once this was defined, Cat would work with all Animal lenses -- and because of the transitive nature of lenses, any lenses Animal extended.
So if animals were expected to themselves have a sound
lens, which would fetch the sound they'd make, then you could make this function for anything to extend the Animal lens
(defn make-sound [our-animal]
(println (l-get-in our-animal [animal sound])))
Here animal
and sound
are lenses being composed, and note you included the animal
first to ensure that whatever you're looking at is attempted to be converted into an animal first (and if that succeeds, now it transitively will work with all of animal's lenses; as we know, lens compose).
This would work for any sort of behavior --if you wanted to give something 'battle behavior', you'd ask "what data can express the change battle has to cause? Well, how about {:hp :strength :defense} (except I think, again, I used records). Ok, now let's extend this type we have to allow battle
;; Interface for battle
(defrecord BattleBody [hp strength defense])
(make-lenses BattleBody)
(defrecord ComplexType [ health initial-strength magic-strength status-effects ...])
(extend-protocol HasBattleBody
ComplexType
(get-BattleBody [obj]
{ :hp (:health obj)
:strength (+ (:initial-strength obj)
(magic-strength obj)
(get-status-effect-strength-multiplier obj)})
(set-BattleBody [obj body]
(assoc obj :health (:hp body)))))
;; Perhaps this 'BattleBody' only can change health -- not strength or defense, which as it is
;; may have lost information in what individual stats they were derived from
Then
(def battle [attacker defender]
(l-update-in defender [body health] - (l-get attacker strength)))
Where body
and health
and strength
are lenses.
I may be violating some lens laws there, I forget -- this is the first time I had an example where I realize I might not want it to perfectly, symmetrically, get and set
I would never do real Clojure this way, but it is useful for looking at things in different ways. However, there has finally been a recent use where I've considered for a personal project (never otherwise) using a watered down map version, where I have registers on a cpu that can be accessed like this
AF
, which will will view the registers A
and F
together as one 16 bit integer, if I remember
A
, which will just view the register A
as an 8 bit register
F
, which will do the same
I still like the ability to do things like
(l-set cpu :a 0xff)
(l-set cpu af 0x11ff)
Although now that I'm away from the problem, I forget why I wanted to do that in this case, and not have some other functions for it (perhaps it was for fun again, but I feel like there was something I wanted to actually accomplish)
Anyways, unlike Haskell many of the nice features you might be trying to accomplish with lenses are not exactly a problem to begin with, and already have simple implementations