Code style: how do you order map keys?

2019-09-18 Thread Yuri Govorushchenko
I usually try to organize related things in different files in the same 
order to ease code reviewing, reading and writing new code. For example: 
order of protocol method declarations, their definitions in records and 
unit tests. And I was wondering what's the best way to order keys in maps 
and specs.

The simple rule could be to always sort keys lexically, so that the order 
stays the same across files and there's no additional thinking needed when 
adding new keys.
But with `s/keys` it's not that simple since `:req`/`:req-un` can break the 
consistency across the files. E.g. during spec definition the correct 
lexical order is `:req-un [:a/x :a/y ::d]`, but during "usage" it will be 
different: `{:d ... :x ... :y ...}`.
The lesser drawback of this approach is that some human-friendliness is 
lost, e.g. keys `host, port, db, user, password` must be ordered as `db, 
host, password, port, user`.

Do you try to maintain the same order of map keys (including keys in specs) 
across the files? If yes, what rules do you follow?

Thank you.

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/clojure/d29d4e36-4dee-4bd6-9347-7fa397103f5e%40googlegroups.com.


Re: Keys spec with :additional-keys boolean option

2019-03-04 Thread Yuri Govorushchenko
I find closed keys specs generally easier to work this (e.g. it protects 
from cases when I forget to spec some new data returned from the function). 
In my current project the number of open keys specs is ~1.5% and they're 
are for entities where the shape is really unknown beforehand. At the 
moment my rule of thumb is to start with strictest specs possible and then 
loosen them based on the feedback from code.

So far I can't recall to have any painful breakages described in 
Speculation talk. I haven't analyzed why, maybe eventually I'll stumble 
upon such case or this may be due to the fact that I have control over all 
the APIs and consumer code in the project.

Here's my take at adding `:disallowed-keys` into the `explain-data` of the 
closed spec (see `speced-keys` macro): 
https://gist.github.com/metametadata/53a847cd3b02056e8e4c124e63d9ae5a:

(s/def ::req-field1 any?)

(let [s (sp/speced-keys :req [::req-field1])
  value {::req-field1 123
 :extra1  100}

  ; act
  actual (s/explain-data s value)]
  ; assert
  (is (= {::s/problems [{:in  []
 :path[]
 :pred'no-disallowed-keys?
 :disallowed-keys #{:extra1} ; <-
 :val value
 :via []}]
  ::s/spec s
  ::s/valuevalue}
 actual)))


