This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-12-05
Channels
- # adventofcode (419)
- # aleph (8)
- # aws (6)
- # beginners (148)
- # boot (9)
- # cider (24)
- # cljs-dev (37)
- # cljsjs (8)
- # clojure (134)
- # clojure-android (6)
- # clojure-brasil (15)
- # clojure-dev (8)
- # clojure-dusseldorf (2)
- # clojure-greece (67)
- # clojure-italy (8)
- # clojure-japan (3)
- # clojure-russia (3)
- # clojure-spec (8)
- # clojure-uk (13)
- # clojurescript (54)
- # clojurex (6)
- # cursive (5)
- # data-science (12)
- # datomic (15)
- # defnpodcast (11)
- # emacs (25)
- # fulcro (95)
- # graphql (3)
- # lein-figwheel (1)
- # leiningen (27)
- # luminus (1)
- # lumo (6)
- # mount (2)
- # off-topic (112)
- # om (3)
- # onyx (24)
- # perun (3)
- # re-frame (20)
- # reagent (1)
- # reitit (2)
- # ring-swagger (13)
- # rum (10)
- # shadow-cljs (45)
- # spacemacs (24)
- # sql (2)
- # unrepl (78)
- # yada (1)
Thanks @tbaldridge @smith.adriane @hiredman for the excellent feedback. I'm just not sure I get it regarding the comments about folds or fold-like reducing. Am I missing something really deep here?
https://clojure.org/guides/destructuring#_namespaced_keywords Why does :keys
use a keyword (`:person/name`) for destructuring namespaced keywords rather than a symbol (`person/name`)? Both work, but given this guide it seems the former is what you're expected to use.
@dominicm From the same page: "The :keys
key is for associative values with keyword keys, but there are also :strs
and :syms
for string and symbol keys respectively."
I believe he's talking about the distinction when using :keys
destructuring.
(let [{:keys [:person/name b]} {:person/name "Name" :b 2}]
[name b])
=> ["Name" 2]
vs
(let [{:keys [person/name b]} {:person/name "Name" :b 2}]
[name b])
=> ["Name" 2]
The distinction between using :person/name
as a keyword or person/name
(as a symbol)
Why the first seems encouraged if both work and the second seems more in line with how we destructure unqualified keywords.
Symbols are preferred in my opinion. Either should work.
I see, thanks!!
@dominicm I think it might be related to auto resolving keywords, possibly? Using ::some-ns/id
would work only if some-ns
was available in that scope, but using symbols some-ns/id
would be happily accepted either way.
alias resolving keywords is probably a good reason. I wonder if symbols are supposed to work? e.g. the same way that (ns (require …))
worked until spec came in and caught everyone out.
That's a good question. I went with the flow of always using keywords according to what's in the available documentation, but I agree that it introduces a somewhat odd distinction.
Feel free to file an issue or a pr! https://github.com/clojure/clojure-site
:keys destructuring intentionally takes keywords to support the use of autoresolved keywords (either :: or : with an alias)
Symbols are preferred though if you’re not leveraging those features
Symbols can be either namespaced or not - the name part of the symbol is always what the bound local name will be
on the topic of autoresolved keywords, is there a jira ticket for being able for namespace aliases for non-existing namespaces?
You mean as an enhancement?
@alexmiller I'm not familiar with :
being used for autoresolved keywords, what do you mean by that?
Sorry I meant :: with an alias. InsufficientCoffeeException
That's okay! That brings me onto my next question then, does that mean the guide is wrong in suggesting the use of :person/name
and instead person/name
should be preferred?
Either is valid, I’d say symbol is preferred
Usually we use symbols in :keys (as they then match the created locals)
With qualified, it’s a little muddier
Yeah, until I knew about autoresolved keywords, the answer was very obvious. Now it feels like there's a special exception in :keys
.
Re the enhancement, we have considered it (I actually implemented it) but Rich doesn’t want to change current state, however he has some ideas for an alternative for managing keyword aliases specifically
@alexmiller I'm curious, could you give a hint as to what Rich would like this to look like?
Nope, I don’t know
what would you guys call this function?
(defn update-conj-vec
"Takes map and adds v to the collection at k. If value at k is currently nil v
is wrapped in a vector and set as k's value."
[m k v] (update m k #(if (nil? %) [v] (conj % v))))
@triss I've found that when I need that kind of function, I'm usually doing something wrong.
@dominicm care to elaborate? I’ve got some maps with lots of lists in that need adding too…. what gives you that feeling?
@triss Not that you asked, but I'd write that like so:
[m k v]
(update m k (fnil conj []) v)
"Takes a map and conj's v to the collection at k. If the value at k is currently nil, a vector will be used as the collection type" is how I would word the docstring, the "how" of wrapping the v is too precise.
@triss however, if you're adding values to lots of keys, perhaps two generic functions would work together better? (def vec-conj (fnil conj []))
and a function to update-keys
for updating many keys with the same fn.
user=> (def vec-conj (fnil conj []))
#'user/vec-conj
user=> (defn update-keys [m ks & args]
#_=> (reduce (fn [m k] (apply update m k args)) m ks))
#'user/update-keys
user=> (update-keys {:k nil :v [1 2] :c (list :a)} [:new :k :v :c] vec-conj :new-val)
{:k [:new-val], :v [1 2 :new-val], :c (:new-val :a), :new [:new-val]}
As I was toying around 🙂(let [s (lazy-seq
(println "Called!")
(cons :foo nil))]
(first s))
Called!
=> :foo
How could first
be lazy?
well, Clojure has eager evaluation semantics so it’s not lazy in that sense
it just lays around all day waiting for something to happen, psh
you have to sufficiently motivate it. otherwise it just prints nah
and dies in constant time
@chris you could also try http://eta-lang.org/ if it's needed to run it on JVM
Is trampoline
's performance good?
There's an JavaScript <https://taylodl.wordpress.com/2013/06/07/functional-javascript-tail-call-optimization-and-trampolines/|article> (Clojure trampoline
implementation seems to be similar) and it says:
>The trampoline implementation has essentially the same number of function invocations as the traditional recursive implementation. A TCO would eliminate these function invocations.
>We’ve traded the work of creating stack frames with creating function bindings. Both operations consume time and memory. TCO doesn’t create function references due to its modifying the function implementation in place to transform the recursive
implementation to a loop.
So it seems that trampoline
isn't a substitute for TCO.
while it is right that the number of function invocation stays the same, the big difference is that those stack frames don't exist in memory all at once but they exist one at a time
I see. Thank you, @bronsa!
@dpsutton it's interesting, on the x86 the place the code jumps to on a return is simply a pointer on the stack. So you can implement other models that patch up that value to do stuff like TCO or continuation passing. Sadly this is also fairly insecure, so the JVM abstracts all that away, and doesn't give you control over the return address.
heh, and these days, with branch prediction, procedure calls are practically free 😄
sure, but the compilation strategy in the paper generates code where calls are just a jump without a pushed return address unless required (looks like cps or a precursor), of course it predates worrying about the x86 calling conventions, because people were still arguing about if subroutines were a good idea
> Interprocedural Specialization of Higher-Order Dynamic Languages Without Static Analysis This looks like a promising avenue for optimizing functional languages like Clojure
same works for downgrading
i.e. lein update 2.5.0
will up/downgrade to that version
Does anyone recommend a library for writing isomorphic data conversions/bijections?
Basically being able to write one implementation to go from type a -> type b
, and getting type b -> type a
"for free"
is that even possible? it sounds kinda like https://github.com/nathanmarz/specter
where you can zoom in on part of a datastructure, perform transformations locally, and then zoom out
@quest, clojure prefers data (hashmaps vectors) instead of types, so I'm not sure this applies?
there are a lot of libraries that do things like define some base conversion functions, then create a graph of all the types, then use the graph and base conversion functions to generate the rest of the conversion functions
https://github.com/dm3/clojure.java-time/blob/master/src/java_time/graph.clj is an example of something like that
@smith.adriane possibly, and specter seems nice, but not quite it
@tbaldridge Clojure doesn't emphasize types, but they're still implicitly there. You'll see something like (defn a->b [a] ...)
, which (by convention) is a typecast from a to b
@hiredman I saw https://github.com/twitter/bijection which seems to fit. Know of any others?
@quest but what types are you trying to convert. If a and b are records, then (map->b a) is automatic
as is (map->a b)
I have two different representations of the same thing. I want to write a single set of rules that goes from a to b, and get the reverse function (b to a) automatically
That's what I'm getting at, why do you have two representations of the same thing? Why not store them in one format?
Keep the data in hashmaps, or even records, and then stuff just works.
also - it really depends on what data transformation you are talking about, it’s provable that you can’t write a general function for such conversions so there must be other constraints implied here
Because the things in question are events, and the "business event" looks different from the event that's sent out to the external event service. There's no magic solution of "use the same format" here
that sounds like a data modeling problem. Chances are that with namespaced keywords and hashmaps your required transformations would be quite minimal
{:event/timestamp ...} can exist in any hashmap that's an event with a timestamp
and if you have {:service.event/timestamp ...} then its a question if you really need to discriminate between a service event timestamp and a ui event timestamp
what does an "event" look like quest? I think i follow you and i see what you are trying to do
I'm saying all this because the last time I needed a library like this I was in a C# app where a good 40% of all our code was nothing more than type conversions. Moving to Clojure removed the need for that.
sounds like there's a notion of "add a customer", and this app calls it create-customer
and some other apps call it customer-create
OK -- for instance, our business event contains various fields.
{:foo-id "abcde"}
The external service we publish them to supports "custom fields" via a facts
map.
{:facts {:foo-id "abcde"}}
All I want a way to specify this transformation that will work "forwards and backwards"
I can handroll this easy enough, just figured there was a "declarative" way to specify these transformations. I notice someone made https://github.com/gfredericks/schema-bijections which is the same idea
it’s not a standard or built in thing - that lib is the closest thing I’ve seen
fair enough. I can kinda imagine a way to do this via transducers if they each one in the chain was "reversible", but never seen that either
I misunderstood, thought you wanted to describe a one way transition and have the reverse defined automatically
clojure has no way to define, declare, or detect reversibility
yeah -- no way you could do this "automatically" without also having definitions of what (and how) things can be reversed
though if you limit yourself to purely isomorphic translations (and your library understands that), then it could generate the reverse for you
I wouldn’t be surprised if the haskell or idris compiler did such things, but clojure’s compiler doesn’t even aim to be clever like that
that’s true - you could make your own declarative language for transforms
might be a fun thing to try in eg. core.logic
hmm... that's true, and finding out how to mesh this with specs would be an interesting (and relevant) challenge for me. good idea 🙂
having known gfredericks for a while, my instinct when something he wrote comes up is that either I’m on track of something very clever and useful or something very clever and … perverse
(known online that is, just hung out with him at a couple conferences)
haha - I’ll stop now
there’s a lot of overlap with what you’re describing and specter. basically you have a bunch of reversible operations. the library has a specific use case in mind (manipulating immutable nested data structures), but you could in theory have navigators that are just reversible operations (i think)
i’m not saying it’s a good fit for what you’re doing, but you may be able find/borrow inpirations
didn't even consider that. Specter does have a lot of nice stuff going on under the hood, will also take a look at that
it’s clear that you could use select
and a path to go from one type to another
not sure if you could use transform
to automatically do the reverse given the same path
(a path being a list of navigators)
which you would be repurposing as reversible operations
don't know enough about Specter -- but worst case, as long as there's a set of operations, I can manually denote which ones are reversible (and how). whether that ends up being an extension of specter, core.logic, or a different library entirely... we'll see
seems cool