clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
2021-03-27T03:17:39.022Z

I don't think you can do a lot of typing with spec no? Cause the predicates rarely map to a logical type?

2021-03-27T03:18:03.022200Z

It would have been nice if maybe spec had done something like: type + pred

2021-03-27T03:18:40.022400Z

So like the broader type + a refinement pred

2021-03-27T03:19:24.022600Z

And if I understand Typescript properly, a hord of devs jumped on it and retrofitted types for a ton of existing JavaScript. I mean this could happen for core.type as well but did not

2021-03-27T03:20:16.022800Z

Also personally, not sure how useful types would be without having abstract data types as the standard compound data-structure.

2021-03-27T03:21:16.023Z

All maps would still just be of type map. So the issue of like what keys are in this? Am I using a key that doesn't exist? Etc. Would still be there

dpsutton 2021-03-27T03:26:32.024Z

I think type script has row type polymorphism that can handle this. Not sure what it’s like in practice though

2021-03-27T03:52:30.024200Z

I think row type just means that any superset is automatically valid, even if not explicitly defined as is-a relationship.

2021-03-27T03:52:55.024400Z

But it would still require a definition of the fields on the type no?

2021-03-27T03:53:53.024600Z

Like I think you can do row polymorpism with Clojure records.

dpsutton 2021-03-27T04:09:56.025500Z

Yes that’s my understanding. Don’t know if core typed has this kind of analysis but would love it

2021-03-27T04:37:40.025700Z

Ya I think that's correct. Basically it just let's you say this function works on any type which have at minimum these keys of some given types. But it still requires the type checker to know what keys of what types the object has. So for it to work with maps, it would need that somehow it could infer for any given map at any point in the program what are the keys it would now contain and what types those keys would be of. Which seems like really hard to do. And that ignores that it's possible to have a dynamic key too, like where the key name is coming at run-time from external input

2021-03-27T04:38:57.025900Z

But records would work well with it. Cause records already force you to define the mandatory keys, you'd only need to also define their types and you could then do row polymorpism type checking, if your variables and function arguments specific row type constraints.

martinklepsch 2021-03-27T13:35:34.028700Z

I just wrote this to cycle through a list of values in a stateful way, would be curious if others have better ideas on how to implement this. I think there might be a way with keeping a reference to a lazy seq but I don’t know if that’s a good idea and also I’m not sure how that would be done

(defn stateful-cycle [xs]
  (let [state (atom (shuffle xs))]
    (fn []
      (let [r (first @state)]
        (reset! state (conj (vec (rest @state)) r))
        r))))

1👏
2021-03-29T23:57:01.073100Z

slightly more compact version

(defn stateful-cycle3
  [xs]
  (let [state (atom (cycle (shuffle xs)))]
    #(first (swap! state rest))))

2021-03-27T13:43:03.028800Z

cycle is built into Clojure core library, and returns infinite (lazy) sequence that cycles through its input collection values.

2021-03-27T13:44:19.029Z

If you want a stateful function that returns the elements in a cyclic order on multiple subsequent calls, you could put the return value of cycle into an atom, and call first and rest / next on that sequence on each call.

2021-03-27T13:45:47.029200Z

There would be no difference in behavior from the implementation you show, but an implementation using cycle would probably be more efficient in CPU cycles than yours, where you are building a vector of all elements of the cycle as a vector using vec on every call.

2021-03-27T13:47:41.029400Z

The implementation you show is not safe to call from multiple threads, since it reads the current atom value twice, and modifies it once, with no synchronization. If you needed something that could be used from multiple threads correctly, you could make a pure function that takes the current value of the cycle, and returns the next value of the cycle, and call that function on the atom via swap!

2021-03-27T13:52:56.029600Z

Here is an implementation that should be safe to call from multiple threads, and uses cycle for better efficiency (should be O(1) time per call, rather than O(n) where n is the number of elements in xs):

(defn stateful-cycle2 [xs]
  (let [state (atom (cycle xs))]
    (fn []
      (let [next-cycle (swap! state next)]
        (first next-cycle)))))

2021-03-27T13:53:59.029800Z

I did not include the call to shuffle like yours does. In terms of API, it seems better to me to allow the caller to make the decision to shuffle the sequence, or not, e.g. by calling (stateful-cycle2 (shuffle xs)) if they want randomization of order, or call (stateful-cycle2 xs) if they do not.

2021-03-27T13:54:36.030Z

"better" in the sense that the function is useful in more situations, because it does not force the shuffle behavior.

martinklepsch 2021-03-27T15:36:36.037100Z

Wow Andy, that’s great feedback all around! Good point about shuffle as well

martinklepsch 2021-03-27T15:50:20.037300Z

thread safety wasn’t really a concern since it’s JS but I like how your usage of next just simplifies the whole thing

chrisn 2021-03-27T16:58:03.037600Z

Another jvm-specific formulation that may be interesting:

(let [state-iter (.iterator ^Iterable (cycle xs))] 
  #(locking state-iter
     (.next state-iter)))

seancorfield 2021-03-27T19:18:16.038800Z

Thank you @slipset:

(! 1012)-> clj -Sdeps '{:deps {org.clojure/data.json {:mvn/version "2.0.2"}}}' -A:1.11
Clojure 1.11.0-alpha1
user=> (require '[clojure.data.json :as json])
nil
user=> (json/write-str {:a 1 :b 2})
"{\"a\":1,\"b\":2}"
user=> (json/write-str {:a 1 :b 2} :value-fn (fn [_ v] (inc v)))
"{\"a\":2,\"b\":3}"
user=> (json/write-str {:a 1 :b 2} {:value-fn (fn [_ v] (inc v))}) ; Clojure 1.11 calling style
"{\"a\":2,\"b\":3}"
user=> 

seancorfield 2021-03-27T19:18:40.039100Z

Now I can clean up a bit more code at work! 🙂

slipset 2021-03-27T19:19:38.039500Z

You’re welcome!