As a bonus, `speced-keys` specs can be merged via `merge-keys`.
They also validate at runtime that all fields have specs already registered 
(related topic: 
https://groups.google.com/forum/#!topic/clojure/i8Rz-AnCoa8).

On Monday, March 4, 2019 at 3:30:00 AM UTC+2, Daniel Dinnyes wrote:
>
> Hi Everyone,
>
> I am using spec for a while (currently on the version shipped with Clojure 
> 1.10). This post is intended to be a kind-of status report, focusing on a 
> particular issue I am facing at the moment. To explain it in detail, please 
> have a look on this following macro I wrote, which is an upgraded version 
> of *spec/keys*:
>
> (ns myns
>   (:refer-clojure :exclude [keys])
>   (:require
> [clojure.set :as set]
> [clojure.core :as core]
> [clojure.spec.alpha :as spec]))
>
> (defmacro keys
>   "Same as `clojure.spec/keys`, but accepts additional boolean option 
> :additional-keys. Unless :additional-keys is set true, only the declared 
> keys are allowed, and any additional keys will be invalid."
>   [& {:keys [additional-keys] :as args}]
>   (let [args (dissoc args :additional-keys)]
> (if additional-keys
>   `(spec/keys ~@(mapcat identity args))
>   (let [allowed-keys #{}
> allowed-keys
> (reduce
>   (fn [acc k] (into acc (k args)))
>   allowed-keys [:req :opt])
> allowed-keys
> (reduce
>   (fn [acc k] (into acc (map (comp keyword name) (k args
>   allowed-keys [:req-un :opt-un])]
> `(spec/and
>(spec/keys ~@(mapcat identity args))
>(fn [m#] (set/subset? (core/keys m#) ~allowed-keys)))
>
> I would like to explain my use-case for writing this, and the reason I 
> think there is a need for such feature.
> I am writing an import/export library from a serialized XML data format, 
> into in-memory representation. I need the import and export functions to be 
> invertible:
>  * given an original external XML data, if I import it, and then 
> immediately export it, the re-exported data has to be equal to the original.
>  * given an original internal in-memory data, if I export it, and then 
> immediately import it, the re-imported data has to be equal to the original.
>
> The main issue is that silent data-loss is absolutely unacceptable, (e.g. 
> if a newer version of the XML data format has additional unspecified 
> fields, these additional fields will fail to be imported to the in-memory 
> model. Also, if any additional unspecified in-memory data gets added, it 
> will fail to be exported. All this would all happen silently, without any 
> error/warning).
>
> The problem I am facing is I think one of the valid use-case, spec should 
> have support for such scenarios.
>
> Another similar use-case is when due to GDPR 
>  
> regulatory rules, extreme care should be taken what information gets 
> stored, and any unverified data is potentially violating requirements. 
> (e.g. a user might use clear-text comment fields to store credit-card 
> information, N.I. numbers, etc.)
>
> The original keys spec allows for additional data to be present, which is 
> in line with what Rich described as design goals for spec (i.e. "requiring 
> less", or "providing more" is not breakage but growth, and should be 
> welcomed, as per the "Speculation 
> " talk).
>
> After I started using the above macro, it became obvious that it isn'

Re: Why does the `def-` not exist?

2018-02-28 Thread Yuri Govorushchenko
I've never used defn-, ^:private, etc. But I rigorously prepend a prefix to 
mark private things, e.g. 
`-this-is-private-and-can-only-be-used-in-current-file`. From my 
experience, it makes reading/maintaining code much easier, in any 
programming language. I think I borrowed this convention from Python where 
_ is used to mark private things.

This rule can also be applied in data structures: {:public-api-data ... 
:-something-users-shouldnt-write-or-read ...}. This happens frequently when 
writing controlled inputs in React where user is responsible for storing 
the whole state of the UI control but at the same time it should be 
prohibited to modify or depend on some "internal" state parts, e.g. {:text 
"..." :-cursor-position {:x 123 :y 45}}.

In rare cases I also extract *.impl.* namespaces, in such case prefixing is 
not needed but there are more folders, files and jumps between editor tabs.

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: [core.spec] Stricter map validations?

2017-11-10 Thread Yuri Govorushchenko
t;If you're not annoying somebody, you're not really alive."
>>>>>> -- Margaret Atwood
>>>>>>
>>>>>>  
>>>>>> --
>>>>>> *From:* clo...@googlegroups.com  on behalf 
>>>>>> of Beau Fabry 
>>>>>> *Sent:* Friday, October 6, 2017 9:10:36 AM
>>>>>> *To:* Clojure
>>>>>> *Subject:* Re: [core.spec] Stricter map validations? 
>>>>>>  
>>>>>> A use case that comes to mind is a system/library that specifies the 
>>>>>> structure of some inputs/outputs, but lets users/consumers (optionally) 
>>>>>> specify further validation of the leaves. I suppose that would be 
>>>>>> possible 
>>>>>> with (s/def ::foo any?) but you'd have to be a bit more careful about 
>>>>>> load 
>>>>>> order. The other use case (which is mine) is I'm just lazy and only want 
>>>>>> to 
>>>>>> write out broad strokes specs sometimes without getting into the nitty 
>>>>>> gritty. 
>>>>>>
>>>>>> If s/keys were to validate that the keys it's provided have specs it 
>>>>>> would have to do it at validation time, so you wouldn't get the error 
>>>>>> until 
>>>>>> something was actually validated against that key spec. Trying to do it 
>>>>>> at 
>>>>>> definition time would break recursive specs.
>>>>>>
>>>>>> As one of the (apparently pretty uncommon) users who actually does 
>>>>>> happily define s/keys specs without correspondingly speccing the leaves 
>>>>>> as 
>>>>>> an "incrementally lock down/validate" approach, I wouldn't be too upset 
>>>>>> if 
>>>>>> I lost that ability and it started throwing an error. I mean it throws 
>>>>>> an 
>>>>>> error if I go to generate it anyway.
>>>>>>
>>>>>> On Friday, October 6, 2017 at 8:58:38 AM UTC-7, Leon Grapenthin 
>>>>>> wrote: 
>>>>>>>
>>>>>>> Thanks, Beau. 
>>>>>>>
>>>>>>> I am still interested why this default behavior has been chosen. It 
>>>>>>> doesn't seem like a reasonable trade-off at this point.
>>>>>>>
>>>>>>> It enables me to say: "The map must have this key", without 
>>>>>>> specifying how the data mapped to it will look like.
>>>>>>>
>>>>>>> If I ever wanted to do that, I could as well spec that key with 
>>>>>>> "any?".
>>>>>>>
>>>>>>> What are other benefits? They must justify the expense of likely 
>>>>>>> runtime errors.
>>>>>>>
>>>>>>>
>>>>>>> On Friday, October 6, 2017 at 5:34:16 PM UTC+2, Beau Fabry wrote: 
>>>>>>>>
>>>>>>>> Leon, perhaps you could add this code to your test suite? 
>>>>>>>>
>>>>>>>> boot.user=> (let [kws (atom #{})]
>>>>>>>>#_=>   (clojure.walk/postwalk (fn [x] (when 
>>>>>>>> (qualified-keyword? x) (swap! kws conj x)) x) (map s/form (vals 
>>>>>>>> (s/registry (clojure.set/difference @kws (set (keys (s/registry
>>>>>>>>#_=> )
>>>>>>>> #{:clojure.spec.alpha/v :clojure.spec.alpha/k}
>>>>>>>> boot.user=>
>>>>>>>>
>>>>>>>> On Friday, October 6, 2017 at 5:56:29 AM UTC-7, Leon Grapenthin 
>>>>>>>> wrote: 
>>>>>>>>>
>>>>>>>>> Open maps/specs are fine.
>>>>>>>>>
>>>>>>>>> s/keys supporting unregistered specs are not.
>>>>>>>>>
>>>>>>>>> At least to me. I just fixed two more bugs in production that were 
>>>>>>>>> would not have happened.
>>>>>>>>>
>>>>>>>>> What are the supposed benefits of this feature? 
>>>>>>>>>
>>>>>>>>> I can only infer "being able to require keys without their sp

Re: [core.spec] Stricter map validations?

2017-10-04 Thread Yuri Govorushchenko
Thanks. This approach is also different from the macro because it will 
check specs existence at the validation time, not at the s/def call.

On Wednesday, October 4, 2017 at 4:18:16 PM UTC+3, Moritz Ulrich wrote:
>
> Yuri Govorushchenko > writes: 
>
> > Thank you the pointers! So far I ended up with writing a small `map` 
> macro 
> > which is similar to `s/keys` but checks that keys are already in the 
> > registry: 
> https://gist.github.com/metametadata/5f600e20e0e9b0ce6bce146c6db429e2 
>
> Note that you can simply combine a custom predicate and `s/keys` in 
> clojure.spec to verify that all keys in a given map have a underlying 
> spec: 
>
> ``` 
> (s/def ::whatever (s/and (s/keys ...) 
>  #(every? keyword? (keys %)) 
>  #(every? (comp boolean s/get-spec) (keys %)) ) 
> ``` 
>

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: [core.spec] Stricter map validations?

2017-10-03 Thread Yuri Govorushchenko
3) I agree that my argumentation was vague but the sentiment still holds 
true: I'd like to easily validate keyword args with the same level of 
confidence as I can validate positional args. 

1,2)

> Because all map keys are independent and don't rely on each other for 
ordering.

> You can add additional checks to verify stricter things on top of open 
specs. You can't add openness to something that is already closed.

If I understand you correctly, library currently is only concerned with 
doing simplest things and that's why `s/keys` is not very strict? But then 
I'm not sure if it's the simplest behaviour though since `s/keys` specs 
validate 2 things: keys AND (sometimes) values. And that "sometimes" part 
makes production code more error-prone. 

Is there any chance that in the future core.spec will have additional API 
to allow specifying other levels of map validation strictness? Possible 
options:

* Keyset validation strictness:
  - open (current behavior, allows putting any additional keys into map)
  - closed (map must contain only the listed keys)
* Values validation strictness: 
  - never validate (i.e. any values will pass, not sure about the use cases 
though)
  - only if spec for key is registered (current behavior)
  - always validate values (should fail if there's no spec for value 
registered)

Thanks.

On Tuesday, October 3, 2017 at 6:43:56 PM UTC+3, Alex Miller wrote:
>
>
>
> On Tuesday, October 3, 2017 at 8:10:30 AM UTC-5, Yuri Govorushchenko wrote:
>>
>> 1) About `s/keys` silently ignoring missing value specs. My question was: 
>> "Is there any way to ensure that the keys I used in `s/keys` have the 
>> associated specs defined?."
>>
>>  Specs can be defined or added later, so there is no valid way to do this.
>>
>>
>> OK, so requiring that values are spec-ed can't be enforced at compilation 
>> time because this would make it impossible to define value specs after 
>> `s/keys`:
>>
>> ```
>> (s/def ::foo (s/keys :req [::bar]))
>> ,,,
>> (s/def ::bar ,,,)
>> ```
>>
>> This can explain why it's not built into the library. In such case I'm 
>> fine with using a custom macro instead of `s/keys` (see my gist in the 
>> previous post).
>>
>> But what about enforcing existence of value specs at runtime, during 
>> validation? `s/cat` does that, e.g.:
>>
>> ```
>> cljs.user=> (s/def ::foo (s/cat :bar ::baz))
>> :cljs.user/foo
>> cljs.user=> (s/valid? ::foo [123])
>> Unable to resolve spec: :cljs.user/baz
>> ```
>>
>>
> This is required because you are essentially parsing a sequential data 
> structure - one component could be a single value or many values, so you 
> must know it's definition.
>  
>
>> Why is it then not a default behaviour for `s/keys` as well? I.e.:
>>
>
> Because all map keys are independent and don't rely on each other for 
> ordering.
>  
>
>>
>> ```
>> ; current behavior
>> cljs.user=> (s/def ::foo (s/keys :req [::x]))
>> :cljs.user/foo
>> cljs.user=> (s/valid? ::foo {::x 123})
>> true
>>
>> ; desired behavior
>> cljs.user=> (s/def ::foo (s/keys :req [::x]))
>> :cljs.user/foo
>> cljs.user=> (s/valid? ::foo {::x 123})
>> Unable to resolve spec: :cljs.user/x
>> ```
>>
>> I don't think Spec-ulation Keynote addresses this behaviour.
>>
>> 2) There's no *built-in* way restrict the keyset of the map in 
>> `core.spec`.
>>
>> The reasoning for this seems to be based around the idea of backwards 
>> compatible evolving specs (see Spec-alution Keynote). But there are several 
>> good examples already covered in this thread which demonstrate that it 
>> would be very convenient to also have strict keyset validations available 
>> in `core.spec`. After all, the library is about specifying the structure of 
>> data and not only about specifying API contracts.
>>
>
> You can add additional checks to verify stricter things on top of open 
> specs. You can't add openness to something that is already closed.
>  
>
>>
>> 3) Thinking more about `s/keys` vs. `s/cat` *specifically* in the context 
>> of asserting API contracts (I'm stressing on the context here because 
>> `core.spec` is a general library and should take in account other use cases 
>> too).
>>
>> In this context it's even more apparent that `s/keys` should behave in 
>> the same way as `s/cat` because there's not much difference between 
>> positional arguments and keyword arguments. 
>>
>
> But ther

Re: [core.spec] Stricter map validations?

2017-10-03 Thread Yuri Govorushchenko
Thank you, it looks handy!

On Tuesday, October 3, 2017 at 6:59:27 PM UTC+3, Tommi Reiman wrote:
>
> Hi.
>
> Spec-tools (https://github.com/metosin/spec-tools) has some tools for 
> this: the spec visitor (walking over all core specs, e.g. to collect all 
> registered specs) and map-conformers: fail-on-extra-keys and 
> strip-extra-keys. 
>
> Here's an example to strip away extra keys:
>
> (require '[clojure.spec.alpha :as s])
> (require '[spec-tools.core :as st])
>
> (s/def ::name string?)
> (s/def ::street string?)
> (s/def ::address (st/spec (s/keys :req-un [::street])))
> (s/def ::user (st/spec (s/keys :req-un [::name ::address])))
>
> (def inkeri
>   {:name "Inkeri"
>:age 102
>:address {:street "Satamakatu"
>  :city "Tampere"}})
>
> (st/select-spec ::user inkeri); {:name "Inkeri";  :address {:street 
> "Satamakatu"}}
>
>
> There is CLJ-2116 that would enable this without any wrapping of specs: 
> https://dev.clojure.org/jira/browse/CLJ-2116. Seems stalled.
>
> If runtime transformations would be supported (currently: out of scope), I 
> think we could go even further - we could rewrite conform* as walk* to 
> support all of fast coercion, explain and conform. We are using conform + 
> unform as a substitute of coercion but sadly, it's quite slow, especially 
> for maps.
>
> Tommi
>
> tiistai 3. lokakuuta 2017 16.10.30 UTC+3 Yuri Govorushchenko kirjoitti:
>>
>> 1) About `s/keys` silently ignoring missing value specs. My question was: 
>> "Is there any way to ensure that the keys I used in `s/keys` have the 
>> associated specs defined?."
>>
>>  Specs can be defined or added later, so there is no valid way to do this.
>>
>>
>> OK, so requiring that values are spec-ed can't be enforced at compilation 
>> time because this would make it impossible to define value specs after 
>> `s/keys`:
>>
>> ```
>> (s/def ::foo (s/keys :req [::bar]))
>> ,,,
>> (s/def ::bar ,,,)
>> ```
>>
>> This can explain why it's not built into the library. In such case I'm 
>> fine with using a custom macro instead of `s/keys` (see my gist in the 
>> previous post).
>>
>> But what about enforcing existence of value specs at runtime, during 
>> validation? `s/cat` does that, e.g.:
>>
>> ```
>> cljs.user=> (s/def ::foo (s/cat :bar ::baz))
>> :cljs.user/foo
>> cljs.user=> (s/valid? ::foo [123])
>> Unable to resolve spec: :cljs.user/baz
>> ```
>>
>> Why is it then not a default behaviour for `s/keys` as well? I.e.:
>>
>> ```
>> ; current behavior
>> cljs.user=> (s/def ::foo (s/keys :req [::x]))
>> :cljs.user/foo
>> cljs.user=> (s/valid? ::foo {::x 123})
>> true
>>
>> ; desired behavior
>> cljs.user=> (s/def ::foo (s/keys :req [::x]))
>> :cljs.user/foo
>> cljs.user=> (s/valid? ::foo {::x 123})
>> Unable to resolve spec: :cljs.user/x
>> ```
>>
>> I don't think Spec-ulation Keynote addresses this behaviour.
>>
>> 2) There's no *built-in* way restrict the keyset of the map in 
>> `core.spec`.
>>
>> The reasoning for this seems to be based around the idea of backwards 
>> compatible evolving specs (see Spec-alution Keynote). But there are several 
>> good examples already covered in this thread which demonstrate that it 
>> would be very convenient to also have strict keyset validations available 
>> in `core.spec`. After all, the library is about specifying the structure of 
>> data and not only about specifying API contracts.
>>
>> 3) Thinking more about `s/keys` vs. `s/cat` *specifically* in the context 
>> of asserting API contracts (I'm stressing on the context here because 
>> `core.spec` is a general library and should take in account other use cases 
>> too).
>>
>> In this context it's even more apparent that `s/keys` should behave in 
>> the same way as `s/cat` because there's not much difference between 
>> positional arguments and keyword arguments. I'll try to illustrate what I 
>> mean with an example. Let's say there's a function with positional 
>> arguments:
>>
>> ```
>> (defn foo-pos [x y z])
>>
>> ; call example:
>> (foo xxx yyy zzz)
>> ```
>>
>> I hope we can agree that it's more or less equivalent to this function 
>> with the keyword arguments where each keyword corresponds to the position 
>> number in `foo-pos`:
>>
>> ```
>> (de

Re: [core.spec] Stricter map validations?

2017-10-03 Thread Yuri Govorushchenko
1) About `s/keys` silently ignoring missing value specs. My question was: 
"Is there any way to ensure that the keys I used in `s/keys` have the 
associated specs defined?."

 Specs can be defined or added later, so there is no valid way to do this.


OK, so requiring that values are spec-ed can't be enforced at compilation 
time because this would make it impossible to define value specs after 
`s/keys`:

```
(s/def ::foo (s/keys :req [::bar]))
,,,
(s/def ::bar ,,,)
```

This can explain why it's not built into the library. In such case I'm fine 
with using a custom macro instead of `s/keys` (see my gist in the previous 
post).

But what about enforcing existence of value specs at runtime, during 
validation? `s/cat` does that, e.g.:

```
cljs.user=> (s/def ::foo (s/cat :bar ::baz))
:cljs.user/foo
cljs.user=> (s/valid? ::foo [123])
Unable to resolve spec: :cljs.user/baz
```

Why is it then not a default behaviour for `s/keys` as well? I.e.:

```
; current behavior
cljs.user=> (s/def ::foo (s/keys :req [::x]))
:cljs.user/foo
cljs.user=> (s/valid? ::foo {::x 123})
true

; desired behavior
cljs.user=> (s/def ::foo (s/keys :req [::x]))
:cljs.user/foo
cljs.user=> (s/valid? ::foo {::x 123})
Unable to resolve spec: :cljs.user/x
```

I don't think Spec-ulation Keynote addresses this behaviour.

2) There's no *built-in* way restrict the keyset of the map in `core.spec`.

The reasoning for this seems to be based around the idea of backwards 
compatible evolving specs (see Spec-alution Keynote). But there are several 
good examples already covered in this thread which demonstrate that it 
would be very convenient to also have strict keyset validations available 
in `core.spec`. After all, the library is about specifying the structure of 
data and not only about specifying API contracts.

3) Thinking more about `s/keys` vs. `s/cat` *specifically* in the context 
of asserting API contracts (I'm stressing on the context here because 
`core.spec` is a general library and should take in account other use cases 
too).

In this context it's even more apparent that `s/keys` should behave in the 
same way as `s/cat` because there's not much difference between positional 
arguments and keyword arguments. I'll try to illustrate what I mean with an 
example. Let's say there's a function with positional arguments:

```
(defn foo-pos [x y z])

; call example:
(foo xxx yyy zzz)
```

I hope we can agree that it's more or less equivalent to this function with 
the keyword arguments where each keyword corresponds to the position number 
in `foo-pos`:

```
(defn foo-pos* [{x 1 y 2 z 3}])

; call example:
(foo-pos* {1 xxx 2 yyy 3 zzz})
```

And making a step further to better naming:

```
(defn foo-kw [{:keys [::x ::y ::z]}])

; call example:
(foo-kw {::x xxx ::y yyy ::z zzz})
```

So, the biggest difference is the syntax of function calls. Keyword 
arguments are usually more readable (esp. when there are several args) and 
easier to maintain (since they can be reordered at the call site and 
function definition safely). Let's now spec-ify the arguments using `s/cat` 
for positional args and `s/keys` for keyword args (as recommended in docs). 
These specs ensure that argument `x` is present and is of type `::x`, `y` 
is present and is of type `::y`, etc.:

```
(s/def ::foo-pos-args (s/cat :x ::x :y ::y :z ::z))
(s/def ::foo-kw-args (s/keys :req [::x ::y ::z]))
```

Now (because the functions are equivalent) I'd expect their specs to 
validate equivalent inputs in the similar way. But it's not the case if 
developer forgets to define the `::y` spec!

```
; ::y spec is missing
(s/def ::x int?)
(s/def ::z int?)

; positional args
(def pos-inputs [1 2 3])
(s/valid? ::foo-pos-args pos-inputs) ; => Unable to resolve spec: 
:cljs.user/y (good)

; keyword args
(def kw-inputs {::x 1 ::y 2 ::z 3})
(s/valid? ::foo-kw-args kw-inputs) ; => true (ouch!)
```

TL/DR: (specifically in the context of function contracts) `core.spec` 
shouldn't treat APIs with positional arguments and APIs with keyword 
arguments differently and thus `s/keys` should check arg values at keys in 
the same way `s/cat` checks arg values at positions.

On Tuesday, October 3, 2017 at 6:01:06 AM UTC+3, Alex Miller wrote:
>
>
>
> On Monday, October 2, 2017 at 10:37:31 AM UTC-5, Yuri Govorushchenko wrote:
>>
>> Hi!
>>
>> I have some noobie questions for which I couldn't google the compelling 
>> answers.
>>
>> 1) Is there any way to ensure that the keys I used in `s/keys` have the 
>> associated specs defined? 
>>
>
> Specs can be defined or added later, so there is no valid way to do this.
>  
>
>> At compile time or at least at runtime. Maybe via an additional library? 
>> I could imagine a macro (smt. like `s/keys-strict` or `s/map-pairs`, as 
>> maps can also be

Re: [core.spec] Stricter map validations?

2017-10-02 Thread Yuri Govorushchenko
Thank you the pointers! So far I ended up with writing a small `map` macro 
which is similar to `s/keys` but checks that keys are already in the 
registry: https://gist.github.com/metametadata/5f600e20e0e9b0ce6bce146c6db429e2

On Monday, October 2, 2017 at 10:03:57 PM UTC+3, Beau Fabry wrote:
>
> fwiw, I vote for leaving it. It's extra flexibility and is afaict a fairly 
> easy error to catch. Here's a function that I think should give you every 
> qualified keyword that is used in a spec but that does not have a spec 
> defined for it:
> boot.user=> (let [kws (atom #{})]
>#_=>   (clojure.walk/postwalk (fn [x] (when (qualified-keyword? x) 
> (swap! kws conj x)) x) (map s/form (vals (s/registry 
> (clojure.set/difference @kws (set (keys (s/registry
>#_=> )
> #{:clojure.spec.alpha/v :clojure.spec.alpha/k}
> boot.user=>
>
> On Monday, October 2, 2017 at 10:30:57 AM UTC-7, Leon Grapenthin wrote:
>>
>> I second this from my experience, using spec quite extensively since its 
>> release.
>>
>> We already had some invalid data passing silently because of this. It can 
>> easily happen if you have a typo in the spec.
>>
>> Also we never experienced benefits from being able to not spec keys 
>> required in s/keys. It appears to be a pretty obsolete feature, making 
>> vulnerabilities more likely.
>>
>> On Monday, October 2, 2017 at 5:37:31 PM UTC+2, Yuri Govorushchenko wrote:
>>>
>>> Hi!
>>>
>>> I have some noobie questions for which I couldn't google the compelling 
>>> answers.
>>>
>>> 1) Is there any way to ensure that the keys I used in `s/keys` have the 
>>> associated specs defined? At compile time or at least at runtime. Maybe via 
>>> an additional library? I could imagine a macro (smt. like `s/keys-strict` 
>>> or `s/map-pairs`, as maps can also be viewed as sets of spec'ed pairs) 
>>> which additionally checks that all keys have specs registered. I'm OK with 
>>> sacrificing some flexibility (e.g. being able to define key specs after map 
>>> specs, dynamically, etc.) in favour of more strictness.
>>>
>>> Motivation: I don't fully trust my map validation code when using 
>>> `core.spec`. `s/keys` doesn't require that the key has the spec registered 
>>> to validate its value. Although this may be flexible but in practice can 
>>> lead to errors. Specifically, it's quite easy to forget to create a spec 
>>> for a key, mistype it or forget to require the namespace in which key spec 
>>> is defined (e.g. if the common key specs reside in a dedicated ns):
>>>
>>> ```
>>> ; totally forgot to define a spec for ::foo
>>> (s/def ::bar (s/keys :req [::foo]))
>>>
>>> ; fooo vs. foo typo
>>> (s/def ::fooo string?)
>>> (s/def ::bar (s/keys :req [::foo]))
>>>
>>> ; :common/foo vs. ::common/foo typo
>>> (s/def ::bar (s/keys :req [:common/foo]))
>>>
>>> ; didn't require common.core ns (spec for :common.core/foo is not added 
>>> to global registry)
>>> (s/def ::bar (s/keys :req [:common.core/foo]))
>>> ```
>>>
>>> These subtle mistakes can lead to map validations passing silently (as 
>>> long as keysets are correct).
>>>
>>> Related to this: there're feature requests for Cursive IDE which try to 
>>> address typing and reading mistakes related to keywords, e.g. 
>>> https://github.com/cursive-ide/cursive/issues/1846 and 
>>> https://github.com/cursive-ide/cursive/issues/1864.
>>>
>>> After using Schema for a while it's difficult to appreciate the way 
>>> `core.spec` defines it's own global registry which uses keywords instead of 
>>> using spec instances and good old variables, especially since Cursive IDE 
>>> has quite a nice support for variables already. But I think this is another 
>>> topic which was already discussed, e.g. in 
>>> https://groups.google.com/forum/#!topic/clojure/4jhSCZaFQFY ("Spec 
>>> without global registry?").
>>>
>>> 2) What is the motivation for library having a "loose" default behaviour 
>>> of `s/keys` and no "strict" variant at all for spec-ing both keys and 
>>> values at the same tome? I think in majority of cases I'd need to spec both 
>>> keys and values of the map instead of only keys and would expect the 
>>> library to have built-in API for this. Maybe for the future references it 
>>> would be beneficial to ad

[core.spec] Stricter map validations?

2017-10-02 Thread Yuri Govorushchenko
Hi!

I have some noobie questions for which I couldn't google the compelling 
answers.

1) Is there any way to ensure that the keys I used in `s/keys` have the 
associated specs defined? At compile time or at least at runtime. Maybe via 
an additional library? I could imagine a macro (smt. like `s/keys-strict` 
or `s/map-pairs`, as maps can also be viewed as sets of spec'ed pairs) 
which additionally checks that all keys have specs registered. I'm OK with 
sacrificing some flexibility (e.g. being able to define key specs after map 
specs, dynamically, etc.) in favour of more strictness.

Motivation: I don't fully trust my map validation code when using 
`core.spec`. `s/keys` doesn't require that the key has the spec registered 
to validate its value. Although this may be flexible but in practice can 
lead to errors. Specifically, it's quite easy to forget to create a spec 
for a key, mistype it or forget to require the namespace in which key spec 
is defined (e.g. if the common key specs reside in a dedicated ns):

```
; totally forgot to define a spec for ::foo
(s/def ::bar (s/keys :req [::foo]))

; fooo vs. foo typo
(s/def ::fooo string?)
(s/def ::bar (s/keys :req [::foo]))

; :common/foo vs. ::common/foo typo
(s/def ::bar (s/keys :req [:common/foo]))

; didn't require common.core ns (spec for :common.core/foo is not added to 
global registry)
(s/def ::bar (s/keys :req [:common.core/foo]))
```

These subtle mistakes can lead to map validations passing silently (as long 
as keysets are correct).

Related to this: there're feature requests for Cursive IDE which try to 
address typing and reading mistakes related to keywords, e.g. 
https://github.com/cursive-ide/cursive/issues/1846 and 
https://github.com/cursive-ide/cursive/issues/1864.

After using Schema for a while it's difficult to appreciate the way 
`core.spec` defines it's own global registry which uses keywords instead of 
using spec instances and good old variables, especially since Cursive IDE 
has quite a nice support for variables already. But I think this is another 
topic which was already discussed, e.g. 
in https://groups.google.com/forum/#!topic/clojure/4jhSCZaFQFY ("Spec 
without global registry?").

2) What is the motivation for library having a "loose" default behaviour of 
`s/keys` and no "strict" variant at all for spec-ing both keys and values 
at the same tome? I think in majority of cases I'd need to spec both keys 
and values of the map instead of only keys and would expect the library to 
have built-in API for this. Maybe for the future references it would be 
beneficial to add concrete code examples into motivation in the core.spec 
guide (https://clojure.org/about/spec#_map_specs_should_be_of_keysets_only) 
which would better illustrate the described benefits of the current lib 
behaviour?

Thanks.

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


[spec] Instrumentation for protocol methods?

2017-05-18 Thread Yuri Govorushchenko
Hello,

I wonder, will it be possible to add specs to defprotocols? I use protocols 
a lot and it would be very handy to be able to instrument protocol methods. 
It is something Plumatic Schema cannot do at the 
moment: https://github.com/plumatic/schema/issues/117

Thanks.

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: A call for an idiomatic rendering of Clojure EDN in info/error messages

2015-11-13 Thread Yuri Govorushchenko
I recall Google uses vertical bars in ObjC comments for similar purposes, 
as stated in their style guide:

Use vertical bars to quote variable names and symbols in comments rather 
than quotes or naming the symbol inline.

This helps eliminate ambiguity, especially when the symbol is a common word 
that might make the sentence read like it was poorly constructed. E.g. for 
a symbol "count":

// Sometimes we need |count| to be less than zero.

or when quoting something which already contains quotes

// Remember to call |StringWithoutSpaces("foo bar baz")|


-- 
https://google.github.io/styleguide/objcguide.xml?showone=Implementation_Comments#Implementation_Comments

пятница, 13 ноября 2015 г., 13:55:40 UTC+2 пользователь Colin Yates написал:
>
> Hi all,
>
> Can we, the community agree a consistent way of rendering Clojure EDN when 
> we report it in info or error. For example, given the EDN "2" (i.e. a 
> string containing a single character 2) I have seen various libraries 
> render it as:
>
>  - 2
>  - "2"
>  - ["2"]
>  - [2]
>  - (2)
>  - '"2"'
>
> I would like to propose that we standardise around "_" as the boundary 
> character as that isn't likely to be used in (except for some markup I 
> guess but that is pretty unlikely), so a test framework might render as 
> such:
>
>  - expected _2_ but was _"2"_
>
> Please? :-)
>

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


[ANN] clj-fakes 0.3.0 - an isolation framework for Clojure and ClojureScript

2015-11-12 Thread Yuri Govorushchenko
Hi! This is the first public release of my lib for mocking and stubbing in 
unit tests, I would greatly appreciate any feedback.

GitHub: https://github.com/metametadata/clj-fakes
Documentation: http://metametadata.github.io/clj-fakes/

*Features*
   
   - All test doubles are named "fakes" to simplify terminology
   - Fakes can be created for:
  - protocol and Java interface instances
  - functions
   - "Nice" and "strict" protocol fakes are supported
   - Monkey patching is supported to fake implicit dependencies
   - Self-testing: automatically checks for unused fakes
   - Test runner agnostic
   - Arrange-Act-Assert style testing

*Example of creating a mock object*

(defprotocol AnimalProtocol
  (speak [this name])); ...

(deftest example
  (f/with-fakes
; create fake instance of specified protocol
(let [cow (f/reify-fake p/AnimalProtocol
; ask framework to record method calls
(speak :recorded-fake))]
  ; call method on fake object
  (p/speak cow "Bob")

  ; assert that method was called with specified args
  (is (f/method-was-called p/speak cow ["Bob"])

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: How to make a static variable dynamic at runtime?

2015-07-22 Thread Yuri Govorushchenko
Hello Alexander, Marc,

Ah, now I get this. Thank you for your responses!

вторник, 21 июля 2015 г., 17:54:42 UTC+3 пользователь Yuri Govorushchenko 
написал:
>
> The problem I'm trying to solve is how to stub a variable (e.g. a function 
> from a third-party lib) in tests so that the stubbing occurs only in the 
> current thread with other threads continuing using var's root value. It's 
> important because unit tests may be run in parallel. Without thread-local 
> binding two threads stubbing the same function will lead to race conditions:
>
> (binding [somelib/foo foo-stub] ; throws java.lang.IllegalStateException: 
> Can't dynamically bind non-dynamic var
>   ; invoke tested code which depends on foo
>   ; assert stuff
>   )
>
> I've tried to use *.setDynamic* (as described in blog post [1]) but it 
> doesn't work without direct *deref*-ing of the var:
>
> (def static-var 123)
> (defn quz[]
>   (.setDynamic #'static-var true)
>   (binding [static-var 1000]
> (println "static-var =" static-var "deref =" @#'static-var)))
>
> (quz) ; => static-var = 123 deref = 1000
>
> This approach seems to be used in a recent *bolth* lib [2]. And The 
> strange thing is that in REPL running this code line by line works:
>
> user=> (def static-var 123)
>> #'user/static-var
>> user=> (.setDynamic #'static-var true)
>> #'user/static-var
>> user=> (binding [static-var 1000] (println "static-var =" static-var))
>> static-var = 1000
>
>
> Looking at Var class implementation I couldn't figure out why .
> *setDynamic* call wouldn't work. My guess is that compiler somehow caches 
> initial static Var value for performance reasons?..
>
> So the questions are:
> 1) Is it a bug that *.setDynamic* + *binding* don't work?
> 2) Is there any other way to temporally rebind static variable 
> thread-locally? Considering I can't add *^:dynamic* into third-party lib 
> and don't want to write a wrapper or use dependency injection in order to 
> explicitly substitute the dependency in my unit tests.
> 3) Is there a Clojure parallel test runner which runs tests in new 
> processes instead of threads? This would eliminate any race conditions. 
> Python's *nose* test runner works this way [3].
> 4) Maybe crazy: does Clojure allow dynamically rebinding the symbol to a 
> new Var instance so that I could set *'static-var* to point at *(.setDynamic 
> (Var/create)*?
> 5) Even crazier idea: can I change the nature of the var so that it 
> behaves like an InheritedThreadLocal instead of ThreadLocal, but without 
> forcing a user to *deref* it (as it was described in [4])?
>
> Links:
> [1] http://blog.zolotko.me/2012/06/making-variable-dynamic-in-clojure.html
> [2] 
> https://github.com/yeller/bolth/blob/323532683e3f66ae11566db5423c1e927e51818e/src/bolth/runner.clj#L99
> [3] 
> http://nose.readthedocs.org/en/latest/doc_tests/test_multiprocess/multiprocess.html
> [4] https://aphyr.com/posts/240-configuration-and-scope  - see 
> "Thread-inheritable dynamic vars in Clojure"
>
>

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Re: How to make a static variable dynamic at runtime?

2015-07-22 Thread Yuri Govorushchenko
Thank you for a reply, I totally agree with you on dependency injection. 
Though I'm exercising in writing a mocking framework and thought it would 
be an interesting feature to implement a thread-safe mocking of an implicit 
dependency.

среда, 22 июля 2015 г., 5:03:36 UTC+3 пользователь Surgo написал:
>
> Not that it's the answer you're looking for, but usually this is when you 
> rewrite the code you're testing to use dependency injection (ie, take the 
> var of interest as an argument instead of a global or in its lexical 
> environment).
>
> -- Morgon
>
> On Tuesday, July 21, 2015 at 10:54:42 AM UTC-4, Yuri Govorushchenko wrote:
>>
>> The problem I'm trying to solve is how to stub a variable (e.g. a 
>> function from a third-party lib) in tests so that the stubbing occurs only 
>> in the current thread with other threads continuing using var's root value. 
>> It's important because unit tests may be run in parallel. Without 
>> thread-local binding two threads stubbing the same function will lead to 
>> race conditions:
>>
>> (binding [somelib/foo foo-stub] ; throws java.lang.IllegalStateException: 
>> Can't dynamically bind non-dynamic var
>>   ; invoke tested code which depends on foo
>>   ; assert stuff
>>   )
>>
>> I've tried to use *.setDynamic* (as described in blog post [1]) but it 
>> doesn't work without direct *deref*-ing of the var:
>>
>> (def static-var 123)
>> (defn quz[]
>>   (.setDynamic #'static-var true)
>>   (binding [static-var 1000]
>> (println "static-var =" static-var "deref =" @#'static-var)))
>>
>> (quz) ; => static-var = 123 deref = 1000
>>
>> This approach seems to be used in a recent *bolth* lib [2]. And The 
>> strange thing is that in REPL running this code line by line works:
>>
>> user=> (def static-var 123)
>>> #'user/static-var
>>> user=> (.setDynamic #'static-var true)
>>> #'user/static-var
>>> user=> (binding [static-var 1000] (println "static-var =" static-var))
>>> static-var = 1000
>>
>>
>> Looking at Var class implementation I couldn't figure out why .
>> *setDynamic* call wouldn't work. My guess is that compiler somehow 
>> caches initial static Var value for performance reasons?..
>>
>> So the questions are:
>> 1) Is it a bug that *.setDynamic* + *binding* don't work?
>> 2) Is there any other way to temporally rebind static variable 
>> thread-locally? Considering I can't add *^:dynamic* into third-party lib 
>> and don't want to write a wrapper or use dependency injection in order to 
>> explicitly substitute the dependency in my unit tests.
>> 3) Is there a Clojure parallel test runner which runs tests in new 
>> processes instead of threads? This would eliminate any race conditions. 
>> Python's *nose* test runner works this way [3].
>> 4) Maybe crazy: does Clojure allow dynamically rebinding the symbol to a 
>> new Var instance so that I could set *'static-var* to point at *(.setDynamic 
>> (Var/create)*?
>> 5) Even crazier idea: can I change the nature of the var so that it 
>> behaves like an InheritedThreadLocal instead of ThreadLocal, but without 
>> forcing a user to *deref* it (as it was described in [4])?
>>
>> Links:
>> [1] 
>> http://blog.zolotko.me/2012/06/making-variable-dynamic-in-clojure.html
>> [2] 
>> https://github.com/yeller/bolth/blob/323532683e3f66ae11566db5423c1e927e51818e/src/bolth/runner.clj#L99
>> [3] 
>> http://nose.readthedocs.org/en/latest/doc_tests/test_multiprocess/multiprocess.html
>> [4] https://aphyr.com/posts/240-configuration-and-scope  - see 
>> "Thread-inheritable dynamic vars in Clojure"
>>
>>

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


How to make a static variable dynamic at runtime?

2015-07-21 Thread Yuri Govorushchenko
The problem I'm trying to solve is how to stub a variable (e.g. a function 
from a third-party lib) in tests so that the stubbing occurs only in the 
current thread with other threads continuing using var's root value. It's 
important because unit tests may be run in parallel. Without thread-local 
binding two threads stubbing the same function will lead to race conditions:

(binding [somelib/foo foo-stub] ; throws java.lang.IllegalStateException: 
Can't dynamically bind non-dynamic var
  ; invoke tested code which depends on foo
  ; assert stuff
  )

I've tried to use *.setDynamic* (as described in blog post [1]) but it 
doesn't work without direct *deref*-ing of the var:

(def static-var 123)
(defn quz[]
  (.setDynamic #'static-var true)
  (binding [static-var 1000]
(println "static-var =" static-var "deref =" @#'static-var)))

(quz) ; => static-var = 123 deref = 1000

This approach seems to be used in a recent *bolth* lib [2]. And The strange 
thing is that in REPL running this code line by line works:

user=> (def static-var 123)
> #'user/static-var
> user=> (.setDynamic #'static-var true)
> #'user/static-var
> user=> (binding [static-var 1000] (println "static-var =" static-var))
> static-var = 1000


Looking at Var class implementation I couldn't figure out why .*setDynamic* 
call wouldn't work. My guess is that compiler somehow caches initial static 
Var value for performance reasons?..

So the questions are:
1) Is it a bug that *.setDynamic* + *binding* don't work?
2) Is there any other way to temporally rebind static variable 
thread-locally? Considering I can't add *^:dynamic* into third-party lib 
and don't want to write a wrapper or use dependency injection in order to 
explicitly substitute the dependency in my unit tests.
3) Is there a Clojure parallel test runner which runs tests in new 
processes instead of threads? This would eliminate any race conditions. 
Python's *nose* test runner works this way [3].
4) Maybe crazy: does Clojure allow dynamically rebinding the symbol to a 
new Var instance so that I could set *'static-var* to point at *(.setDynamic 
(Var/create)*?
5) Even crazier idea: can I change the nature of the var so that it behaves 
like an InheritedThreadLocal instead of ThreadLocal, but without forcing a 
user to *deref* it (as it was described in [4])?

Links:
[1] http://blog.zolotko.me/2012/06/making-variable-dynamic-in-clojure.html
[2] 
https://github.com/yeller/bolth/blob/323532683e3f66ae11566db5423c1e927e51818e/src/bolth/runner.clj#L99
[3] 
http://nose.readthedocs.org/en/latest/doc_tests/test_multiprocess/multiprocess.html
[4] https://aphyr.com/posts/240-configuration-and-scope  - see 
"Thread-inheritable dynamic vars in Clojure"

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.