This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-11-06
Channels
- # announcements (12)
- # babashka (34)
- # beginners (85)
- # calva (76)
- # cider (14)
- # clj-kondo (49)
- # cljs-dev (32)
- # clojure (418)
- # clojure-europe (3)
- # clojure-france (4)
- # clojure-italy (17)
- # clojure-losangeles (8)
- # clojure-nl (5)
- # clojure-norway (2)
- # clojure-spec (2)
- # clojure-uk (88)
- # clojuredesign-podcast (4)
- # clojurescript (49)
- # clojurex (75)
- # clr (2)
- # core-async (13)
- # cursive (6)
- # datomic (57)
- # duct (31)
- # emacs (6)
- # fulcro (25)
- # graalvm (67)
- # graphql (13)
- # hoplon (1)
- # java (6)
- # juxt (11)
- # kaocha (5)
- # keechma (2)
- # leiningen (16)
- # mount (1)
- # off-topic (19)
- # pathom (2)
- # pedestal (1)
- # re-frame (11)
- # reagent (21)
- # reitit (22)
- # rewrite-clj (1)
- # shadow-cljs (98)
- # spacemacs (5)
- # sql (16)
- # tools-deps (8)
- # vim (28)
- # xtdb (4)
@hermannkevin Yes, the arguments are treated as a vector of values and used as a key into the cache. Why?
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.
You could specify set
so that a set of values instead of a vector of values is used.
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.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=>
Is there a (quick) way how to generate lein's project.clj (or at maven's pom at least) from deps.edn file?
My goal is to install a lib (which only uses deps.edn) as a jar into my local maven repo
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
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.
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
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
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 "."]
I assumed the varargs was somehow gracefully integrated. Good to know how it's done for future reference.
I want to call the variable arity string form: https://docs.oracle.com/javase/7/docs/api/java/nio/file/Paths.html
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
@retrogradeorbit how did you annotate the argument? That should have been sufficient
user> (Paths/get ^String ".")
Syntax error reading source at (REPL:2357:29).
Metadata can only be applied to IMetas
I'm about half way through the documentation: https://logicblocks.github.io/pathological/
I want to create a symlink, but another library for clojure nio was missing symlink functionality. But I see yours has it! 👍
Happy to throw together a getting started if it would be useful
@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.
So:
(Paths/get "." (into-array String []))
should do it.user> (Paths/get "." (make-array String 0))
#object[sun.nio.fs.UnixPath 0x3ae4beac "."]
Yep, either of those would work
No problem
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.
They get flushed when you extend the protocol I think, maybe you could abuse that
@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?
presumably
What would be the simplest way of serving a static directory of files via http with clojure?
hi, please advice, how I can invoke java method with var string args?
(java.nio.file.Paths/get (into-array String ["/tmp"]))
this doesnt workclose, since the first argument isn’t varargs you need this:
(java.nio.file.Paths/get "/tmp" (into-array String []))
it’s a known issue and you can vote for it here: https://ask.clojure.org/index.php/4224/java-method-calls-cannot-omit-varargs
oh, thanks!
I'm looking for an option name in a parser to parse the former keywords, would :aliased-keyword
sound good?
the ::keyword is an "auto-resolved keyword"
it resolves to a "qualified keyword"
aliases are really a feature of namespaces, not keywords
a symbol or keyword qualifier may or may not match a namespace alias
or maybe I'll make a separate option :auto-resolve
so it can be orthogonal of keywords maybe
only applies to keywords
well, also maps I guess
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?
(s/cat :x ::x :y ::y :z ::z (s/* (s/cat :t1 ::thing1 :t2 ::thing2)))
?
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
in short, no
although you could wrap it in (s/nonconforming ...) to get literally the input back
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
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
aliases are an independent naming space
and because they only appear in qualifiers, they never collide with var names
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
generally, prefer keywords for "special" enumerated values, yes
so something like this:
$ lein run "[::foo ::str/foo]" "{:resolve-alias {:current user, str clojure.string}}"
[:user/foo :clojure.string/foo]
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?user=> (every? (fn [n] (> n 0)) [3 2 1])
true
user=> (every? (fn [n] (> n 0)) [])
true
user=>
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.
every?
returns a boolean since its about the entire collection. some
does not as its about an individual element. the ?
is appropriately missing
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.
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.
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
also see and
, s/and
, and more in Clojure
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
as someone is fond of saying "people are surprised by many things"
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
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).
I'd be super surprised if (every? satisfies? constraints) returned false when I had no constraints
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.
If you had to implement every? yourself for example, the behavior would be more apparent to you.
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. 😝
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.
"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"
that's two vacuous truths that are relevant to normal business logic
and yeah, in recursion, vacuous truths are common as base cases at the end of recursion
so datastructures built and destroyed recursively (like a linked list) are sometimes called inductive datastructures
"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.
@didibus I literally implement that logic, in multiple apps
> 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.
the behavior for the empty collection relies on a vacuous truth
just like in my example
@noisesmith Can you describe a use case that needs it like that?
one thing is without vacuous truth, (not (every? pred ...))
would differ from (some (complement pred ...))
and that would be quite annoying
@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
an empty list of conditions means the request is always valid
Woa, that's like exactly one of the use case I was thinking where this would lead to possible huge security vulnerabilities
it's not a vuln because I literally map from action to checks, and error on not found
it's only a default to allow access if you explicitly provide an empty list of checks
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).
@didibus the perms, in code, are a hash map literal from action to checks
the user simply submits an action, the code uses the mapping
@didibus a mapping from action to validations is much harder to get wrong than a huge nested conditional
@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
it's a really hard thing to get wrong - an empty vector stands out like a sore thumb
Ok, but that's different. You're just saying it would never be empty because of some other reason
no, I'm saying relying on vacuous truth maps intuitively to domain logic such that the permission checking code is easier to maintain
right, this is standard, it's not just a clojure thing, and especially with recursive code it just makes sense
Hum.. ok I didn't understand then. What part of the logic benefits from the vacuous truth?
in a recursive function (defn all-true [conditions] (if (first conditions) (and ((first conditions)) (recur (rest conditions))) true))
we don't usually need to recur for this, we have functions like every?
it relies on the vacuous truth that if a list of conditions is empty, any assertion you make about each item is true
it's not just true in this case - this is a clear example of it being true
I'm saying that in most domain, an empty list of condition being true is misleading. And so is all vacuous truth statement.
and I'm saying that's false and providing my counter examples
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.
not just for that case
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
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)
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.
Like, should-reserve-room? Well clearly you don't what to reserve the room if there's no members showing up
no, more like raise an alert to management if any members didn't attend, no applicable members means all checks pass, no alert
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.
Wait... raise an alert? That would be on true, so every? Would raise an alert even if there was no member
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.
It's when a programmer like me innocently is thinking in english rather than math that bugs might occasionally pop out of using every?
@isak right, and in a program that answer would be correct
@didibus but that's still wrong, (if (not (every? ...)) ...)
@noisesmith yea I know, but in real life the "right answer" would be "not applicable"
@noisesmith what do you mean in a program that would be correct? That sounds like 100% a bug
this is exhausting, sorry, I don't see this discussion going anywhere
in real life, you can "throw an exception", or refuse to accept the type constraint of the question
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.
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.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
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.
it isn't so much that it is super beneficial as it is an emergent property of the most widely used kinds of logics
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.
the deep rooted ultra useful benefit is that all programming languages, formal systems and programmers are familiar with these definitions and expect them
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.
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
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.
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.
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.
"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
that piece of code is pretty contextual and it could work or not work depending on how everything is set up
if having no tasks means every task is completed or there’s nothing stopping you from disbursing a payment, would be fine
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.
I think that's a great example. Please find me a business owner who wants people paid for doing nothing.
i don’t really see the point of this conversation anymore. the bug isn’t with the every? it’s with the code itself.
the bug is a poor understanding and translation of the spec in an informal system into a formal system
And so I wouldn't be surprised if you can find quite a few bugs in the wild introduced by every?
You can always say a bug is a poor understanding and translation. All bugs are like that
i feel like you just answered your own question
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.
and your suggestion is to diverge from the common behavior from logic, mathematics, and most programming languages
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.
i agree the docs could probably be more clear
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
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
i think the “here’s why” is pretty convincing but that’s just my opinion
it’s also consistent with other programming languages (like ruby)
because math
it's all adopted wholesale from java / maven
there's a path, all entries on that path are merged, any resources are looked up based on that shared root
@deleted-user one thing that helped me immensely was using
in the repl
It helps to open a jar file using winzip or some other zipping tool and just peak at the inside.
that looks something up on classpath, and shows where it was found (if anywhere)
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"]
an editor that is worth using can usually open a jar directly
pom.xml is a file used by maven
maven is a tool for managing classpath
leiningen and deps.edn and boot all know how to use maven artifacts and repositories
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.
an uberjar is a merge of many jars
Uberjar is just when you put the files your app needs and the files all your dependencies also need together into one jar
it's a list of roots that can be checked for the files
each root can be checked for a path
Which can point to jars as well, in which case it knows that means all files in the jar
right, maven is a tool that adds files and directories to the classpath
and lein, deps.edn and boot are maven compatible
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.
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.
the classpath is literally a list of directores and jar files, I don't understand what youu are saying here
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
the classpath is a list of locations, when you want a resource, the resolver looks for that resource under each entry in the path
whether that location is a directory or jar file (or even url!) is an implementation detail
it could be I just didn't understand what you were saying
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
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
in fact, adding a .class file to the classpath doesn't work - the classpath is a list of locations not individual resources to load
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)
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
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
Like you need one path for every jar and every directory (including every nested dir)
not unless one directory should contain multiple roots
you need one entry for each root, and the thing you look up should be asked for with a relative path
you don't usually ask for "all things matching core.clj" but rather "the first available thing matching the lookup clojure/core.clj"
resources are things you can load via the class path
clj files, class files, xml files, whatever, it's the same rules
this is why I suggested using io/resource
- you can feed it an item and it returns the first resource matching that item
it's what clojure is doing when you require, and what java is doing when you reference a class by name
if app
is on classpath, asking require for subdir.helper
make it use app/subdir/helper.clj
unless something else is found first
the nesting is automatic and ubiquitous
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)
right
nested things are looked up by a nesting in the query - sorry that could probably be expressed more clearly
Hum.. okay I was wrong then. Maybe it was just the requirement to explicitly list all jars that was annoying me then.
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
so you will have multiple entries in one project, but unless your setup is pathological they won't be nested
Ya thanks. That explanation based on roots actually makes it a lot easier to reason about
But you can also explicitly tell clj which one to use by passing --main
if I remember correctly
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.
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.
And apparently, there are also vacuous falsehood. And in both cases, the term vacuous is used because it just points at an edge case.
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.
the word vacuous means empty in this sense, so a statement that is true only because it’s applied to the empty set
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.
I can believe it was chosen as the convention because of its utility in mathematical proofs
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.
a lot of computer science is mathematics, though
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.
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
I'm not sure why you're persisting with this rather pointless argument...?
You're the only one here disagreeing with how everyone else here says (every? even? [])
should behave I think?
So you're the one making it contentious 🙂
And I think I do now. But people still seem to tell me I do not understand. So I keep looking
Do you think (every? even? [])
should return something other than true
?
or that (some even? [])
should return something other than nil
?
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.
What about (and)
and (or)
(returning true
and nil
respectively)?
(and how would a different result there play into the whole 0-arity for identity / monoid thing?)
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.
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?
.
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.
(apply + coll)
should produce zero if coll
is empty, for example and (+)
is zero.
Does.
And it should. So the actual behavior is the correct behavior.
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.
Because +
is a monoid.
this is also a useful wiki if you want more info https://en.wikipedia.org/wiki/Universal_quantification
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.
@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)
Really? OK, that's interesting. So you'd expect those expressions to have different semantics?
I guess that's what I don't understand about your position.
The different between Car[]
and Object[]
:rolling_on_the_floor_laughing:
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.
Elephant e = null; // valid Java
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?
Rather than a comp sci angle, you mean?
Well, that depends what you believe the point of a high level programming language is.
Consistency with predicate logic is a goal you can have, but its not a requirement of a programming language to be consistent with it.
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".
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.
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.
(I meant to say "in a book I am reading on that topic" -- it is "Practical Logic and Automated Reasoning" by John Harrison)
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.
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)
@U0CMVHBL2 I wasn't the one arguing the semantics of vacuous BTW. That was before I joined this thread 🙂
My last comment most accurately reflects what Harrison says.
@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.
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.
“Every car in my driveway is not a car” is a beautiful way of saying that my driveway is empty
Not really. It means it contains no cars. It could contain many bicycles and lawn mowers and the statement is still true.
ah yes that’s what I mean
empty of cars. I just thought it sounded like poetry
beautiful and clear to someone who knows the meaning in classical logic. Confusing as heck to most, and sounds like you are talking nonsense.
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.
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".
And it is a meaning that is useful for many purposes, but not necessarily all purposes.
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.
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