Fork me on GitHub
#clojure
<
2019-01-21
>
kaosko05:01:32

hey has anybody worked with postal? I can't quite figure out how to set mime content-id header for the attachment (or whether it's possible at all)?

kaosko05:01:31

oops. should have gone through the source right away (:content-id for the same mime part map works)

Daniel07:01:59

Hi, is this a bug in clojure spec?

(require [clojure.spec.alpha :as s])
(s/def ::foobar keyword?)
(s/def ::bugged keyword?)
(s/def ::form1 (s/keys :req [::foobar]))
(s/def ::form2 (s/keys :req [::bugged]))
(s/explain ::form1 {::foobar :d})
(s/explain ::form1 {::foobar :d ::bugged nil})
The second (s/explain) fails.

andy.fingerhut07:01:39

Others can answer more authoritatively than I can, but I believe this is expected behavior. ::form1 only requires ::foobar, and it allows other key/value pairs to be present as well. If one of those other keys that is present has a spec, then its value must satisfy that spec.

andy.fingerhut07:01:43

If one of those other keys that is present does not have a spec, then its value is not checked to see what it is.

Daniel07:01:56

Hmm, I saw this paragraph in the docs, for s/keys

In addition, the values of *all* namespace-qualified keys will be validated
(and possibly destructured) by any registered specs. Note: there is
no support for inline value specification, by design.

Daniel07:01:30

So there's no way to prevent ::bugged from being checked right?

andy.fingerhut07:01:28

I guess a question would be, if the only valid kind of value to associate with the key ::bugged is a keyword, then why do you hope for no error to occur when you give that key a value nil?

andy.fingerhut07:01:30

And if nil is a valid value to associate with the keyword ::bugged, then maybe you want to change the spec for ::bugged to allow nil

Lennart Buit07:01:43

Well, I could imagine that you sometimes don’t care about what is in ::bugged

Lennart Buit07:01:54

You know, after some form of transform that you would make a stronger guarantee

andy.fingerhut07:01:58

Spec certainly lends itself well to doing that by calling the pre-transform and the post-transform things by different keys.

andy.fingerhut07:01:16

but not sure if that conflicts with other goals you may have.

Lennart Buit07:01:32

Yeah, I was merely stating why I would maybe want to have such an option

Lennart Buit07:01:17

I don’t think its good or bad about spec to expect each key to have a single (canonical/always applying) spec, its just surprising ^^

Daniel07:01:40

Yes, this is something that was surprising to me, and took me a while to figure out if this was a bug with my code, spec, or of my understanding with spec

Daniel07:01:53

On the other hand, unqualified keywords works the opposite

Daniel07:01:52

(s/def ::form1 (s/keys :req-un [::foobar]))
(s/explain ::form1 {:foobar :d :bugged nil}) ;; Success!
(s/def ::form2 (s/keys :req [::foobar]))
(s/explain ::form2 {::foobar :d ::bugged nil}) ;; fails

urbanslug10:01:07

How do you guys exit the clojure repl from clj or clojure ?

urbanslug10:01:59

Me too, but I thought I was doing something hacky. [Edit] Thanks

urbanslug10:01:32

Trying to understand aliases invoked via clojure or clj could someone please explain what this most likely means clojure -A:dev:build:dev/rebel. I see dev and build are aliases that should be in deps.edn but I don't quite get it.

thheller10:01:00

aliases let you selectively add functionality/dependencies. so you just basically combine several aliases to get to the result you want

urbanslug10:01:50

@U05224H0W and the dev/rebel that's also an alias in the deps.edn?

thheller10:01:35

yes thats just a namespaced keyword

thheller10:01:45

but deps can be "inherited" from system configs

thheller10:01:59

so you can also define an alias in ~/.clojure/deps.edn

thheller10:01:19

if you want something that is generic and not specific to a project

urbanslug11:01:43

@U05224H0W So in this case there's likely a deps.edn file along the lines of:

{:aliases 
  {:dev/rebel ...}}
right?

urbanslug11:01:51

Yes it's that, rebel. Okay, let me state the larger issue. I'm trying to get familiar with this tool edge but to understand it I think I need to figure out yada. Docs on both are however out of sync with the repo.

urbanslug11:01:39

If one tries to follow https://juxt.pro/yada/manual/index.html#_build_run you'll see the edge repo has no app dir

thheller11:01:04

