hey friends, is there a way to rewrite clojure code in a file as if you're writing a macro, but separate from executing that code? I have a couple files with hundreds of map literals I need to update, and stepping through them one by one will be a lot of work vs writing a transformer once
I know I could use string parsing, but that's much more error prone vs being able to say "is this a map? do x. is this a function? do y"
rewrite-clj
might help: https://github.com/xsc/rewrite-clj
cool, i'll check that out! thank you
I think I'm looking for idiomatic application of apply
.
(def out-of-scope [:color :size :shape]) ;; some keys i don't care about
(defn summarize-data [body]
(dissoc (json/parse-string body true) out-of-scope) ;; what I want to work
(defn summarize-data [body]
(dissoc (json/parse-string body true) :color :size :shape) ;; what actually works
(apply dissoc (into [] (json/parse-string body true) out-of-scope))
or something like that?
Hi all, does anyone know of a good parser to parse .data file extensions?
(apply dissoc (json/parse-string body true) out-of-scope)
will do the trick!
you never need to create an extra collection for apply:
user=> (apply + 1 2 [3 4 5 6])
21
so (apply dissoc (json/parse-string ...) out-of-scope)
sufficesHi! I'm looking into memoizing a function, but have two questions I could use your advice on:
• The function I want to memoize takes instances of a deftype
class (instances? what's the right name here?) as a parameter. If the field of those instances are the same, the result of the memoized function is the same too, but the memoize function seems to (as expected) to cache based on the identity of the instance. Is there an elegant way around it? I could take the field out of its instance, pass it into the function and then reinstantiate the deftype, but that doesn't seem very elegant or efficient.
• Does the core memoization out of the box work in a parallel setting?
this extension has been used by multiple applications, which one do you need to handle?
memoize "works" in parallel situations, but makes no promise that the function is only called once
does clojure.core/memoize
use weak or strong references? Wondering if cacheing instances as opposed to values might be a bad strategy
memoize uses =
, it will use a cached value if the arg list is =
@dpsutton instance check is just the default implementation of =
for deftype
, you could override the hash
and =
functions for the type for a different behavior, or easier just use defrecord
which uses value rather than instance equality
ah neat. cool
(ins)user=> (deftype Foo [a b])
user.Foo
(ins)user=> (= (->Foo 1 2) (->Foo 1 2))
false
(ins)user=> (defrecord Bar [a b])
user.Bar
(ins)user=> (= (->Bar 1 2) (->Bar 1 2))
true
you can implement the hashCode
and equals
methods on Object
to change deftype's equality check
(cmd)user=> (deftype Baz [a b] Object (equals [this other] (and (= a (.a other)) (= b (.b other)))) (hashCode [this] (hash {:a a :b b})))
user.Baz
(ins)user=> (= (->Baz 1 2) (->Baz 1 2))
true
I feel like there was a reason I chose deftype over defrecord, but now I'm not so sure... Gets a tiny bit more complicated though, my deftype has two parameters, one matters, the other one is (within one run of the recursive function that I'm memoizing) constant and big, so I'd like to cache based on only the first one. In that case, overwriting hashCode
and equals
seems the best approach?
was just going to ask some pointers on how to approach this, awesome, thanks!
you should be able to adapt my example above (I use clojure.core/hash and an inline hash-map to leverage the logic clojure already has)
more than good enough for me 🙂
beware that weird hashCode / equals behavior can combine to make spooky bugs when using your type as a key in a hash-map or member of a set
(or as an argument to a cached function...)
The best is just use a map, and cache only on the but you want to cache on
I'll keep it in mind, but should be fine as it's not used in too many places. The opposite did trip my up before when I switched from basic vectors to deftype, not realising every new deftype (even if they had the same values) would create a new entry in a hashmap
you mean write my own wrapper memoization function that uses a map and takes the value I ant to cache on?
Yes
If you are not sure why you chose deftype over defrecord, think odds are you don't need to be using either
It looked the cleanest way to obtain multiple dispatch, as every time I introduce a new type I need a handful of functions implemented. I guess the alternative would be to use defmulti, but grouping them per function rather than per type seemed quite messy
maybe I'm trying to impose object oriented coding on a language that should be used differently
I'm trying to speed up a workflow where I'm required (currently) to paste some JSON into a field. However,
(require '[cheshire.core :as json])
(json/generate-string {:name "This application was added via Postman"})
;; => "{\"name\":\"This application was added via Postman\"}"
Can I get rid of the escape coding expediently?Note: I'm working at a repl that's sending tap data to reveal
maybe I just should string/replace parts :thinking_face:
@mathpunk (println (json/generate-string {:name "This application was added via Postman"}))
will print it in copy'n'paste-friendly text
Oh it IS there -- I thought that I would get nil
and, I do, but it is present in the repl window
the string i mean
Right. The quoting is coming from using pr-str
/ prn
in the REPL (and in Reveal). println
produces different output (but also returns nil
).
btw, thanks for that (tap> ...)
demo -- I think I'm using it in a pretty ham-handed way, but even still it's a nice experience over what i was doing before
I love tap>
🙂
why not a protocol, implemented by each type?
if you in fact need types
what's the rationale for using keywords, and not strings, to model an enumeration? for example (def states #{:active :inactive :pending})
instead of (def states #{"active" "inactive" "pending"})
?
or are keywords and strings idiomatically interchangeable, here?
both are fine
keywords are slightly more flexible in being invokable as a function
keywords implement IFn
and look themselves up in collections when invoked. they also allow for namespaces. they can often cause annoyances when serialized into a db and require converting from string -> keyword and such
thanks!
Ah, thanks for the reply, but this seems to be a custom layout created by another engineer on the team. I'll need to write my own parser.
That's what I did, I created a protocol:
(defprotocol GameState
(finished-game? [this] "Tests if game is finished")
(reward [this] "Gives reward for state")
(get-available-actions [this] "Gives all available actions from this state")
(apply-action [this action] "creates new state through action"))
And then implemented it with a deftype, for example:
(deftype TicTacToeState [rewards state-vector]
GameState
....
The idea being that different games would have different implementations. Did this months ago, so forgot the exact reasons why, but hope that makes sense?
Thanks for all the help btw!defrecord
is good for map-like immutable values that you want a protocol implementation for, with all of the fields 'exposed' as part of the value.
For deftype
, you get to pick how you want clojure.core/=
and clojure.core/hash
to behave, so it becomes easy to create confusing situations where your definition of =
doesn't make much sense for the type, and/or your implementation of hash
isn't consistent with your definition of =
.
Since you mentioned wanting memoization to work on one field of a deftype object, but ignore another field, consider that memoization is not going to work for you, if the function you are memoizing ever uses the value of the field to determine the return value, but your =
implementation ignores it.
A simple-minded similar example would be: imagine trying to create a memoized version of the function meta
on Clojure collections. =
between two Clojure collections ignores any metadata on the two collections, so would use the same memoization cache entry for two collections with different metadata. =
says true, but the function meta
explicitly uses the thing that =
ignores in determining its return value.
The function you are memoizing may also be correctly ignoring the field that you want =
to ignore, and if so, that is possible to make work.
🙏
All of that was very helpful, I feel like I should go back to clojure for the brave and true to get the familiarize myself with the differences between protocol, record and type 🙂
In the end I took the final solution from this blog: https://kotka.de/blog/2010/03/memoize_done_right.html And repurposed the "naive implementation" a bit for my use case, seems to work