This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-11-28
Channels
- # announcements (1)
- # beginners (205)
- # calva (30)
- # cider (5)
- # cljdoc (25)
- # cljs-dev (2)
- # clojure (119)
- # clojure-brasil (5)
- # clojure-conj (7)
- # clojure-europe (2)
- # clojure-hamburg (7)
- # clojure-italy (14)
- # clojure-nl (2)
- # clojure-russia (13)
- # clojure-spec (79)
- # clojure-uk (58)
- # clojurescript (54)
- # core-logic (2)
- # core-matrix (2)
- # cursive (40)
- # datascript (2)
- # datomic (18)
- # duct (2)
- # emacs (14)
- # figwheel (3)
- # figwheel-main (7)
- # fulcro (30)
- # funcool (1)
- # graphql (10)
- # jobs (1)
- # juxt (13)
- # lumo (1)
- # mount (1)
- # off-topic (56)
- # other-languages (2)
- # pedestal (17)
- # powderkeg (2)
- # protorepl (2)
- # re-frame (10)
- # reagent (1)
- # reitit (7)
- # ring-swagger (10)
- # schema (2)
- # shadow-cljs (70)
- # spacemacs (13)
- # specter (4)
- # sql (9)
- # tools-deps (26)
@dpsutton @jstaab nice! that's occurred to me from time to time as a missing convenience. the downside to that succinct-map is that it's all-or-nothing since, unlike JS where each property is separated by a comma, in clojure maps the commas are ignored/optional. For that reason, this is what I came up with - basically just wraps hash-map
but with a special symbol for including short-hand values alongside the explicitly defined pairs:
@jesse.wertheim totally tangential, but why mapcat
and hash-map
instead of just into {}
?
very important reasons that have nothing to do with me just getting into a logic hole π
initially I did it a bit differently, such that you'd do (short-map :a 1 :b 2 (succ c d))
and it made more sense to use the sequence functions but yeah, into
definitely makes more sense with the way I ended up putting the usage
@jstaab actually it's a lot easier without into
since we're manipulating forms and syms
I suspected that, does this not work?
(defmacro short-map [& xs]
`(into {} ~@(mapcat
(fn [pair]
(if (= 'succ (first pair))
(mapcat #(vector (keyword (name %)) %) (second pair))
pair))
(partition 2 xs))))
Here's one that's very close to what you have:
(defmacro short-map [& xs]
(into {}
(mapcat (fn [[k v]]
(if (= 'succ k)
(map (juxt keyword identity) v)
[[k v]])))
(partition 2 xs)))
It just outputs the map with symbols and keywords. See this macro expansion:
(macroexpand-1 '(short-map :a 1 succ [b c] x))
{:a 1, :b b, :c c}
^ @jesse.wertheim if you're interested
ah well, if it wasn't the end of the workday I'm sure we could come up with something succinct and perfect π
double-mapcat works for me! there's some overhead due to the nested mapcat
but since we're just manipulating inputs in a macro that's not likely to be super relevant (though you could always use eduction
, at least, if you really wanted to optimize away the pointless caching)
Nice, very cool. I gotta look up eduction
. Arguably harder to read than the original though
the tldr on eduction
is that it's basically the same as sequence
but instead of a lazy-seq (which caches), it produces a sequable/iterable - really only useful as an optimization when you're producing something to be immediately consumed (making the lazy-seq caching pointless)
Hey, I feel like there is a macro or fn to do this. Is there a way to take a sequence [x y z] and a sequence of functions [fnx fny fnz] and returns the result of [(fnx x) (fny y) (fnz z)] ? I feel like what I'm doing is over complicating things. Thanks all.
Oh hey, (map 'apply [inc dec] [1 2]) ;; => (2 1)
, though I'm surprised the quoted form worked
first one looks cleaner than what I've got, also surprised if there isn't something for it though. Parsing a file and need to turn some fields to numbers with read-string, rest are just treated with identity
my guess is because it's relatively simple to implemented directly as shown in that first example using map
Yeah, just with 12 arguments it gets a little ugly with the %'s, and clojure is so beautiful, but it works and is much cleaner than my previous code. π thanks
ope, my mistake, the map there works without going up to %12. need more coffee, thanks. awesome
@joseph.guhlin my bad with mapping 'apply
, you're right that it doesn't do what you were looking for. I was just messing around, but apparently symbols are 1 or 2 arity functions, that act like identity of argument #2? What's going on there?
No worries here. Not sure what is going on with 'apply, but it is weird. ('yello inc 34) => 34 (just randomly typing a bit in the repl)
symbols are functions that when given an associative collection, look themselves up as keys in it
and if not found, use arg 2
(sym map not-found)
is basically same as (get map sym not-found)
ah yeah, a shortcut for get, just used to the : before them instead of a ' but they are the same
yeah, same as kw
('foo {'foo 10})
=> 10
('bar {'foo 10} 20)
=> 20
There is an example in the README https://github.com/plumatic/schema
(def StringListOrKeywordMap (s/conditional map? {s/Keyword s/Keyword} :else [String]))
Is there something beyond that which you are curious about?thank you @trailcapital π , i really need to learn how to look for stuffs π
If anyone has a moment, I'd love to hear any feedback on this https://gist.github.com/mseddon/c306bfc5421badb72b68f3b422b3fd4d
itβs fairly readable to me. is (merge-with + tl { expr 1 })
the same as (update tl expr inc)
? build-env
is a little terse maybe use a ->>
is there a paper youβre following for the algorithm? or something that explains it, Iβm interested
ahh, (merge-with + tl { expr 1 })
sets 1 if it doesnβt exists, update inc isnt the same, nvm
there's no real paper on the algorithm- what it's doing is finding all duplicate branches in the tree and allocating symbols for them instead
I've written a cheesy algebraic simplification algorithm, and i'm using that in combination with this to automatically expand some otherwise very tedious fixed size matrix library routines.
incidentally the simplifier https://gist.github.com/mseddon/dedf80760a8ec32a1a7d4e108e6fca12 tries to put expressions into a canonical form, so by running it first, the common subexpressions are more likely to match... e.g. (+ y x z) turns into (+ x y z) after the simplification process
idk if the helps but you might be able to write a more βsuccinctβ (read opaque) version of the simplifier using core.logic
Yeah, I've been looking at that- it has some potential I think. The simplifier itself would be perfectly "readable" in prolog, after all.
https://github.com/jonase/kibit does this, if you aint seen it
and https://github.com/halgari/build-your-own-logic-engine/blob/master/src/build_your_own_logic_engine/core.clj and https://www.youtube.com/watch?v=y1bVJOAfhKY
It would be interesting to know how much seasoned Clojure programmers are using various IDEs (and which IDEs) or are they mostly just hacking with some text editor + clj cli?
And are they using Leiningen or Boot or just clj cli + deps.edn?
And are they using a lot type hints?
Another datapoint:
Where I work (60-75 devs working in Clojure(script)) I believe most people are using intelliJ (70%), then emacs (~15-20%), then others. I am one of two people who use vim.
For all of our apps we use lein.
Wow! 70 developers working with Clojure. I'd like to be there. π
It's nice that I found this Clojure Slack community again. I don't feel so lonely with Clojure any more. π
http://blog.cognitect.com/blog/2017/1/31/clojure-2018-results
Emacs CIDER and IntelliJ Cursive are the by far the most popular.
Although deps.edn
is catching up, I bet leiningen
still is at the top.
Thanks. That was interesting to read.
@kari.marttila as one datapoint, my team uses IntelliJ and Cursive predominantly, as we have a number of different languages in use alongside Clojure. We're using Leiningen and only use type hinting when our linter complains about reflection around Java interop.
Hey guys, I'm new to Lacinia
I'm having a problem that lacinia queries and mutations is returning me clojure maps
I have to define a type on lacinia's schema.edn to return me every key to a graphql like output?
query:
{ sendSms(phone: "+5552999991000") }
response:
{
"data": {
"sendSms": "{:message \"SMS token was sent\", :success true, :uuid 109507373}"
}
}
{:queries {:requestAccessToken
{:type String
:args {:uid {:type (non-null String)}}
:resolve :query/request-access-token}
:sendSms
{:type String
:args {:phone {:type String}}
:resolve :query/send-sms}}}
hm. (merge-with op m0 m1) should preserve the order of m0, IF m0 is an array-map, right?
for 57 iterations, new keys are appended onto the END of m0, yet the next one inserts the value onto the beginning...
https://clojure.org/reference/data_structures#ArrayMaps
Note that an array map will only maintain sort order when un-'modified'. Subsequent assoc-ing will eventually cause it to 'become' a hash-map.
It should be closer to 8 or 16 keys, if I remember correctly. Perhaps the keys you are using just happen to be in the hash-map order?
If you want a map that preserves key insertion order, there is an ordered-map data structure library, linked to from the Clojure cheatsheet in the "Sorted map" section: https://clojure.org/api/cheatsheet
There are also sorted maps that sort the keys according to a comparison function you provide, built in.
But note that the return type of (merge m1 m2) will be the type of m1, not m2, so if you call a function with a map m that inside of that function it calls (merge m1 m2) and returns the result to you, you won't get your order preserved.
There is a sorted-map
that provides key order, not insertion order. I don't see an ordered-map
.
Oh, ordered-map
is in a separate project, I see now.
for now I just split it into a pair, a vector of keys and a map, basically I wanted to build a map but also collect a post-order walk order
good to know though, since anything beyond this smaller use-case would become a pain quickly.
{:queries {:requestAccessToken
{:type String
:args {:uid {:type (non-null String)}}
:resolve :query/request-access-token}
:sendSms
{:type String
:args {:phone {:type String}}
:resolve :query/send-sms}}}
my schema edn.clj
Sorry, I need to go, do ask this also in #graphql and you will get a more focused audience.
mistook the schema.edn
I'll change here
@orestis changed, sorry
Is there some simple way which doesn't impact performance too much to automatically log the name of the function. At the moment I add the function name manually like: (deftest get-products-test (log/debug "ENTER get-products-test") ... ... which logs: 2018-11-28 20:43:55 DE [nREPL-worker-5] DEBUG s.domaindb.domain-single-node - ENTER get-product-groups I'm using clojure.tools.logging with logback.xml configuration.
I have a spec that looks like this:
(s/def ::my-tuple (s/tuple keyword? keyword?))
Lets say I have a function that maps over my tuple and changes the items from strings -> keywords
(defn my-func
[tuple]
(map keyword tuple)
=> (my-func ["pos-1" "pos-2"])
=> (:pos-1 :pos-2)
The thing is, the map is going to return a lazy-seq
which seems to mean that ::my-tuple
will not be valid when passed (:pos-1 :pos-2)
. My question is that s/tuple
seems to be the right thing to do for this spec (the collection is fixed position), but because the map in the function returns a lazy-seq
, it will fail the spec. Would this suggest that I just have to force the lazy-seq back to its original structure and we are good or maybe I am thinking about this wrong?FYI - my solution is to use mapv
over map
, but I wanted to know if there are other ways to consider this.
I haven't tried it but maybe (s/coll-of keyword? :count 2)
would work, if there is no need to return a vector.
I figured that tuple would be more appropriate as it communicates that its fixed position?
Well, if its a two tuple, I donβt see much value in keeping it in a seq
unless computing those two values is a performance disaster π
Looks to me like tuple is mainly useful for mixed types. I'm not sure why it has to be indexed.
(you know, if you had a prime factorisation tuple or something)
map returns a sequence, so it is wasteful to convert to a vector when this is not needed.
not arguing that, but it is not super wasteful with a 2-tuple
yes, maybe it would be simpler, depending on the code that's using it.
Still it seems strange to me that s/tuple implies indexed?.
But maybe that's tuple's intended meaning. The doc just says "positional".
So if you don't want indexed you could also use s/cat.
have you tested this assumption that a two element vector is more wasteful than a two element lazy-seq? given the map transducer you don't need to produce the seq first
(and mapv does exactly this, uses the transducer, thus not creating a seq)
Cool!
Good point!
so - actually testing it, mapv on two items is nearly twice as fast as map on two items all else being equal
(ins)user=> (crit/bench (doall (map keyword ["a" "b" "c"])))
Evaluation count : 119700720 in 60 samples of 1995012 calls.
Execution time mean : 349.709449 ns
Execution time std-deviation : 51.209511 ns
Execution time lower quantile : 317.752122 ns ( 2.5%)
Execution time upper quantile : 501.633441 ns (97.5%)
Overhead used : 1.848273 ns
Found 7 outliers in 60 samples (11.6667 %)
low-severe 1 (1.6667 %)
low-mild 6 (10.0000 %)
Variance from outliers : 84.1226 % Variance is severely inflated by outliers
nil
(cmd)user=> (crit/bench (mapv keyword ["a" "b" "c"]))
Evaluation count : 331083300 in 60 samples of 5518055 calls.
Execution time mean : 184.412115 ns
Execution time std-deviation : 6.374979 ns
Execution time lower quantile : 176.007592 ns ( 2.5%)
Execution time upper quantile : 202.071163 ns (97.5%)
Overhead used : 1.848273 ns
Found 3 outliers in 60 samples (5.0000 %)
low-severe 3 (5.0000 %)
Variance from outliers : 20.6437 % Variance is moderately inflated by outliers
nil
Maybe a bit more of an architectural question, I have this codebase now that has quite a lot of protocols, and I end up writing similar implementations for those protocols in the most usual cases. Naturally I want to remove that duplication. In Haskell, there is deriving for generating this boilerplate for the most common cases, is there something similar in Clojure?
I'm confused, different functions across different protocols share the same implementation?
no, many records implementing the same protocol in similar ways
(but not always!)
Yeah basically
yeah that
I dont quite like that tbh
yeah my haskell is leaking here
:β)
Right, if you want explicit default impl, then just do that. Have a function that isnthe default impl and explicitly extend each type you want with it
I think I will make my protocols extend a deriving
multimethod with some default impl
so I can just do like (deriving MyProtocol MyType)
hmm yeah thats cool!
I've also kinda solved a similar problem in the past by passing in the implementing objects as parameters to the user-facing type
(Deriving is the way that haskell calls it, if you have some datatype in Haskell, you can say deriving (Show)
, giving you some default toString method)
Hum, I guess you could write something similar. Though I get the impression you might be overcomplicating things and trying to do a heavy type driven style of programming, which isn't Clojure's forte.
yeah, will try to reduce that going forward
I guess you need to ascertain which way your similarities are. is this something that could be solved by passing in the implementation at object creation, or something that belongs in the hierarchy?
Then you could write a macro that takes many types and extend them all with a given proto + protocol map
Or, another macro you could write would take a type, a protocol and a protocol map and then a list of "overriding" method to function.
right
And inside that, if there is an override you just assoc it into the map before extending. Something of that sort.
all of this is kind of predicated that all of your similarities are similarities in an is-a?
kind of way
I am kinda lost on what you were trying to say lilactown, could you elaborate?
So you'd do (deriving MyProtocol MyType default-MyProtocol-impl (some-method [this a] ...))
it's the whole composition vs. inheritance thing. I try and be careful, when I see that I have a bunch of code that is similar, to figure out if it's because they all derive from the same abstraction ("this is a car") or if they are composed of similar utilities ("it has an engine")
for instance, here's a rough approximation of how we wrote our data fetching layer in one of our applications:
(defprotocol IRemote
(read! [conn query config])
(write! [conn mutation config]))
(defprotocol ICache
(read [store query config])
(write! [store mutation config value])
(reset! [store]))
(defprotocol ISource
(read! [source query config])
(write! [source mutation config]))
(deftype SourceImpl [remote cache]
ISource
(read! [this query config] ;; implement standard cache check / fetch from remote
)
(write! [this mutation config] ;; implement standard update cache / modify remote
))
right so thats composition
90% of our data sources were REST endpoints that just used a really simple way of caching requests in an atom. so we have a DefaultCache
implementation, and write the implementation of the IRemote
to hit the specific REST endpoint we wanted
Right that makes sense
anyway, it might not be at all what you're looking for. I was just prodding to see if composition might fit your problem better
yeah come to think about it, I have some composition as well
but I believe that larger parts of my appliance call for a more traditional inheritance type of design
Or⦠inheritance is the wrong name
Well it kinda is
Thanks all for pitching in btw, you guys rock!
Make my clojure learning curve less steep
I never want to say never, because there's always a time and place for anything. But in general I'd advise you first try to use existing data-structures and functions grouped in namespaces. If that really doesn't work, I might look into protocols and records, no hierarchy. If you need a hierarchy, defmulti might be more suited, since protocols are not intended for that. And finally, I'd look into deftype.
Maybe I went a bit far with protocols, but I think that there is certainly value in it for me right now
Like personally, though it could depend on more context, but I feel @U4YGF4NGM example could have just been defined using functions
I have been driven down the macro inheritance road before and it was not a fun journey. I suggest extracting the common behavior to var fns and to call them as desired from the various protocol impls.
sounds reasonable
@U0K064KQV it was all in the effort to establish a basic notion of fetching data from an external data source with a cache. There ended up being multiple implementations of that based on the kind of data source we were using.
e.g. we wanted to use GraphQL, so we dropped in apollo-client
to the project to handle batching, caching, etc. and installed it using the Source protocol. Then any part of the application could read from it just like any other data source we had
I really don't want to make implications without knowing the full context by the way, context is key for these design choices. In the end if it turned out well for you, that's all the proof needed.
It does sound like the data source abstraction can be useful, if there are a lot of different ways to fetch data from different sources it totally make sense.
As an alternative, just to show what I mean, you can create a map with a source-type and url and possibly more or less info. And just have a read and write multi-method over source-type.
Btw, extend is treating me well now
Ya, one thing coming from inheritance based languages is you get used to using inheritance as a tool for code reuse. And in Clojure code reuse is almost always just done by sharing the same function.
yeah is nice because I can now have multiple default method sets
And that works well in Clojure because the dynamic types make it so all functions are generic in nature. So duck typing applies, as long as you give it compatible things it'll just work.
Otherwise, I may just write a multimethod deriving
that does basically just that
Hello. What are the most common libraries with an HTTP client and to parse JSON, please? Ideally ones that work with deps.edn, too.
And from the same author https://github.com/dakrone/clj-http
i use aleph https://aleph.io and jsonista https://github.com/metosin/jsonista
clj-http works well @peter.kehl
Thank you @ghadi