probably got reorgnized. maybe that is the main dir now?

urbanslug11:01:18

Ya but docs didn't update. Thanks I get it.

kwladyka11:01:30

@lennart.buit if you don’t care, then :opt instead of :req or define spec which let ::bugged be nil

Saikyun13:01:59

I have a protocol which defines the function last. how do I refer this function without replacing clojure.core/last?

Alex Miller (Clojure team)13:01:40

Either fully qualify with the namespace where the protocol is defined or alias that namespace and refer to it via the alias

borkdude13:01:03

@saikyun you can also use :refer-clojure to exclude last from core and then refer to clojure.core/last when you need it.

borkdude13:01:29

but that can become confusing, since last is probably used often

borkdude13:01:40

Is there a variation of or in which the first non-nil value (e.g. false) is returned?

sgerguri13:01:38

#(first (drop-while nil? %)) Bind it to a suitable name and there's your function.

borkdude13:01:29

yeah, I know how to write a function for it, that wasn’t my question 🙂 the difference between a function and a macro like or is that or short-circuits. of course I could also write my own or.

sgerguri13:01:54

I'm quite certain this will do the same, drop-while will yield a lazy seq which will be realized by first so it should stop the moment you get an actual head of your seq back.

borkdude13:01:51

yeah, but then you should first create a lazy-seq of things you already don’t want to evaluate. e.g.: (or 1 (throw (Exception. "no")))

5
joelsanchez14:01:38

(defn some-nil [coll]
  (when-let [s (seq coll)]
    (if (some? (first s))
      (first s)
      (recur (next s)))))
=> #'dev/some-nil
(some-nil [nil false true]) 
=> false
?

joelsanchez14:01:00

derived from the source of some

erwinrooijakkers14:01:26

Does not short-circuit

erwinrooijakkers14:01:15

user=> (some-nil [nil false (throw (Exception. "no"))])
Exception no  user/eval149 (NO_SOURCE_FILE:7)

erwinrooijakkers15:01:02

What problem are you trying to solve that you need this type of logical operator?

borkdude15:01:15

Suppose you’re reading a configuration option where false is a valid option. Then you could have: (or* (read-from-env …) (read-from-java-opts …) (read-from-file …) (throw. (Exception. "no option provided"))) It’s no problem to write this macro myself, but I already solved it another way

👍 5
borkdude13:01:14

yeah, or (first (filter some? ...)), but I wanted to avoid first filter 😉

Roger14:01:48

what about (some (complement nil?) ...)

borkdude14:01:18

user=> (some (complement nil?) [false])
true

kirill.salykin14:01:46

(some identity …) should work

borkdude14:01:57

nope, same problem 😜

sgerguri13:01:31

I can't think of a way of implementing this with some without an if expression inside off the back of my head - but if anyone can I'd love to hear it!

Saikyun13:01:40

okay, so there's no way to somehow say that the protocol just extends last to include this new datatype?

Saikyun14:01:31

I'm trying to use name.stadig.deque to get deques, but for some reason it has separate a function for last

borkdude14:01:31

@saikyun you can try (:refer-clojure :exclude [last]) in your ns form

Saikyun14:01:55

@borkdude sorry if I'm being unclear, I would want to use clojure.core/last as well. I guess I should've phrased the question as "can I implement last for more types in clojure jvm?"

Saikyun14:01:19

I remember reading that some it wasn't possible with some protocols in clojure jvm, but it is in clojurescript or something like that

borkdude14:01:36

last is a sequence function. if you implement ISeq that should just work out of the box

Saikyun14:01:00

oh, huh. I wonder why the deque defines its own interface then

borkdude14:01:05

but you cannot extend-type ISeq, because it’s not a protocol

Saikyun14:01:05

I'll try changing that then 🙂

Saikyun14:01:34

is ISeq different from Sequable?

borkdude14:01:03

ISeq has first, next and rest. Seqable has a seq function that tells how to turn something into a seq

Alex Miller (Clojure team)14:01:15

An ISeq is a sequence. Seqable is something that can return a sequence when asked (via seq)

Saikyun14:01:45

hmm, I think I understand why they define their own last

Saikyun14:01:08

(last [this]
    (array-last (if (and (zero? (alength suffix))
                         (identical? empty-deque child)
                         (identical? empty-deque substack))
                  prefix
                  suffix)))

Saikyun14:01:55

