This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-09-22
Channels
- # announcements (11)
- # architecture (7)
- # babashka (26)
- # beginners (314)
- # calva (20)
- # chlorine-clover (1)
- # cider (5)
- # circleci (27)
- # clara (6)
- # clj-kondo (41)
- # cljsrn (6)
- # clojure (120)
- # clojure-berlin (2)
- # clojure-czech (2)
- # clojure-dev (4)
- # clojure-europe (45)
- # clojure-france (4)
- # clojure-germany (1)
- # clojure-nl (5)
- # clojure-uk (20)
- # clojuredesign-podcast (8)
- # clojurescript (6)
- # conjure (42)
- # datascript (5)
- # datomic (21)
- # deps-new (28)
- # duct (1)
- # fulcro (36)
- # graalvm (10)
- # graphql (24)
- # jackdaw (19)
- # jobs (2)
- # jobs-discuss (46)
- # kaocha (6)
- # malli (26)
- # off-topic (21)
- # parinfer (5)
- # re-frame (10)
- # sci (19)
- # shadow-cljs (95)
- # tools-deps (24)
- # vim (3)
(defn loop-over-sets
[expr kb]
(filter #(and (= (first %) 'if) (= (second %) expr)) kb)
)
Im getting a dont know how to create from ISEQ error when my expr = 'a and when my kb = '(if a b)does anyone know why?
this works when i have kb = '((if a b)(if b c))
Having just '(if a b)
as kb
doesn't sound right based on what you've described: that's a sequence of just if
, a
, and b
. I think you want kb
to be '((if a b))
i.e., a sequence of expressions.
(and the reason for the error is that filter
will run that predicate over if
, a
, and b
and the predicate calls first
and second
which aren't valid on symbols (but are valid on sequences).
hmmm is there a way to change '(if a b) to '((if a b)) in code?
I think you should probably rethink how you're calling that function
are you trying to loop over a list of expressions and return the results for each?
its because kb can be both
yeah so, What i want to happen is, if kb is ((if a b)(if b c)) and expr is 'a. It would look for "if a" and then it would derive b. And if kb is just '(if a b), then it would still derive b
I think if i do if statement saying if first = 'if, then derive the 2nd nth else use that filer function
it would work
filter*
don't know if there's a better way to do it, but you can (if (list? (first kb)) kb (list kb))
I guess
It's not good practice to have a data structure that can be different shapes like that. kb
should always be a list of expressions, even if it has just one expression in it.
(that whole series of do's and don'ts is worth reading)
I understand what im doing wrong but how do i make sure that kb is '((if a b)) and not '(if a b) when being passed in
You should solve that problem at the source of where kb
is being created -- and ensure it is always a sequence of expressions and never a single expression.
is there a way to change kb to '((if a b))
In other words, your "loop over sets" (terrible name 🙂 ) should always just assume it is passed a sequence of expressions.
It should not be trying to accommodate someone calling it with invalid data.
How/where is kb
created? That's where you should fix the problem.
(defn fwd-infer
[prob kb]
(cond
(= (first prob) 'and) (fwd-infer (first (and-elimination prob)) #(and-elimination prob))
(= (first prob) 'not) (fwd-infer (elmination-not prob) #(elmination-not prob))
(= (first prob) 'if) (fwd-infer (modus-ponens (nth prob 1) kb) #(modus-ponens (nth prob 1) kb))
)
)
This is where kb is createdIt is just passed through my infer function
so it's the and
case that might return just one element
but you're not creating kb there
you're just passing it
For my homework, we just simply use the REPL to pass our data in
so it is a requirement that it accepts both '(if a b) and '((if a b) (if c b))?
i'd say just transform the data when you read it
if the first form is not a list, wrap everything in a list
There is maybe a better way to do this but this is the only way i could think of doing it
Ok ill try that
or you add an 'or and do '(if (or a c) b) 😛
how would i wrap everything in a list
(list x)
i gave you the code earlier
hello, I have a lein project i’m trying to add shadow-cljs to, first time using it (I’m working from the book Web Dev with Clojure) I have, src/clj
and src/cljs
folders each with a guestbook.core
namespace. Could this be causing an issue? I am getting this error trying to start up shadow-cljs, I’ve looked over my project.clj and shadow-cljs.edn a few times and nothing’s jumping out at me as incorrect…
[:app] Configuring build.
[:app] Compiling ...
[:app] Build failure:
The required namespace "guestbook.core" is not available, it was required by "guestbook/app.cljs".
"guestbook/core.clj" was found on the classpath. Should this be a .cljs file?
Your
namespace is requiring guestbook.core
-- but
is ClojureScript and guestbook.core
is Clojure, based on the extensions.
If both src/clj
and src/cljs
are on your classpath, you can't have the same named file in two places -- only one of them will be found.
ClojureScript apps do tend to have .clj
files in them for macros (since ClojureScript relies on Clojure for macro definition and expansion, prior to compilation -- to JavaScript).
thanks. just tried renaming the cljs file and ns, but it’s still not finding it..must be something else im not seeing
Are you getting that same error, or a different one?
The error above is that it is finding guestbook.core
but it's expecting a .cljc
or .cljs
extension on it.
oof…i was missing :source-paths [..., "src/cljs"]
in my project.clj …
build completed now with the original filenames maintained, there’s a core.clj and a core.cljs using the same ns in the project. Not sure if i’ll run into issues down the line…
i was using lein cljsbuild previously, and removed that config entirely which had its own source path prop
Glad you got it working, and thank you for reporting back with the solution!
what is the best way to redefine a binding in an outer scope, for ex:
(let [x 1]
(do-something-to-x-to-make-it 2)
(+ x 1))
what could be the approach? I'm traversing a tree and need to maintain some state
That style is what works in clojure, but sometimes you use loop/recur in place of recursive function calls, but it will have the same shape as a recursive function call style
i'm traversing the tree alright
an example of tree traversal using an atom: https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/util.cljc#L241
the problem is i want to pass some state between sibling branches
I know about atom, thought it was just used for concurrency stuff
https://mitpress.mit.edu/sites/default/files/sicp/full-text/book/book-Z-H-11.html#%_sec_1.2
yeah, that's what i was thinking
http://sarabander.github.io/sicp/html/1_002e2.xhtml#g_t1_002e2_002e1 (i always link to this nicely formatted version)
in this case i'm traversing the tree using a multimethod
so i should wrap it in another function that deals with the state
and call the multimethod inside that
https://github.com/hiredman/qwerty/blob/master/expand.clj#L313 is a rather complicated function (a macro expander for another programming language) that walks a tree (expressions in the language) and passes information down and up and around
@hiredman that's more or less what I'm doing here
the "trick" to make sure your state goes into the siblings is to use a reduce to keep accumulating state between the siblings.
ok, my issue is that now I have to deal with state at every node when the majority of them don't care about it
I think atom is what I want here
(defn loop-over-sets
[expr kb]
(filter #(and (= (first %) 'if) (= (second %) expr)) kb)
)
This gives me the correct output with (if a b)(if b c) as kb and b as expr but when i have kb = (if a b) a (if b c) and my expr = b, I want to get (if b c) but im getting a "Don't know how to create ISeq from: clojure.lang.Symbol"@joao.galrito Do you know if i could change this function so it can read both ((if a b)(if b c)) AND '((if a b) a (if b c))
what do you want to do with the 'a in the middle
ignore it?
because this function only looks for ifs
it should look for ifs
then you need to add another condition to your filter predicate
to check if what you're reading is a list
because right now you're trying to call (first 'a)
i see now
I figured it out
I think
sequential?
is probably the predicate you want here.
nvm i havent got it
haha this is hard
Problems only lead to more problems
@seancorfield any particular reason? If just working with lists
(filter #(and (sequential? %) (= (first %) 'if) (= (second %) expr)) kb)
(instead of list?
for example)
user=> (list? ())
true
user=> (list? (range 5))
false
user=> (list? (map inc (range 5)))
false
user=> (sequential? ())
true
user=> (sequential? (range 5))
true
user=> (sequential? (map inc (range 5)))
true
user=>
gotcha
if at any point it turns into a lazy list it breaks
list?
is very specific and if Nazar is building the kb
programmatically, he might get sequences that are not lists.
he said kb
is coming from the REPL
But he's processing it with fwd-infer
for the reductions?
but even then just using filter
s and map
s can result in a lazyseq
Yup. Test for the abstraction, not the concrete type.
I am using following code to create an empty file.
(defn create-seed [name]
(with-open [wtr (io/writer (get-seed-file-name name))]
(.write wtr "")))
Is there a shorter version?@suren Using spit
...
(spit (get-seed-file-name name) "")
It's a built-in version of the code you posted:
user=> (source spit)
(defn spit
"Opposite of slurp. Opens f with writer, writes content, then
closes f. Options passed to ."
{:added "1.2"}
[f content & options]
(with-open [^java.io.Writer w (apply jio/writer f options)]
(.write w (str content))))
nil
user=>
Now I see the source, I guess you could even do (spit (get-seed-file-name name) nil)
which might be more explicit?
Since (str nil)
is ""
Thanks @seancorfield it worked like a charm
Also, what do you want to happen if the file already exists?
(spit ... nil)
will overwrite it with an empty string; (spit ... nil :append true)
would append an empty string to an existing file (i.e., leave it unchanged apart from the mod date/time).
I suspect .createNewFile
would error out if it already exists
Ah, no, it would just return false
(and it returns true
if it successfully creates the file)
io/file
is very confusing.
I am using following code to list the files in a directory.
(->> (io/file "resources/seeds")
(.list))
As @U11BV7MTK pointed out (.createNewFile (io/file "bob"))
creates a new file.
What exactly is (io/file 'path)'
?user=> (.createNewFile ( "/tmp/foo"))
true ; file did not exist, this created it
user=> (.createNewFile ( "/tmp/foo"))
false ; file existed so it was not touched
user=>
io/file
creates a
object so you'll have to read the Java API docs for that.
But it's basically an abstraction for a file.
So it can be a file that doesn't exist, a directory, a file that does exist, or any implementation of something that can behave like a file.
Java has dramatically improved its support for file ops in more recent versions, so now there's a java.nio.*
package hierarchy with abstractions for paths and files and entire file systems 🙂
Clojure pretty much just delegates to Java interop for file stuff.
@suren You get used to reading (Java) stack traces but they can be daunting at first.
Not sure if this is the best section to ask but for a serious web project would you go with ring/reitit or pedestal?
@U06FM9QN9 both are definitely for serious projects. I would go with ring/reitit but it’s because I’m more familiar with it and my colleagues are more inclined to it. I’d still go through both documentations and decide which is more to your liking. Also reitit comes with swagger support out of the box but that functionality is easily available for pedestal as well so they’re not too far apart. I’m still very much a clojure beginner so somebody with actual production experience with both should chime in.
I would check the reitit faq for some explanation of the differences https://github.com/metosin/reitit/blob/master/doc/faq.md
Yeah. I’ve touched on both but I believe I’m going to focus on reitit. It’s a little friendlier to beginners while at the same time being more than performant.
Assuming a function has too take two arguments, but you want one of them to just not do anything, is just doing ()
an ok option? I only want to log something based on a if-dev
macro which I am unable to modify
Yeah. I’ve touched on both but I believe I’m going to focus on reitit. It’s a little friendlier to beginners while at the same time being more than performant.
like if
?
(if x 1 0)
maps have the same interface as get)
({true 1 false 0} x -1)
also will give you default value
Thank you @U04V4KLKC, I have read from somewhere that only get can append a default value.
Why are
(apply * []) equal 1
(apply + []) equal 0
?
Has it anything to do with 1 and 0 being the identities of monoids with operators * and +?does doseq
not realize a lazy seq?
sounds like doall
?
so doall
/ dorun
+ map
for iterating over a seq for side effects?
thought that was what doseq
was for
I see there's also run!
doseq
does iterate over lazy sequences, but it doesn't return anything. Use it when you need the side effects, but don't care about the values!
I tried doseq
but nothing happened
What did you try? Here's an example:
user=> (doseq [x (range 5)] (prn x))
0
1
2
3
4
nil
(doseq [operations chunks]
(http/post
(str (env :es-host) "/_bulk")
{:body (str (string/join \newline operations) \newline)
:content-type :json})
(println "Inserted " (count operations)))
where chunks
is
(->> documents
(map #(str (json/write-str {:index {:_index collection-name
:_id (get % id-key)}})
\newline
(json/write-str %)))
(partition-all max-chunk-size))
I don't see anything that's wrong. Do you observe your (println "Inserted " (count operations))
output?
> anyway in my case I can take advantage of parallelism so ended up using `pmap` and `dorun` Looks like you worked it out!
if you want to iterate for side effects, I would recommend run!
oh, I think doseq
realizes the sequence before executing the body
anyway in my case I can take advantage of parallelism so ended up using pmap
and dorun
Hi all. I have a design question: I have this map object, which serves as the main "data model" I use in my app. I pass it around, modify it, bla bla. In addition, in order to make it easier to update it without having to break changes in the places that use this map, I ended up encapsulating it with constructor, update and getter functions. something like this:
; Constructor
(defn generate-my-model
[id]
{:id id
:field-a []
:field-b []})
; Updater / setter
(defn update-my-model
([id a b]
(assoc (generate-my-model id)
:field-a a
:field-b b)))
; Getters
(defn my-model->id [m] (:id m)
(defn my-model->a [m] (:a m)
(defn my-model->b [m] (:b m)
I emphasize: these "encapsulation" functions exist to make it easier to update my code later on without breaking things for outside users of my library. Otherwise, everywhere i had to read that map's fields i would have to use the field name explicitly. The getters and updater allows me to hide these implementation details from the outside world.
My question: Is there a better way to do this? I hate writing all these getters and updaters, it's just ugly boilerplate code. In OOP-land these things are a must due to the same reason (encapsulation). But in Clojure .. i dunno, i remember Rich Hickey saying it's ok to pass data maps around. I just don't like the cost of it. It will create a headache for me every time i want to change the implementation of this entity map. 😬
Wdyt?it depends on what you're trying to model so you may need to provide more context for a better answer. if you have getters that are simply just wrappers around specific keys, then they add a layer of indirection for little value. instead of getters/wrappers, it's may be worth creating protocols or multimethods that can provide an interface to your data model eg:
(defprotocol IShape
(area [this])
(perimeter [this]))
(defrecord Circle [r]
IShape
(area [this] (* Math/PI r r))
(perimeter [this] (* 2 Math/PI r)))
(defrecord Square [l]
IShape
(area [this] (* l l))
(perimeter [this] (* l 4)))
(area (->Circle 2))
(perimeter (->Square 4))
Thanks. Thing is, i don't really need polymorphism here. Just some sort of accessors to set and get fields while hiding the implementation.
protocols are the way to separate what from how (ie. hiding the implementation)
(defprotocol IShape
(area [this])
(perimeter [this]))
(defprotocol IScale
(scale [this s]))
(defrecord Circle [r]
IShape
(area [this] (* Math/PI r r))
(perimeter [this] (* 2 Math/PI r))
IScale
(scale [this s] (->Circle (* r s))))
(defrecord Square [l]
IShape
(area [this] (* l l))
(perimeter [this] (* l 4))
IScale
(scale [this s] (->Square (* l s))))
(area (->Circle 2))
(perimeter (->Square 4))
(scale (->Square 2) 2)
see IScale
the idea is to have a stable interface that's independent of the implementation. it can be worth it even in cases where there's just one implementation.
I see. This forces me to declare a record even though i only have a single record type. That is, no square, just a circle.
one of the biggest improvements is just spending time thinking about what the stable interface should be
as for updaters, the answer is always "it depends" 😛 . you can also use multimethods
i guess my question for you is what happens if you want to change :field-a?
nothing complex. just set the field. i just don't think it's a good idea to expose the field name, the key
do you change
(defn my-model->b [m] (:b m)
to
(defn my-model->c [m] (:c m)
or do you still have
(defn my-model->b [m] (:c m)
where the field and function name don't matcheven if i do, just because refactoring tools exist to easily rename functions rather than fields (keys), it makes the job much easier
I think of the big insights I picked up from learning is using clojure is to focus on the data model and work with that directly
rather than focusing on the getters/setters and other functions around the data
there's a bunch of reasons why that mindset works well in the long run compared to the getter/setter approach
ok, so what do i do if i rename a field for example? i have to rename it everywhere i use it, either read or set it
that's a good question
it's the data that really sticks around, not the functions around it
so if you write your data to disk or send it over the network, it's the data that is being sent
so it's really worth trying to get your data model right and thinking about how it might evolve over time
writing getters/setters is convenient in the short term, but it's just kicking the can down the road
> so it's really worth trying to get your data model right and thinking about how it might evolve over time i can't plan 2 years ahead. no matter how hard i try
> plans are useless, but planning is essential
right, you can't predict the future, but that doesn't mean that the decisions you make about your data model won't affect you down the road
the OO claim is that getters/setters will insulate you from these changes, but in my experience, that is a false promise
there's several reasons against using getters/setters, but as far as isolating you from changing field names, it only helps you within your program. if you write your data to disk or send it over the network, you still have to figure out how to deal with changing field names. further, if you package your code as a library, changing a field name can still break library users when they upgrade
these are all good questions
i don't think i'm explaining this alternate approach all that well
btw, this guy says something along your lines: https://clojureverse.org/t/do-you-encapsulate-domain-entity-maps/6147/5
I want to reiterate on this topic after watching this presentation: https://lispcast.com/you-are-in-a-maze-of-deeply-nested-maps-all-alike-talk/ This guy is practically saying the opposite of what we talked about above: There is a lot of sense in using constructors and accessors (getters, setters), which in practice encapsulate your data. It is worth the extra code investment, because you gain the ability to decouple implementation details from depending code areas. It also makes Clojure tell you when you call a function which got renamed, in contrast to changing a key name. There's more to that than I said, especially when it's a good idea to use these abstractions. I recommend you watch this talk.
I'm not generally against having helper functions that provide a way to explicitly list what the common operations are, but I would warn against pretending that it "encapsulates" your data. I also wouldn't start with defining getters/setters and would only consider adding them on a case by case basis. In my experience, getters/setters provide little benefit for data at rest or being sent over the network which is the important part to get right for maintaining long running programs and systems To address "what keys?" and "what entity?", I would just use spec. As he mentions in the talk, it's meant to address these problems. The big difference between spec's approach and defining getters/setters is that spec is data focused and getters/setters are operations focused. The differences between a data specification and an operational specification are really critical. Most programs would be better served by focusing on the data than on the operations. Depending on your domain, it may be useful to use something like specter or getters/setters to help declutter deeply nested code. In my experience, there are many domains where an extra layer of abstraction is unnecessary. I really wish computer science would be renamed data-ology.
i think this is an interesting discussion. i don't have anything to add except to note that re-frame basically has getters and setters on a big map (`app-db`) in the form of subscriptions and events. for a while i really felt like i was back in oop land, writing a getter and setter for each piece of data in app-db
.
in that context, i do think there's something to be said for separating out the data from "exactly where in this huge nested map does it belong"
i've run into many snags trying to reorganize the structure of app-db
and unknowingly breaking code that depended on that exact configuration
I think if you're running into "what's the shape of the data supposed to be?", I would reach for spec or defining the data specification rather than getters/setters
Hello again
I'm playing with a deps.edn
project and I'm having some difficulty starting up my REPL properly
So, here's my deps.edn
file:
{:paths ["src"]
:deps {org.clojure/clojure {:mvn/version "1.10.1"}
com.datomic/datomic-free {:mvn/version "0.9.5697"}
com.datomic/client-pro {:mvn/version "0.9.63"}
org.postgresql/postgresql {:mvn/version "9.3-1102-jdbc41"}
http-kit {:mvn/version "2.4.0"}
metosin/reitit {:mvn/version "0.5.5"}}
:aliases {:server {:main-opts ["-m" "grok.core"]}
:dev {:extra-paths ["config/dev" "env/dev"]}
:test {:extra-paths ["test" "config/test"]
:extra-deps {lambdaisland/kaocha {:mvn/version "0.0-529"}
lambdaisland/kaocha-cloverage {:mvn/version "1.0.63"}}
:main-opts ["-m" "kaocha.runner"]}
:socket-repl {:jvm-opts ["-Dclojure.server.repl={:port,50505,:accept,clojure.core.server/repl}"]}}}
When I do clj -A:dev:socket-repl
, I get a REPL in the namespace user
However, I can't do (ns com.my-project.core)
and have any definitions in said namespace be loaded in the REPL
https://clojure.org/guides/repl/navigating_namespaces has some good information
I tried that but it's unable to resolve any of the definitions
Lambda Island (https://lambdaisland.com/guides/clojure-repls/clojure-repls) says the REPL that appears when you do this isn't the socket REPL per se, and you need to connect to it via netcat (`nc localhost 50505`)
Hold on
user=> (require 'grok.db.core)
nil
user=> database-uri
Syntax error compiling at (REPL:0:0).
Unable to resolve symbol: database-uri in this context
Mmm, still not resolving. I also tried doing (in-ns 'grok.db.core)
to no avail
It's weird, because there was one time it worked and I could access everything from the REPL
When I reconnected via netcat, I suddenly had the same errors appearing again
if you've already run (ns grok.db.core)
you've created a new empty namespace. restart your repl and then do
(require '[grok.db.core])
(in-ns grok-db.core)
I think I got it now. I was spoiled by lein
in that I didn't have to require
namespaces there before switching to them: I can just do it directly with ns
Your solution definitely works. Thanks!
One thing that bothers me though is why in-ns
works when you pass in a nonexistent namespace
Per the docstring: "Sets ns to the namespace named by the symbol, creating it if needed."
So it will create a completely empty namespace if it doesn't already exist. It won't have any clojure.core
functions referred in.
Is there a usecase for that? I'd hate to be derailed by an errant typo
Ahh, hmm so how does it differ from ns
then?
ns
does a whole bunch of stuff: creates the ns if needed, refer in clojure.core and a bunch of other stuff, requires specified namespaces, imports specified classes, etc.
in-ns
is very low-level.
Wait, you meant *ns*
right?
I see, I see
I mean the ns
macro. Not *ns*
. The latter is a dynamic Var.
user=> (in-ns 'foo.bar)
#object[clojure.lang.Namespace 0xdf5f5c0 "foo.bar"]
foo.bar=> (ns-publics *ns*)
Syntax error compiling at (REPL:1:1).
Unable to resolve symbol: ns-publics in this context
foo.bar=> (clojure.core/ns-publics *ns*)
Syntax error compiling at (REPL:1:1).
Unable to resolve symbol: *ns* in this context
foo.bar=> (clojure.core/ns-publics clojure.core/*ns*)
{}
foo.bar=> (clojure.core/ns-refers clojure.core/*ns*)
{}
foo.bar=> (clojure.core/ns-aliases clojure.core/*ns*)
{}
foo.bar=>
BTW I saw your post earlier this year on whether or not switching to deps.edn
is a good idea and it's the main reason I'm trying to do it now. Thanks!
See how I have to specify clojure.core/
in front of various symbol names? Normally -- when you use the ns
macro -- those are automatically referred in.
I mean in your first reply, since *ns*
appears as ns
Oh, this? Per the docstring: "Sets `*ns*` to the namespace named by the symbol, creating it if needed." Yeah, I just pasted in the docstring and forgot it would auto-format that!
Just checked ClojureDocs, I should probably check it more often. In any case, thanks for the detailed responses! Really starting to get the hang of this thing.
Don't forget you can use doc
and source
right there in your REPL.
Oh yeah, that's really helpful. Should probably get used to them.
I have doc
bound to ctrl-; d
and source bound to ctrl-; S
in my editor so they are easily available 🙂
(aside from helping beginners with REPL sessions like the above, I never actually type into my REPL -- I always type into a source file, sometimes in a comment
form, and then eval code directly into the REPL from the editor)
Ahh, since I’m moving around so much and can’t bring a decent keyboard with me all the time, I’m kind of stuck with vim. But hearing your suggestion has led me to this https://thoughtbot.com/blog/writing-clojure-in-vim so I’m going to try to replicate your setup. Let’s see how it goes.
All right, lemme try that
and if you're just using a bare repl, don't netcat just type in the repl. no need for another process involved
(defn find-not-in-kb
[expr kb]
(filter #(and (sequential? %) (= (first %) 'if) (= (second %) (nth expr 1))) kb)
)
Currently, this looks for an "if" in the first of a sequence and then an random element in the second of sequence, how would i change my function so it looks for the third element of the sequence instead of the second ?this method used to work for me --> (.getURLs (java.lang.ClassLoader/getSystemClassLoader))but now i get the runtime error --> java.lang.IllegalArgumentException: No matching field found: getURLs
i wonder what has changed and how i might repair this ?
see https://blog.codefx.org/java/java-9-migration-guide/#Casting-To-URL-Class-Loader @codeperfect
ahh, migration is a good search term
marvellous, a simple (seq (java.lang.System/getProperty "java.class.path")) is working for me in the repl
Note that (java.lang.System/getProperty "java.class.path")
returns a string, so (seq
ing it will return a sequence of characters (which may or may not be what you’re after).
(clojure.string/split (java.lang.System/getProperty "java.class.path") #":")
might be more like what you’re after?
yes, i am always surprised at how i mess up data presentation, and splitting on the ";" is just right
No worries - I do it all the time too. I find the REPL and (clojure.pprint/pprint)
are my best friends for helping to visualise data. 😉
There are also fancier GUI-based tools that can graphically present Clojure data structures (often integrated with the REPL), but I’ve found (pprint)
to be more than enough in most cases.
hi! Functions that have side effects are often named with an ! in the end. How do you name functions that use resources that can change. E.g. querying a db or using other non-immutable resources.
I tend to just use a normal function name, even though the function could return different results over time. Otherwise everything up your call chain to -main
would need to be annotated with something indicating "side effects" 🙂
The original intent of !
was to indicate functions that would not be safe in a "transaction" (STM) but its use isn't very consistent -- some folks use it on anything that causes side effects, some only for functions that cause external side effects, some even for functions that call down to such functions.
if you wanted to write a new faq entry for https://github.com/clojure/clojure-site/blob/master/content/guides/faq.adoc, this is a frequent question - I think you've said the critical bits here
Under Reader and Syntax?
"What do ? and ! mean in function names?" perhaps?
(following "What does an _ mean...")
That sounds like a good place for it
In clojure.java.jdbc
and next.jdbc
, functions that are intended to be "just" queries have regular names (`query`, get-by-id
, find-by-keys
, plan
-- since plan
itself doesn't actually do anything, only when you reduce
it do you get the side-effects), as opposed to update!
, insert!
, execute!
(`next.jdbc` is a bit more aggressive about this because execute!
and execute-one!
can be used for reading the DB as well as updating it, unlike c.j.j -- and there are next.jdbc.plan/select!
and next.jdbc.plan/select-one!
which are helpers that wrap plan
with reductions, even if they are queries)
How would i filter a seqeunce like this "((if a b) a (if b c)) and only return a "a"
So i want my filter function to find a specific element in my sequence
you might want some
instead
Ok thank you!!
is point free style dead?
what's the latest on point free style. does the community like it?
if all my functions were unary, i'd feel unconflicted about composing them. but since they aren't, it makes point free style more... involved.
Despite the presence of comp
and partial
, the use of anonymous functions is generally preferred.
Can you say more about it? I think I heard that partial
is "better" because it doesn't create a closure. Is #()
considered more idiomatic?
Rich is on record (several times) as saying he considers #()
to be more idiomatic than partial
.
partial
definitely does create closures -- look at its source.
Well, I figured it must create at least something equivalent (callable and holding the arguments)
partial
creates a closure with multiple arities -- since it doesn't know how many arguments your function takes (so it doesn't know how many are still "missing") so if you have a function that takes four or more arguments, you're going to be running via a variadic function using apply
.
If you use #(f arg1 %1 %2)
then you get a 2-arity closure and your function f
will be directly invoked with three arguments -- so you'll get IDE/linter support on the call of f
in terms of validating the arity, and you'll also get runtime arity checking when that closure is called. With partial
, you lose any IDE/linter support at the call site when writing the code, and you also defer the runtime arity checking (since you go through another call layer between the source code and the actual call site of your function -- and it might go through apply
, pushing the arity checking a little further away from what you wrote. All of which makes errors harder to spot and debug.
FWIW, when I got started with Clojure a decade ago, I was very enamored with comp
and partial
... but I hardly ever use them nowadays.
(I do still like juxt
tho')
So partial
seems to be a good fit for when you really want varargs, though? Like (partial + 10)
But in what contexts would you actually want a varargs (anonymous) function? I can't recall ever using %&
in a decade of production Clojure and I think I've only used partial
on a variadic function "by accident", i.e., when the actual call(s) of that expression are known arities.
Yeah, just grepped my code here: no instances of %&
in well over 100k lines of Clojure (including all my OSS work). I have just a handful of (partial apply =)
in test code (but that is not variadic, it takes a single collection argument -- so I could (and should) just use #(apply = %)
Across our entire codebase, we have just over 100 calls to partial
in just under 60 files.
Yeah, I can probably come up with an artificial example, but I guess you're right, it's not common at all
By contrast, we have well over 500 anonymous functions with #(..)
across nearly 200 files.
(I actually had to check the docs before even mentioning %&
for its existence; never used it in real life)
That being said, comp
is common when composing transformations as part of a transducer invocation.
I think I got it now. I was spoiled by lein
in that I didn't have to require
namespaces there before switching to them: I can just do it directly with ns
Your solution definitely works. Thanks!
One thing that bothers me though is why in-ns
works when you pass in a nonexistent namespace
Ahh, since I’m moving around so much and can’t bring a decent keyboard with me all the time, I’m kind of stuck with vim. But hearing your suggestion has led me to this https://thoughtbot.com/blog/writing-clojure-in-vim so I’m going to try to replicate your setup. Let’s see how it goes.
I want to reiterate on this topic after watching this presentation: https://lispcast.com/you-are-in-a-maze-of-deeply-nested-maps-all-alike-talk/ This guy is practically saying the opposite of what we talked about above: There is a lot of sense in using constructors and accessors (getters, setters), which in practice encapsulate your data. It is worth the extra code investment, because you gain the ability to decouple implementation details from depending code areas. It also makes Clojure tell you when you call a function which got renamed, in contrast to changing a key name. There's more to that than I said, especially when it's a good idea to use these abstractions. I recommend you watch this talk.