Fork me on GitHub
#clojure
<
2019-11-06
>
Appo703:11:29

does memoization care about the order of arguments?

seancorfield03:11:44

@hermannkevin Yes, the arguments are treated as a vector of values and used as a key into the cache. Why?

Appo703:11:09

i got a function I'd like to memoize and ideally I'd want to ignore the order

seancorfield03:11:09

Look at org.clojure/core.memoize -- it lets you use metadata on a function definition so you can provide a function to apply to the arguments before they are used as a cache key.

seancorfield03:11:35

You could specify set so that a set of values instead of a vector of values is used.

Appo703:11:36

thanks, that looks like what I want!

seancorfield03:11:21

Example (`memo` is an alias for clojure.core.memoize):

user=> (defn ^{:clojure.core.memoize/args-fn set} foo [a b c] (println 'foo a b c) (+ a b c))
#'user/foo
user=> (def bar (memo/memo #'foo))
#'user/bar
user=> (bar 1 2 3)
foo 1 2 3
6
user=> (bar 3 2 1)
6
user=>
That second call uses the memoized function even tho' the arguments are in a different order.

seancorfield03:11:19

You could provide the metadata at the point of memoization if you don't control the source of the function

user=> (def bar (memo/memo (with-meta foo {:clojure.core.memoize/args-fn set})))
#'user/bar
user=> (bar 1 2 3)
foo 1 2 3
6
user=> (bar 3 2 1)
6
user=>

Appo704:11:40

thanks, that worked for me.

jumar08:11:15

Is there a (quick) way how to generate lein's project.clj (or at maven's pom at least) from deps.edn file?

jumar08:11:18

My goal is to install a lib (which only uses deps.edn) as a jar into my local maven repo

vlaaad09:11:13

clj -Spom will create a pom

jumar09:11:49

The best way I could find:

# make sure this is in the deps.edn file under :aliases
  :depstar
  {:extra-deps {seancorfield/depstar {:mvn/version "0.3.1"}}}

# then build the jar
clojure -A:depstar -m hf.depstar.jar clindex-0.2.4-SNAPSHOT.jar

# finally install it
mvn org.apache.maven.plugins:maven-install-plugin:3.0.0-M1:install-file -Dfile=clindex-0.2.4-SNAPSHOT.jar -DgroupId=clindex -DartifactId=clindex -Dversion=0.2.4-SNAPSHOT -Dpom=pom.xml

vlaaad09:11:07

can deploy (or install locally) jars you've built

jumar09:11:04

Ah, that's nice - thanks!

Crispin11:11:37

Hi there. I need some help with Java interop. I'm trying to call a java method but clojure keeps picking the wrong implementation of the method to call. I want the one taking a string argument, but it chooses the one taking the URI argument, and then tries to cast the string I pass in into a URI.

Crispin11:11:39

user> (import '[java.nio.file Paths])
java.nio.file.Paths
user> (Paths/get ".")
Execution error (ClassCastException) at user/eval12360 (form-init19142548285076119.clj:2354).
java.lang.String cannot be cast to java.net.URI

jumar11:11:30

I think the problem is that it's actually a varargs method: https://docs.oracle.com/javase/7/docs/api/java/nio/file/Paths.html

jumar11:11:27

Which is an illusion created by the Java compiler and not well supported by Clojure. You can try something like this:

(Paths/get "." (make-array String 0))
#object[sun.nio.fs.UnixPath 0x1f4d61ce "."]

Crispin11:11:30

Ah thanks! Interesting.

Crispin11:11:22

I assumed the varargs was somehow gracefully integrated. Good to know how it's done for future reference.

Crispin11:11:39

annotating the argument with ^String doesnt help

Crispin11:11:13

how do I help clojure choose the other method?

Toby Clemson11:11:48

A little tangential but I've written a Clojure wrapper for the NIO2 stuff in Java which might be helpful: https://github.com/logicblocks/pathological

Toby Clemson11:11:22

@retrogradeorbit how did you annotate the argument? That should have been sufficient

Crispin11:11:41

user> (Paths/get ^String ".")
Syntax error reading source at (REPL:2357:29).
Metadata can only be applied to IMetas

Crispin11:11:09

@toby924 that library looks perfect! Thanks very much for pointing that out!

Toby Clemson11:11:48

I'm about half way through the documentation: https://logicblocks.github.io/pathological/

Crispin11:11:51

I want to create a symlink, but another library for clojure nio was missing symlink functionality. But I see yours has it! 👍

Crispin11:11:02

I will read your source with intrigue

Toby Clemson11:11:05

Happy to throw together a getting started if it would be useful

Toby Clemson11:11:19

@retrogradeorbit on your specific question, there are 2 arities of (Paths/get ...), one that takes a single argument of type URI and one that takes two arguments, the first a string, the second, variadic of strings. To invoke with just a string, you also have to provide an empty variadic argument which for Clojure is an empty array.

Crispin11:11:04

oh ok interesting. Im not a Java coder... so not something Im familiar with

Toby Clemson11:11:20

So:

(Paths/get "." (into-array String []))
should do it.

Crispin11:11:29

user> (Paths/get "." (make-array String 0))
#object[sun.nio.fs.UnixPath 0x3ae4beac "."]

Crispin11:11:39

well I'll be!

Toby Clemson11:11:42

Yep, either of those would work

Crispin11:11:58

love it. thanks heaps @toby924

alexyakushev13:11:59

Hi, does anybody know if it is possible to somehow flush protocols' MethodImplCache caches? I'm using tools.reloading a lot in a project, and it looks like these caches are the reason for a memory leak. since they retain the class objects that transitively hold onto some state that I recreate between reloads.

Alex Miller (Clojure team)13:11:01

They get flushed when you extend the protocol I think, maybe you could abuse that

alexyakushev13:11:48

@alexmiller Thank you! I will try that. Right now, it looks like the exact offender is clojure.core.protocols/coll-reduce. I will try to dummy-extend it. But I assume that this has to be done for every non-reloaded protocol that ever sees my "heavyweight" classes, right?

Cas Shun14:11:57

What would be the simplest way of serving a static directory of files via http with clojure?

kirill.salykin15:11:28

hi, please advice, how I can invoke java method with var string args?

(java.nio.file.Paths/get (into-array String ["/tmp"]))
this doesnt work

schmee15:11:09

close, since the first argument isn’t varargs you need this:

(java.nio.file.Paths/get "/tmp" (into-array String []))

borkdude15:11:03

what's the proper name for ::user or ::s/user compared to :mynamespace/user?

borkdude15:11:13

aliased keyword? namespaced keyword (but the latter one is too)?

vlaaad15:11:47

latter if fully-qualified keyword

vlaaad15:11:10

former is aliased I'd say, all of them namespaced

borkdude15:11:47

I'm looking for an option name in a parser to parse the former keywords, would :aliased-keyword sound good?

Alex Miller (Clojure team)15:11:00

the ::keyword is an "auto-resolved keyword"

Alex Miller (Clojure team)15:11:14

it resolves to a "qualified keyword"

borkdude15:11:24

ah, so :auto-resolved-keyword

Alex Miller (Clojure team)15:11:56

aliases are really a feature of namespaces, not keywords

Alex Miller (Clojure team)15:11:30

a symbol or keyword qualifier may or may not match a namespace alias

borkdude15:11:38

or maybe I'll make a separate option :auto-resolve so it can be orthogonal of keywords maybe

borkdude15:11:46

or just :resolve

borkdude15:11:52

:resolve-alias... hmm

Alex Miller (Clojure team)15:11:53

only applies to keywords

Alex Miller (Clojure team)15:11:05

well, also maps I guess

borkdude15:11:07

but also to maps no?

dpsutton15:11:03

i think i asked this a while ago and unfortunately its lost to the history. is there a way to spec a seq such that its first three members match specs and then the elements after that come in pairs matching a spec? ie, [:X :Y :Z], [:X :Y :Z :thing1 :thing2], where thing1 and thing2 can repeat but only in pairs?

Alex Miller (Clojure team)16:11:04

(s/cat :x ::x :y ::y :z ::z (s/* (s/cat :t1 ::thing1 :t2 ::thing2))) ?

dpsutton16:11:55

yeah that works perfectly. Is there an easy way to conform it back to the original vector? unfortunately this produces a map when we really want the original vector back

dpsutton16:11:07

(and thank you for responding)

dpsutton16:11:16

yeah. was afraid of that.

Alex Miller (Clojure team)16:11:27

although you could wrap it in (s/nonconforming ...) to get literally the input back

dpsutton16:11:40

thank you. i think s/nonconforming is new to us and seems quite helpful

Alex Miller (Clojure team)16:11:54

it's technically not in the public api (intentionally un-doc'ed) and requires some care as it is surprising when used in combination with other things

dpsutton16:11:31

will tread lightly and not complain about edge cases then 🙂

borkdude16:11:20

I thought *ns* would be a nice option to designate the current namespace, like in:

$ lein run "::foo" "{:resolve-alias {*ns* bar}}"
:bar/foo
but apparently that can also be an alias 😕
user=> (require '[clojure.string :as *ns*])
nil
user=> (*ns*/starts-with? "foo" "f")
true

Alex Miller (Clojure team)16:11:46

aliases are an independent naming space

Alex Miller (Clojure team)16:11:08

and because they only appear in qualifiers, they never collide with var names

borkdude16:11:00

what is an example of a qualifier?

borkdude16:11:55

oh you mean var names in the current namespace, unqualified

borkdude16:11:01

yeah, I was only talking about for an options map, to talk about the "current" namespace, to resolve ::foo, *ns* is apparently not a good choice, since that can already be taken (although that would be very weird). Maybe :current is better

Alex Miller (Clojure team)16:11:14

generally, prefer keywords for "special" enumerated values, yes

borkdude16:11:28

yeah, it's very nice that symbols and keywords can't conflict

borkdude16:11:09

so something like this:

$ lein run "[::foo ::str/foo]" "{:resolve-alias {:current user, str clojure.string}}"
[:user/foo :clojure.string/foo]

borkdude16:11:37

maybe :auto-resolve is a nicer name

Brian16:11:22

I'm trying to validate the contents of a vector such that my function returns true or false. For instance, I have this function

(defn valid? [vals]
  (map (fn [num] (> num 0)) vals))
which returns (true true true false true true). How can I write a function which only returns true or false depending on if the values in that vector are all greater than zero?

Brian16:11:49

Amazing thank you @borkdude!

chepprey16:11:14

every? has "noteworthy" behavior on empty collection:

chepprey16:11:18

user=> (every? (fn [n] (> n 0)) [3 2 1])
true
user=> (every? (fn [n] (> n 0)) [])
true
user=> 

chepprey16:11:28

I'm curious to hear the Clojure philosophy on this

Henry16:11:42

seems natural to me. every item in the collection satisfies the predicate

walterl16:11:14

Python too:

python
>>> all([])
True
>>> any([])
False

mloughlin17:11:14

C# LINQ's All: >true if every element of the source sequence passes the test in the specified predicate, or if the sequence is empty; otherwise, false.

walterl16:11:43

What seems more out of place, is that every?'s counterpart is some and not some?

chepprey17:11:45

Huh, that's noteworthy too (`some` vs some?)

dpsutton17:11:51

every? returns a boolean since its about the entire collection. some does not as its about an individual element. the ? is appropriately missing

chepprey17:11:14

I have no problem with how every? works. I think I just had some deep-rooted instinct that imagined a boolean-flag not being set to true until the predicate had run at least once.

chepprey17:11:59

As an exercize, implemented every? in Java (my most familiar language) and can see that even my instinct is probably wrong. The method is trying to falsify something. Start assuming everything is ok. Iterate collection and look for something that violates the predicate. If the collection is empty, nothing violates it. So I had bad instinct here.

dpsutton17:11:41

the best way i know of it is, "if its false, show me a counter example"

didibus17:11:53

To be honest, I wouldn't be surprised if there isn't a few bugs out there due to every? returning true of empty set

dpsutton17:11:33

in mathematics this is know as "vacuously true"

💯 8
chepprey17:11:06

"you're not wrong" vs. "you're definitely right" 🙂

Alex Miller (Clojure team)17:11:24

also see and, s/and, and more in Clojure

chepprey17:11:23

user=> (and)
true
user=> 
Indeed!

didibus17:11:42

Ya, it doesn't matter what its known of as in math :p. I'd still bet there's a few bugs due to it out there. I'm not saying it's the wrong behaviour. Just it doesn't really fit he principle of least surprise unless you know about the vacuous truth

Alex Miller (Clojure team)17:11:27

as someone is fond of saying "people are surprised by many things"

Alex Miller (Clojure team)17:11:11

I would find it very surprising for these functions to not respect the vacuous truth aspect of logic and I think you'll find this is common not just in math but across many programming languages

4
chepprey17:11:23

Totally agree on likelihood of bugs with every?. I'm not as convinced this is elevated to a violation of least-surprise principle. Especially given my daily encounters of code that violates surprise -- I find these violations far more... sociopathic and sinister. every? is much more of a nuanced "oops" (IMHO).

bfabry17:11:39

I'd be super surprised if (every? satisfies? constraints) returned false when I had no constraints

didibus17:11:35

That's true that either way it would surprise someone somewhere and possibly lead them to a bug. I think that's a cognitive load that's often downplayed when learning higher level languages like Clojure though.

didibus17:11:07

If you had to implement every? yourself for example, the behavior would be more apparent to you.

didibus17:11:28

That's why I believe the REPL is an invaluable tool

didibus17:11:57

Because validating your assertions is really important in Clojure

👍 4
didibus17:11:59

Btw, the wikipedia article does say: > Outside of mathematics, statements which can be characterized informally as vacuously true can be misleading. Such statements make reasonable assertions about qualified objects which do not actually exist. 😝

didibus17:11:20

And it goes on to say that even in math, vacuous truth is mostly of no interest, except as a base case for inductive proofs. So I do wonder what of interests it is in a comp-sci context, unless you're implementing an inductive proof.

hiredman17:11:31

recursion is induction

noisesmith17:11:33

"every datum should satisfy every constraint in this list of constraints, if there are no constraints they automatically pass, if there is no data it automatically passes"

noisesmith17:11:48

that's two vacuous truths that are relevant to normal business logic

hiredman17:11:49

so inductive proofs are deeply relevant in computer science

noisesmith17:11:43

and yeah, in recursion, vacuous truths are common as base cases at the end of recursion

hiredman17:11:59

so datastructures built and destroyed recursively (like a linked list) are sometimes called inductive datastructures

didibus17:11:38

"every datum should satisfy every constraint in this list of constraints, if there are no constraints they automatically pass, if there is no data it automatically passes" Except, by the wikipedia article.and my own impression, I believe that for most domain outside of math, that's not going to be the correct or common requirement.

hiredman17:11:31

the wikipedia article is incorrect

hiredman17:11:37

s/math/logic/

hiredman17:11:22

programming languages are symbolic logics

noisesmith17:11:25

@didibus I literally implement that logic, in multiple apps

didibus17:11:30

> so datastructures built and destroyed recursively (like a linked list) are sometimes called inductive datastructures I don't believe vacuous truth matters here. That's just a different use of the word induction, but it's no longer relevant to the vacuous truth as far as I can tell.

noisesmith17:11:53

the behavior for the empty collection relies on a vacuous truth

noisesmith17:11:01

just like in my example

didibus17:11:05

@noisesmith Can you describe a use case that needs it like that?

dpsutton17:11:01

one thing is without vacuous truth, (not (every? pred ...)) would differ from (some (complement pred ...)) and that would be quite annoying

noisesmith17:11:02

@didibus a request against the foo service requires permissions [bar baz quux], instead of hard-coding this check, I map each request to a vector of conditions the requesting account must pass

noisesmith17:11:18

an empty list of conditions means the request is always valid

didibus17:11:47

Woa, that's like exactly one of the use case I was thinking where this would lead to possible huge security vulnerabilities

didibus17:11:09

Why would you default to allow access?

noisesmith17:11:13

it's not a vuln because I literally map from action to checks, and error on not found

noisesmith17:11:28

it's only a default to allow access if you explicitly provide an empty list of checks

didibus17:11:02

How does that work from the user side? They submit an empty form?

isak17:11:07

I just checked, and even SQL follows this logic with the ALL operator, which is interesting because SQL sometimes makes similar things a syntax error (e.g., x IN () - an empty list).

noisesmith17:11:27

@didibus the perms, in code, are a hash map literal from action to checks

noisesmith17:11:38

the user simply submits an action, the code uses the mapping

bfabry17:11:45

ruby too...

irb(main):001:0> [].all? {|a| false }
=> true

noisesmith17:11:31

@didibus a mapping from action to validations is much harder to get wrong than a huge nested conditional

didibus17:11:35

But if the checks is empty it will allow the action right?

bfabry17:11:40

and python

>>> all([])
True

noisesmith17:11:18

@didibus sure, it would also be an error to say (def perm-check (constantly true)) - that's a logic bug in both cases, it's not a problem with the technique

noisesmith17:11:51

it's a really hard thing to get wrong - an empty vector stands out like a sore thumb

didibus17:11:40

Ok, but that's different. You're just saying it would never be empty because of some other reason

didibus17:11:53

Not that you rely on every? returning true when empty

noisesmith17:11:23

no, I'm saying relying on vacuous truth maps intuitively to domain logic such that the permission checking code is easier to maintain

bfabry17:11:37

and java

jshell> Arrays.asList().stream().allMatch(e -> false)
$1 ==> true

noisesmith17:11:03

right, this is standard, it's not just a clojure thing, and especially with recursive code it just makes sense

didibus17:11:36

Hum.. ok I didn't understand then. What part of the logic benefits from the vacuous truth?

noisesmith17:11:57

in a recursive function (defn all-true [conditions] (if (first conditions) (and ((first conditions)) (recur (rest conditions))) true))

noisesmith17:11:11

we don't usually need to recur for this, we have functions like every?

noisesmith17:11:50

it relies on the vacuous truth that if a list of conditions is empty, any assertion you make about each item is true

didibus17:11:07

Okay, we're arguing at different level

noisesmith17:11:39

it's not just true in this case - this is a clear example of it being true

didibus17:11:55

I'm saying that in most domain, an empty list of condition being true is misleading. And so is all vacuous truth statement.

noisesmith17:11:12

and I'm saying that's false and providing my counter examples

didibus17:11:03

But I understand that for some base case, you want empty to default to true, mostly for inductive proofs and maybe a few recursive logic not sure.

didibus17:11:18

But those are for the domain of math

noisesmith17:11:22

not just for that case

bfabry17:11:43

look I'm going to stop spamming the channel @didibus but it's also true for javascript, SQL, and excel. so I think the world just doesn't agree with you

noisesmith17:11:00

also for things like "every condition returns true for this input" (empty condition list - always true) or "every member must attend this meeting" (no members, always true)

didibus17:11:41

Ya, I understand this isn't special to Clojure. I'm not against what Clojure did or not. I just feel when leveraged as domain logic, it is misleading. And the wikipedia article acknowledges that. So I feel the world might agree either way.

didibus17:11:38

When would you want every member must attend this meeting, no member to be true?

didibus17:11:13

Like, should-reserve-room? Well clearly you don't what to reserve the room if there's no members showing up

isak17:11:50

thats an ANY question though

8
noisesmith17:11:54

no, more like raise an alert to management if any members didn't attend, no applicable members means all checks pass, no alert

chepprey17:11:03

The tension isn't between math/logic vs prog languages, the tension is between math/logic and "common sense". For another example: me and wife getting ready to go out somewhere. Wife: "should I wear the blue shirt or the red shirt?" Me: "Yes." Wife: smacks me.

👍 4
didibus17:11:34

Wait... raise an alert? That would be on true, so every? Would raise an alert even if there was no member

chepprey17:11:45

Mathematically I'm right. In common sense, I'm not. Math "or" is not english "or".

isak17:11:24

i think a better example of didibus's point would be your boss asking you "have you completed all your tasks?", and you say "yes" when you haven't been given any tasks.

didibus17:11:32

(if (every? not-attend members) (raise-alert))

chepprey17:11:46

It's when a programmer like me innocently is thinking in english rather than math that bugs might occasionally pop out of using every?

noisesmith17:11:48

@isak right, and in a program that answer would be correct

noisesmith17:11:13

@didibus but that's still wrong, (if (not (every? ...)) ...)

isak17:11:14

@noisesmith yea I know, but in real life the "right answer" would be "not applicable"

bfabry17:11:40

the boolean function can't return "I haven't been assigned any tasks"

didibus17:11:47

@noisesmith what do you mean in a program that would be correct? That sounds like 100% a bug

didibus17:11:53

In expected behavior

noisesmith17:11:23

this is exhausting, sorry, I don't see this discussion going anywhere

isak17:11:25

in real life, you can "throw an exception", or refuse to accept the type constraint of the question

didibus18:11:53

Hum.. that's kinda what I mean. You now need additional checks to avoid introducing a defect from the empty set treatment of every?. So, we can all agree to disagree and I'm not suggesting changing the default, you need to know it and deal with it. Being consistent with all other languages is probably best. And like Alex said, defaults are never always right or always wrong, there's always someone surprised no matter what. Now, personally, I think it makes total sense to say that this behavior is often misleading. That misleading property is inherent to the vacuous truth. Math chose the default convenient to the use cases most common in math. And I think programming languages chose to be consistent with math. But that does mean that when using every? to implement business logic and rules for most non math domain, you should be careful to special handle the empty set.

kwladyka18:11:29

Deploy to Clojars4s
user=> 
Run clojure -A:deploy
  clojure -A:deploy
WARNING: Specified aliases are undeclared: [:deploy]
Clojure 1.10.1
user=> 
I would expect exit code error here with such an error. Just my feedback. I didn’t see why it doesn’t work, because of no error. I forgot I changed the alias name.

didibus18:11:46

(if (every? :completed tasks) (disburse-payment) (withhold-payment))

didibus18:11:52

:pre [(every? some arg)]

hiredman18:11:35

if the payment is (apply + (map :cost tasks))

hiredman18:11:45

you can either understand the contentions of logic and use them to your advantage or stub your toe on them every time you interact with a formal system

8
didibus18:11:38

To be honest, that's what I'm looking for here. I'm trying to see if there is a deep rooted ultra useful benefit to the default that I'm missing, but if I knew, I'd realize it can be leveraged very often to simplify logic.

didibus18:11:56

But I've yet too see someone bring it up for every?

hiredman18:11:08

I would recommend diving deeper in to the wikipedia logic entries

hiredman18:11:49

it isn't so much that it is super beneficial as it is an emergent property of the most widely used kinds of logics

hiredman18:11:06

a programming languages are logics

didibus18:11:12

In your example, I see your point, like if the conditional is deciding if you do something or not to the set, and since the set is empty, everything you'd do to it following would mostly be no-op anyways.

bfabry18:11:02

the deep rooted ultra useful benefit is that all programming languages, formal systems and programmers are familiar with these definitions and expect them

didibus19:11:32

That would be totally fair.

didibus19:11:34

Then the reason would just be, you needed a default, and someone chose truthy a while back and everyone else just kept consistent with it. No higher level justification.

didibus19:11:37

Btw, that's why it bothers me Clojure doesn't call it all and any.

bfabry19:11:22

I don't think it's a good idea to pretend there is no higher level justification even if it makes things easier for those learning

didibus19:11:12

Okay, I know I'm starting to sound stubborn, but it's really because I'm trying to see if there is a higher level justification in this case and I've yet to find or understand it. The only one I found was inductive proofs and some cases of recursions. And those are fine reasons in math. But for programming don't really seem to apply. So for programming it really just seems the reason was consistency with math.

didibus19:11:40

And that's all good if you come from math. But if you don't, it will be inconsistent with common sense. So if you don't, well you just have to learn it and deal with it.

didibus19:11:31

Which isn't always the case. There's a lot of seemingly odd cases in Clojure which are actually done that way on purpose to enable better and more natural composition.

bfabry19:11:27

"all good if you come from math" boolean logic comes from propositional logic which is math. you can't get away from it. if you start redefining things you get things that make no sense, not just things that are counterintuitive. if (every? pred X) is false it follows that (every? #(not (pred %)) X) is true correct? Now how do you square that with your desire that every? on an empty collection return false because it's intuitive? both (every? pred X) and (every? #(not (pred X)) X) would return false, which is about the most counter-intuitive thing I can think of

bfabry19:11:25

eh, that example sucks. I concede it's all an arbitrary shell game

chrisulloa18:11:57

that piece of code is pretty contextual and it could work or not work depending on how everything is set up

chrisulloa18:11:10

if having no tasks means every task is completed or there’s nothing stopping you from disbursing a payment, would be fine

isak18:11:46

yea that is not a good example. Better would be: if you completed all your tasks for the day, and there was at least one, you can go home early.

didibus18:11:06

I think that's a great example. Please find me a business owner who wants people paid for doing nothing.

hiredman18:11:33

that is an error in programming though

hiredman18:11:57

you are missing the "there exists a task" part of the spec

didibus18:11:22

Yes, that's exactly my point.

didibus18:11:26

This is a bug

didibus18:11:46

Because the behavior of every? is misleading to its user

chrisulloa18:11:56

i don’t really see the point of this conversation anymore. the bug isn’t with the every? it’s with the code itself.

hiredman18:11:11

the bug is a poor understanding and translation of the spec in an informal system into a formal system

didibus18:11:12

And so I wouldn't be surprised if you can find quite a few bugs in the wild introduced by every?

didibus18:11:45

You can always say a bug is a poor understanding and translation. All bugs are like that

didibus18:11:16

And every bug introduced by mutability are poor understandings of mutation

chrisulloa18:11:33

i feel like you just answered your own question

didibus18:11:49

What do you mean?

didibus18:11:05

I think maybe we've derailed a bit. I'm making a software correctnes argument, and ya, I lack empirical evidence for this. But just like I believe mutability leads to defects, I believe every?'s behavior on empty set does as well. Both because of the same reason, the cognitive burden and mismatch to the programmer.

dpsutton18:11:47

and your suggestion is to diverge from the common behavior from logic, mathematics, and most programming languages

didibus18:11:36

Well... I mean... isn't that what FP advocates try to tell everyone else 😋. But no, in this case I think it would be worse to do so. I'm not sure how to best address this issue. My take will be to bring it up early on and mention it say in clojure-docs that you might be surprised by its handling of empty set.

chrisulloa18:11:26

i agree the docs could probably be more clear

didibus18:11:46

So if someone asks, why does every? return true on empty. I won't say... oh here's why? (super smart reason that is very convincing) I'll just say: ya that's misleading, you just need to be aware of it, sorry, that's how it goes

chrisulloa18:11:27

the source code is clear about it and there are examples on clojuredocs of how it behaves with empty collections, but it’s not obvious from the docstring itself

chrisulloa19:11:05

i think the “here’s why” is pretty convincing but that’s just my opinion

didibus19:11:36

Well, "because math" has never really worked on me, but ya, that could be personal

chrisulloa19:11:10

it’s also consistent with other programming languages (like ruby)

didibus19:11:36

Well consistency can be why Clojure did it. But why did the first language do it?

didibus19:11:41

Which all other copied

chrisulloa19:11:49

because math

didibus19:11:08

Oh, that emoji looks way different on slack

didibus19:11:16

That's not the right emotion

didibus19:11:23

That's better

didibus19:11:00

And math seems to have done it to simplify the base case of inductive proofs

ghadi19:11:26

can we take this to #deadhorses or a thread?

16
ghadi19:11:03

not a problem, just think we've exhausted it

noisesmith19:11:30

it's all adopted wholesale from java / maven

didibus19:11:50

Hum... not sure. But it's pretty simple. We can probably summarize it to you quickly

noisesmith19:11:22

there's a path, all entries on that path are merged, any resources are looked up based on that shared root

noisesmith19:11:13

@deleted-user one thing that helped me immensely was using in the repl

didibus19:11:33

It helps to open a jar file using winzip or some other zipping tool and just peak at the inside.

noisesmith19:11:43

that looks something up on classpath, and shows where it was found (if anywhere)

noisesmith19:11:45

user=> (http://clojure.java.io/resource "clojure/core.clj") #object[java.net.URL 0x14dda234 "jar:file:/Users/justin.smith/.m2/repository/org/clojure/clojure/1.10.0/clojure-1.10.0.jar!/clojure/core.clj"]

didibus19:11:53

Its a zip with extension .jar

noisesmith19:11:08

an editor that is worth using can usually open a jar directly

noisesmith19:11:19

pom.xml is a file used by maven

noisesmith19:11:26

maven is a tool for managing classpath

noisesmith19:11:53

leiningen and deps.edn and boot all know how to use maven artifacts and repositories

didibus19:11:37

Basically Java code is just compiled into .class files. And those are just zipped together into a library which by convention uses .jar as extension. Additional files can be put into that zipped file as well, at your choosing.

noisesmith19:11:08

an uberjar is a merge of many jars

didibus19:11:52

Uberjar is just when you put the files your app needs and the files all your dependencies also need together into one jar

didibus19:11:33

And the classpath is just a list of paths to every file your app needs.

noisesmith19:11:54

it's a list of roots that can be checked for the files

noisesmith19:11:11

each root can be checked for a path

didibus19:11:12

Which can point to jars as well, in which case it knows that means all files in the jar

noisesmith19:11:02

right, maven is a tool that adds files and directories to the classpath

noisesmith19:11:09

and lein, deps.edn and boot are maven compatible

didibus19:11:20

Ya, so its similar. The package manager just downloads all required files, and then creates a list of paths to all of them in correct order.

didibus19:11:51

The only annoying thing is that the paths on the classpath are to a file, not to a directory. So for every single file you need to add a path to it.

noisesmith19:11:59

the classpath is literally a list of directores and jar files, I don't understand what youu are saying here

didibus19:11:15

Hum... am I wrong? I though it didn't do any path expansions. So you if you had three .class files in a folder you'd have to specify 3 paths

didibus19:11:30

Maybe it's just that it doesn't expand to recursive dir

noisesmith20:11:41

the classpath is a list of locations, when you want a resource, the resolver looks for that resource under each entry in the path

noisesmith20:11:03

whether that location is a directory or jar file (or even url!) is an implementation detail

noisesmith20:11:32

it could be I just didn't understand what you were saying

didibus20:11:07

I just meant, you end up having to list way more directories then you'd expect. Because each jar must be directly pointed too, and it's not recursive either

didibus20:11:37

But it seems I was wrong about each .class file having to be listed, for that just putting the dir works, though still it is not recursive

noisesmith20:11:12

in fact, adding a .class file to the classpath doesn't work - the classpath is a list of locations not individual resources to load

didibus20:11:12

Ya you're right

noisesmith20:11:21

a potential source of confustion is you have two kinds of path here a.jar:b.jaar:c.jar (an ordered list of places to look) and clojure/core.clj (a path under one place in which you'd find a thing)

didibus20:11:55

But because it's not recursive, and it doesn't automatically include resources inside jars, you have to list way more paths even for trivial apps

noisesmith20:11:23

and when you ask eg. io/resource for clojure/core.clj what happens under the hood is that the resolver looks for the directory clojure and the file inside it core.clj in each location in order until it finds a match

didibus20:11:31

Like you need one path for every jar and every directory (including every nested dir)

noisesmith20:11:50

not unless one directory should contain multiple roots

noisesmith20:11:12

you need one entry for each root, and the thing you look up should be asked for with a relative path

didibus20:11:41

Hum... I didn't know that. That's for resources?

noisesmith20:11:46

you don't usually ask for "all things matching core.clj" but rather "the first available thing matching the lookup clojure/core.clj"

didibus20:11:54

Does it work for code files as well?

noisesmith20:11:58

resources are things you can load via the class path

noisesmith20:11:11

clj files, class files, xml files, whatever, it's the same rules

noisesmith20:11:51

this is why I suggested using io/resource - you can feed it an item and it returns the first resource matching that item

didibus20:11:02

Hum... I'm pretty sure that never worked for me

noisesmith20:11:29

it's what clojure is doing when you require, and what java is doing when you reference a class by name

didibus20:11:36

If I have /app/core.clj and /app/subdir/helper.clj

didibus20:11:55

I need to add /app and /app/subdir to the classpath

didibus20:11:05

Or require won't find the code

didibus20:11:34

But, maybe I'm wrong

noisesmith20:11:54

if app is on classpath, asking require for subdir.helper make it use app/subdir/helper.clj unless something else is found first

noisesmith20:11:07

the nesting is automatic and ubiquitous

didibus20:11:41

Okay, so then you are saying you'd only need to add the Clojure jar and the root of your source code to the path (assuming you consume no other dependency)

noisesmith20:11:02

nested things are looked up by a nesting in the query - sorry that could probably be expressed more clearly

didibus20:11:25

Hum.. okay I was wrong then. Maybe it was just the requirement to explicitly list all jars that was annoying me then.

didibus20:11:43

But it makes a lot of sense the way you explain it

didibus20:11:48

Since each jar is a different root

noisesmith20:11:06

there's also the fact that in one project you probably put resources that aren't code, and jar files, and source files under three separate directories

noisesmith20:11:29

so you will have multiple entries in one project, but unless your setup is pathological they won't be nested

didibus20:11:57

Ya thanks. That explanation based on roots actually makes it a lot easier to reason about

didibus19:11:19

Right so one more thing that goes in a Jar is a manifest file

didibus19:11:26

Which is metadata

didibus19:11:44

And that tells you what the main file is if there is one

didibus19:11:24

But you can also explicitly tell clj which one to use by passing --main if I remember correctly

didibus19:11:58

Hum... ya not sure.

didibus19:11:16

I mean you can put a custom metadata file in it

didibus19:11:25

That you use for whatever you want

didibus19:11:41

But I don't know if the default manifest file supports custom meta

didibus22:11:10

I recommend not going down the rabbit whole of why every? is true for empty set 😛 As it seems to be a huge point of debate with no clear agreement both in the philosophy and math stackexchanges hehe. The most conclusive statement that seems to always happen is "Just Cause". As I just did, and lost a good afternoon of work.

didibus22:11:39

But, the most logical one is that in propositional logic, For All statements are proved by showing that there are no example disproving the proposition. So a For All statement on an empty set cannot be disproved as no counter-example can be found, since the set has nothing in it, thus implying the proposition to be true.

didibus22:11:48

And apparently, there are also vacuous falsehood. And in both cases, the term vacuous is used because it just points at an edge case.

didibus22:11:06

Where vacuous falsehoods would be its negation if I got that right. Like, (NOT For All of pred empty set) is always false. That is a vacuous falsehood.

didibus22:11:35

And vacuuous is used in the sense: "it doesn't really say anything meaningful"

chrisulloa22:11:32

the word vacuous means empty in this sense, so a statement that is true only because it’s applied to the empty set

chrisulloa22:11:39

i don’t necessarily think it’s not meaningful or just points to an edge case… it’s a pretty useful tool when disproving conjectures for example. used very frequently in math.

didibus22:11:37

Well, I mean that, its just a choice of convention

didibus22:11:09

I can believe it was chosen as the convention because of its utility in mathematical proofs

didibus22:11:48

But that can't be said for programming or philosophy. Which seem to have both chosen it simply to remain consistent with the mathematical convention.

chrisulloa22:11:15

a lot of computer science is mathematics, though

seancorfield22:11:21

As a mathematician (who also has some experience with propositional calculus), I'll say that it is not just a convenient "convention". It's fundamental.

didibus22:11:14

I mean, that seems to disagree with the top voted answers in the math stack exchanges. Where they seem to say it is not a formalism

seancorfield22:11:55

I'm not sure why you're persisting with this rather pointless argument...?

didibus22:11:59

So I don't know who to trust 😛

didibus22:11:26

Actually, I'd say curiousity

didibus22:11:57

But it seems I've found a rather contentious topic to be curious about

seancorfield22:11:43

You're the only one here disagreeing with how everyone else here says (every? even? []) should behave I think?

seancorfield22:11:09

So you're the one making it contentious 🙂

didibus22:11:57

Well, I'm not trying to disagree or agree, just to understand.

didibus22:11:30

And I think I do now. But people still seem to tell me I do not understand. So I keep looking

seancorfield22:11:40

Do you think (every? even? []) should return something other than true?

seancorfield22:11:22

or that (some even? []) should return something other than nil?

didibus22:11:50

I think it could have returned false, and that would have been totally fine, and possibly a better default yes. I do not think we should now go and change it.

seancorfield22:11:24

What about (and) and (or) (returning true and nil respectively)?

seancorfield22:11:08

(and how would a different result there play into the whole 0-arity for identity / monoid thing?)

didibus22:11:09

I don't know about those.

didibus22:11:06

I think each of these are edge cases, and in propositional logic, they thought of a most appropriate default for their usage which made most sense in the context of propositional logic.

seancorfield22:11:01

reduce/transducers/etc rely on (f) returning an identity/init value: +, *, conj, etc. That's your "base" case behind every? as well -- if you could write (apply and (map even? coll)) which is the semantics behind every?.

didibus22:11:05

And I think programming language first tried to implement propositional logic in code, thus copied all of prospositional logic's behavior, including these. And that's why every? returns true on empty coll.

seancorfield22:11:46

(apply + coll) should produce zero if coll is empty, for example and (+) is zero.

didibus22:11:24

Should or does ?

seancorfield22:11:46

And it should. So the actual behavior is the correct behavior.

didibus22:11:29

Right, but then the follow up question is why should it? And that necessitate a practical answer. And the context will matter for the practice. So in my opininon, it should if it is most useful most often.

didibus22:11:02

And I'd agree that for programming (+) returning 0 is most useful most often

seancorfield22:11:18

Because + is a monoid.

didibus22:11:21

But I'm not sure it is the case of (every? pred []) returning true

chrisulloa22:11:47

this is also a useful wiki if you want more info https://en.wikipedia.org/wiki/Universal_quantification

didibus22:11:32

I read that wiki already, and it says > By convention, the formula ∀ x ∈ ∅ P ( x ) {\displaystyle \forall {x}{\in }\emptyset \,P(x)} \forall{x}{\in}\emptyset \, P(x) is always true, regardless of the formula P(x); see vacuous truth.

seancorfield22:11:46

@didibus would you say that (every? pred coll) and (apply and (map pred coll)) should be equivalent? (allowing that you could write the latter directly)

didibus22:11:52

So it seems to be just a convention

didibus22:11:19

I would not say so no.

seancorfield22:11:46

Really? OK, that's interesting. So you'd expect those expressions to have different semantics?

didibus22:11:53

I don't think of the former as a shorthand of the latter.

didibus22:11:14

I'd expect every? to check for existence

seancorfield22:11:34

I guess that's what I don't understand about your position.

didibus22:11:58

Because of statements like: "Every car in my empty driveway is an elephant".

didibus22:11:02

Or the more confusing: "Every car in my empty driveway is not a car"

seancorfield22:11:42

The different between Car[] and Object[] :rolling_on_the_floor_laughing:

seancorfield22:11:29

What type is nil (or null in Java)? Answer: it is every type. null is an Elephant as far as the type system is concerned.

seancorfield22:11:43

Elephant e = null; // valid Java

didibus22:11:28

Well, and that also seems to be a cause of bugs

seancorfield22:11:30

So maybe you're looking at this from the p.o.v. of typed systems and you're overlaying that line of thinking onto this problem?

didibus22:11:04

I think I'm taking it more from a linguistic angle

seancorfield22:11:17

Rather than a comp sci angle, you mean?

didibus22:11:25

If it was called map-and I'd have no issue

didibus23:11:01

Well, that depends what you believe the point of a high level programming language is.

didibus23:11:55

Consistency with predicate logic is a goal you can have, but its not a requirement of a programming language to be consistent with it.

andy.fingerhut23:11:17

Sean, I believe that there are some examples in history (sorry, no link handy to point at it right now) of vacuous either being undefined/meaningless, or perhaps even defined the opposite way of classical/propositional logic. I have heard that some later logicians criticized Aristotle for not defining its meaning, perhaps? It certainly seems reasonable to say "in mathematics, defining it the way it most often is, it makes some mathematical manipulations easier to state without having a special case for the empty set".

didibus23:11:31

Ya, I think I read some people mention on the math stack exchange that intuisonistic logic disagrees with some of the chosen vacuuous truth of propositional logic. I don't know anything about intuisonistic logic, so can't speak to that.

andy.fingerhut23:11:41

According to the introductory chapter on automated reasoning in computers, George Boole's initial boolean logic/calculus defined its "or" as what is more precisely called "exclusive or", rather than the "inclusive or" that is most common today.

andy.fingerhut23:11:03

(I meant to say "in a book I am reading on that topic" -- it is "Practical Logic and Automated Reasoning" by John Harrison)

andy.fingerhut23:11:17

Huh, I just re-read that mention, and I misremembered it. Harrison says that in Boole's original calculus "p+q" has no meaning if both p and q could be true at the same time, much like the expression "x/y" being undefined in arithmetic if y is 0.

chepprey23:11:41

So you mean "exclusive or" defines p+q as false, vs. Boole calculus defines it as "has no meaning" (when p and q are both true)

seancorfield23:11:15

@U0CMVHBL2 I wasn't the one arguing the semantics of vacuous BTW. That was before I joined this thread 🙂

andy.fingerhut23:11:56

My last comment most accurately reflects what Harrison says.

andy.fingerhut23:11:18

@U04V70XH6 I understand. I hesitated to jump in, but it does seem that some thinkers of the past have disagreed on whether such statements should have meaning or not. I personally got used to the vacuous truth perspective at a young age, and it doesn't bother me.

andy.fingerhut23:11:52

I like the following English statement as an example of why it can be confusing or seem nonsensical, a slight modification of an example didibus mentioned above: "Every car in my driveway is not a car" being true when there are no cars in the driveway I certainly can see being something meant to confuse people, not enlighten them.

danielneal08:11:31

“Every car in my driveway is not a car” is a beautiful way of saying that my driveway is empty

andy.fingerhut14:11:00

Not really. It means it contains no cars. It could contain many bicycles and lawn mowers and the statement is still true.

danielneal14:11:28

ah yes that’s what I mean

danielneal14:11:34

empty of cars. I just thought it sounded like poetry

andy.fingerhut14:11:57

beautiful and clear to someone who knows the meaning in classical logic. Confusing as heck to most, and sounds like you are talking nonsense.

didibus17:11:24

Even in classical logic, they know that this is an accident of the current laws of logic, at least from what I gather. And that's why they call it the vacuous truth. As even though it is "true" from the laws, it is devoid of useful meaning.

andy.fingerhut17:11:23

It has a well defined meaning in current laws of logic: either the set you are quantifying over is non-empty, and the statement is true for all of those elements, or the set you are quantifying over is empty, and the statement is defined to be true by the current laws of logic. That is "a meaning", not necessarily "the meaning I wish it had".

❤️ 4
andy.fingerhut17:11:05

And it is a meaning that is useful for many purposes, but not necessarily all purposes.

didibus17:11:42

Assuming the point of classical logic in itself is to define an algebra for logical reasoning. It is meaningless not in the sense that it is never useful in math (though Wikipedia does mention it is of little interest in math as well not sure why), but in the sense that what it says for logical reasoning is meaningless, and does't make logical sense.

didibus17:11:37

At least, I've read multiple sources mention that. But my sources have been the internet so I don't know, might be missing something

didibus17:11:06

Though it makes sense to me as well. Every car in my driveway is an airplane. Is kind of a pointless line of thought.

didibus18:11:25

It wouldn't really make sense to then go and try and defend this as being meaningfully true. That's why you need another type of truth, which is the vacuous truth.

👍 4