so I guess I should just qualify that last. it's just kind of sad that it's conceptually the same last as clojure.core/last

Saikyun14:01:27

eit seems that clojure.core/last works on the deque as well. maybe their own last is faster or something

Saikyun14:01:35

it seems to use a mutable array internally

Saikyun14:01:09

thanks for the help 🙂

Saikyun14:01:43

and thanks for the link @alexmiller

orestis15:01:49

I’m using clojure.tools.namespace.repl/refresh but it seems my :after symbol is not called. I’m not sure why. Should it be in the user namespace or can it live anywhere?

Geoffrey Gaillard17:01:02

Can I assume that the clojure.core/hash of too clojure sets are always equal if the sets are equal by value? I have a use case where I'd like to store the hash in a database in order to find a set by its hash. Can too different values have the same hash with this implementation?

hiredman17:01:35

that is a hash collision

andy.fingerhut17:01:45

There are far more possible sets than there are 32-bit hash values, so there must be collisions. And in practice if you start generating similar random sets, e.g. of integer values, you can find such collisions without a lot of trouble.

andy.fingerhut17:01:40

Maybe more detail than you care to know, but there are non-Clojure Java sets that have inconsistent hash values, even though they are = to Clojure sets. If you restrict your attention to Clojure sets of Clojure values, you should avoid those oddities.

mpenet17:01:38

depends on what's in the set too, no guarantee that hashCode is stable accross process executions in theory

andy.fingerhut17:01:57

Article containing even more details, if you are interested: https://clojure.org/guides/equality

andy.fingerhut17:01:57

Certainly in Clojure, most hashes of Clojure values changed from pre-Clojure-1.6 to Clojure 1.6-and-later, but pretty stable except for that change. But sure, the hash function could change again in a future Clojure version, or perhaps because of changes in Java hash functions on some values that Clojure calls out to (I don't recall implementation details well enough to know whether Clojure even uses any Java hash code any more, for Clojure values).

mpenet17:01:41

a set could contain some java instance of Foo with an unstable hashcode in theory

mpenet17:01:07

unstable across process executions I mean

andy.fingerhut17:01:17

Sure, that falls outside of my qualification of "Clojure values" that I slipped in above 🙂

mpenet17:01:59

all in all relying on hash sounds like a bad idea imho

mpenet17:01:43

but that's just me

bronsa17:01:28

Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application. 

bronsa17:01:34

from the javadoc

bronsa17:01:41

so an unstable hashcode is illegal

WhoNeedszZz17:01:44

Whatever happened with that hash function that was discovered that had a very small amount of collisions?

mpenet17:01:00

ah? is that something that changed? I recall reading the opposite

andy.fingerhut17:01:21

@whoneedszzz Do you mean the hash function that Clojure used before Clojure 1.6? It was replaced.

WhoNeedszZz17:01:09

No, I remember a hash function that was discovered by some person a few years back that promised very few collisions, but I haven't seen use of it

andy.fingerhut17:01:12

crypto hashes like SHA / MD5 / etc. promise (or strongly hope) very few collisions, but tend to be a little more expensive to calculate than non-cryptographically-strong ones. Do you recall whether the one you heard of was intended to be cryptographically strong?

WhoNeedszZz20:01:25

Found it - Buzhash.

mpenet17:01:36

also is that true for execution in runtime from vendor A to vendor B?

bronsa17:01:49

@mpenet notice the difference between "during an execution" and "need not remain consistent from one execution to another"

bronsa17:01:53

maybe that's what you were talking about?

mpenet17:01:01

I need to read this again 😛

mpenet17:01:27

oh yeah that's what I meant

bronsa17:01:00

gotcha then you were correct

bronsa17:01:09

sorry for the noise :)

mpenet17:01:14

no worries

Geoffrey Gaillard17:01:24

They are going to contain UUIDs (so java.util.UUID instances on the JVM), but since the UUIDs are identifying users (or other resources) they are going to repeat a lot since the database contains far less records than the set of 32-bit values. Thanks you all for the quick answer, and thank you for the article. I shall read it. Do you know any function/library providing non-colliding hashes (or very low probability, mathematically speaking)? Or an alternative clean way to create a unique identity?

andy.fingerhut17:01:35

@ggaillard We may be talking all around your question, without solving your problem here. Depending on how long this database will last, relying on someone else's hash function implementation may be a bad idea, since it may change more quickly than the lifetime of your database.

mpenet17:01:53

replikativ has something like that I think

andy.fingerhut17:01:01

Unless you have an implementation of the hash function that you have source code to that you believe to be stable for longer than your database.

mpenet17:01:12

but it has other tradeoffs

hiredman17:01:22

because a hash is a summary they contain fewer bits of information then exist in the value being hashed, collisions will always be at least theoretically possible

hiredman17:01:03

you can construct a perfect hash, but you need to know the set of values a head of time

WhoNeedszZz17:01:21

This is getting to be a lot of discussion on this topic. Can you guys please start a thread to keep track?

Geoffrey Gaillard17:01:03

Sorry about that, thank you for the reminder

👍 5
hiredman17:01:31

I've been there wanting to use a set as a key in some database and trying to come up with some scalar value which maps to the set, and there is no satisfying way to do it

hiredman17:01:07

a hash is not great, maybe a bloom filter gets closer, but still has issues

Geoffrey Gaillard17:01:42

If there is no perfect solution, an acceptable solution might be way enough.

Geoffrey Gaillard17:01:12

If a bloom filter can at least reduce my search space by an order of magnitude I'd be happy with it.

andy.fingerhut17:01:23

If the set elements are UUIDs or other things with a total order that you can sort them on, would sorting the set elements and using a string representation of that sorted order work?

Geoffrey Gaillard17:01:03

It would work I guess, but the string might get huge… If I do it this way I need to find out what will be the maximum set size, and I don't know that yet

Geoffrey Gaillard17:01:25

Let's think of it pragmatically: If I hash the sets, I might get collisions. But even if I get some collisions the probability is still small, so it might be acceptable. But if the hash changes because the implementation changed, I'll get into trouble. So I'll have to find a way to hash a set that won't change over time/machine/process/version. A string might do it, but might get huge. A bloom filter might produce some false positives, but it might be acceptable anyway. I don't know how to persist a bloom filter yet though. You all gave me a lot to think about, and I think I might take the time to refine my requirements and constraints before asking other questions 🙂 Thank you very much!

andy.fingerhut17:01:10

The string I mentioned would never have collisions, but yes might get too large.

andy.fingerhut17:01:49

Even if you had a hash function that didn't have collisions, and had a small result, were you planning on storing the full set in the database, in any form at all? If so, what technique were you planning to use? Perhaps storing its elements across different database entries?

Geoffrey Gaillard18:01:47

I'm using a SQL relational database, and the current use case is to store users and groups of users. So I have a Group (id) table, a User (id) table and UserGroup (group_id, user_id) join table. I want to find as fast as possible a group having an exact set of user. Not a subset nor a superset. SQL got set operations, but don't provide a way to compute set equality in one operation. And with this table design I have to perform a GROUP BY group_id HAVING count(*) = <size of the user set>, thus iterating with a nested loop at most over all groups. So I was thinking that hashing the set and storing the hash would gave me direct access…

andy.fingerhut19:01:36

If you are willing to iterate over all groups that have the same hash, then a decent hash function can help significantly reduce the number of sets you would need to check in a slower way for precise membership equality, often down to 1 candidate, but even if occasionally down to several, would be much faster than checking all with say, the same number of elements.

👍 5
mccraigmccraig19:01:02

@ggaillard sort the set and reduce with the v5 uuid constructor fn - that will give you a stable, deterministic hash of your set, with as low a probability of collision as the nsa’s best crypto engineers of the 1990s could devise

Geoffrey Gaillard19:01:21

Thank you! Will look into it

mccraigmccraig20:01:33

i'm currently happily using clj-uuid for a V5 implementation: https://github.com/danlentz/clj-uuid#namespaced-v3v5-identifiers

Geoffrey Gaillard20:01:43

It looks a lot like what replikative/hasch (posted by mpenet) is doing with (uuid5 (edn-hash <value>)) for this use case

mccraigmccraig20:01:58

oh, i hadn't seen edn-hash before - yes, that's similar, although perhaps more general (in that it's designed to do all the hard work of distinguishing your general edn structure before feeding it to a hash fn)

orestis18:01:38

Bumping a previous question — how should I debug a tools.namespace.repl.refresh invocation not calling my :after function? I’m calling it like this: (c.t.n.r/refresh :after 'nosco.dev/go) — where nosco.dev/go is a no-args function as instructed. The refresh works (I see a bunch of refreshed namespaces printed out) but the function is not called (nor do I see any exception).

kulminaator20:01:38

hitting an interesting java interop issue at last that i'm having trouble solving 🙂

kulminaator20:01:20

if you were to declare a java function that expects a biconsumer string-string lambda you would declare it as public void foo (BiConsumer<String, String> aLambda) { .... aLambda.accept("a-string", "another-string"); .... } , something like this ... and whenever other java code tries to invoke it then javac is smart enough to check if that lambda is actually capable of consuming the string string argument pairs

kulminaator20:01:54

but if i expose a function from clojure ... how do i achieve the same typehint so that java invokers would be aware of the bi-consumer expected ?

kulminaator20:01:16

so i need at leat to write pure java interfaces for the "hinting layer"

kulminaator20:01:44

a bit sad 😞

markmarkmark20:01:27

you could write something that reifies the interface that you need without it being too hateful

hiredman20:01:04

you are thinking of the reverse

kulminaator20:01:35

yes i figured the same, reify is in the opposite direction of my issue 🙂

ghadi20:01:44

Because of type erasure in the JVM there's no way to ensure

ghadi20:01:53

"don't pass it non-strings"

hiredman20:01:21

my guess is it is more a question of providing intellisense

kulminaator20:01:51

it's not about runtime checks 🙂 , it's about making life easier for coworker who invokes my clojure function from java

kulminaator20:01:23

so that he wouldn't feed my code a biconsumer that expects an elephant and a teapot as argument types

hiredman20:01:52

but that is just dynamic typing right?

hiredman21:01:47

clojure is a dynamic language, no getting around it

kulminaator21:01:27

well it does look kind of weird that i can say (defn foo [^String a-string] .... ) but not (defn foo [^java.util.List<String> many-strings] ...) ...

kulminaator21:01:48

but as said, i'm not married to the biconsumer interface, so i can declare my own, more restrictive, it will have to do

ghadi21:01:14

type erasure happens in the jvm

hiredman21:01:30

nah, it happens in the java compiler

hiredman21:01:49

java the language has generics

ghadi21:01:50

Also, ^String doesn't actually mean the parameter is a string

hiredman21:01:56

the jvm doesn't

ghadi21:01:12

clojure will still make the function signature Object, and do local casts to string where necessary

hiredman21:01:19

type hints are not type declarations

kulminaator21:01:35

i understand that part

kulminaator21:01:14

i'm not trying to alter that part either 🙂 ... all i need to do is to define type hints for people invoking the code from java ... so that their intellij could be helpful for them when invoking my code

hiredman21:01:34

there are huge issues with that as well

hiredman21:01:03

the classes etc created by clojure may not be visible until you load the clojure code

kulminaator21:01:13

well the gen-class part etc. i already solved

kulminaator21:01:50

so i'm generating a nice jar file that has nicely callable functions ... just wanted the parameter types to be more precise

markmarkmark21:01:58

you could create the interface in a java file and then implement the interface in clojure

kulminaator21:01:11

instead of just java.util.List 🙂

hiredman21:01:19

and stop aot compiling your clojure code

hiredman21:01:37

(after you write a stub with whatever types in java)

kulminaator21:01:58

gen-class with :methods and matching -some-method is cleaner when you look from the java side

kulminaator21:01:05

they won't even know it's clojure they are invoking

hiredman21:01:22

sure, I mean, you write a facade in java and invoke clojure like that

kulminaator21:01:29

i'm not struggling with invoking itself 🙂

hiredman21:01:44

expose your java facade then with whatever types you want

kulminaator21:01:50

was just struggling with the generic part which while being a mimic (since the compiler throws it all away in the end) , is helpful at dev time

kulminaator21:01:07

at least helpful in the way their intellij will draw a bold red line around the elephant and teapot

hiredman21:01:24

I am an old foe of clojure's aot compilation (I've seen it cause real head scratchers) so I am generally of the opinion that the design that leaves it out is best, in this case writing some facade in java (so it gets intellisense and types, maybe even javadocs), and having it call clojure via clojure.java.api seems best

seancorfield21:01:24

I agree with @hiredman that if you want IntelliJ to provide type hints, you're going to have to write a facade in Java and then call into Clojure behind the scenes. The Java API that Clojure exposes is really nice -- we rely very heavily on it in production and have done for years to integrate Clojure into our legacy apps.

kulminaator21:01:37

well this piece of code has just one entry point (calling one singe function) and what follows is a bunch of clojure all the way

PT21:01:15

Is type hinting loses effect at function boundaries? In the code below I would expect that no reflection is needed, but lein check says it does: Reflection warning, core.clj:26:43 - reference to field getDisplayLanguage can't be resolved. Why? Please explain type hinting rules. (I've read https://clojure.org/reference/java_interop#typehints and googled this, but can't see why it is working as it does in this case.) (ns core (:import [java.util Locale Locale$Builder])) (defn get-default ^Locale [] (let [l (Locale/getDefault)] ;; local usage of the result of a static method: ;; no type hint is needed to avoid reflection: (println "gd" (.getDisplayLanguage l)) l)) (defn getd2 [] (let [l (get-default)] ;; local usage of the result of a type hinted return value of an fn: ;; no new type hint is needed to avoid reflection: (println "gd2" (.getDisplayLanguage l)) l)) (defn -main [& _] (let [l (getd2)] ;; indirect usage of the result of a type hinted return value of an fn: ;; type hint is ineffective, reflection is used (println "Hello, type hinting World!" (.getDisplayLanguage l))))

hiredman21:01:51

type hinting is purely local to function boundaries

hiredman21:01:31

type hints are not type declarations, type hints are not propagated

hiredman21:01:55

there is nothing like a type inference algorithm going on behind the scenes

hiredman21:01:35

it is helpful to consider clojure in the context of being dynamic and code reload and things being redfined

hiredman21:01:20

it is not safe to conclude getd2 will always return a whatever because the current definition defines it as returning a whatever

kulminaator21:01:20

was a small surprise to me too at some point that clojure typehints dont enforce anything really

kulminaator21:01:12

i was passing through a completely wrong type and the machine happily hummed along 👼

hiredman21:01:18

type hints are just that, hints to the compiler when dealing with interop, if you don't do interop they do nothing

Alex Miller (Clojure team)22:01:58

(except long and primitive double type hints)

PT22:01:01

Thanks for the info. Then https://clojure.org/reference/java_interop#typehints is quite misleading for me, stating that "Once a type hint has been placed on an identifier or expression, the compiler will try to resolve any calls to methods thereupon at compile time. In addition, the compiler will track the use of any return values and infer types for their use and so on, so very few hints are needed to get a fully compile-time resolved series of calls." Now I think this could be used to deduce the behavior you describe above: "Type hints are metadata tags placed on symbols or expressions that are consumed by the compiler."

hiredman22:01:17

the docs on the page are maybe correct if you add a modifier like "within a function" somewhere

seancorfield23:01:41

It's probably worth mentioning that Clojure's "unit of compilation" is a single top-level form -- so when getd2 is compiled, it has no type hint attached to indicate what it might return, and when -main is compiled that definition of getd2 with no true hint is what it sees being used.

seancorfield23:01:56

It would be incorrect in a lot of cases for Clojure to automatically tag the return type of a function based on whatever it deduces the type of the last expression in its body to be (which would be the only way the type deduced for l inside getd2 to be propagated out of the function).

seancorfield23:01:27

Does that help @771?

PT21:01:33

Thanks for your explanation, it helps! (Honestly, I thought that type inference is capable of determining the correct type in many cases, e.g. in the case of getd2's return type, and Clojure does it IF it is "hinted" to do so: I thought that once I type hinted something, Clojure respects the explicit intention and avoids reflection in all cases where it can, based on the hint I gave. I understood the http://clojure.org reference (linked previously) in this way too. After the answers I got here I tend to suspect that Rich H. doesn't want to encourage the usage of type hinting, so limits its capabilities.)

seancorfield21:01:03

Yeah, real world Clojure can get a lot done without type hints -- if you're not worried about the performance of reflection, you almost never need type hints. If you decide to worry about performance due to reflection, it's still most often surprising just how few type hints you really need -- and they tend to be very localized, which I really appreciate.

PT21:01:45

I agree, I never needed type hints, I "decided to worry about it" :)) just because I am working on a free sw whose usage I don't want to hinder by possible performance bottle necks. I found also that very few type hints were enough to eliminate all reflections -- just I didn't understand why exactly those type hints are needed / why exactly there... And the reference doc didn't help me here.

5