I am evaluating some code in emacs, and the representation of the value I'm getting back does not make sense to me. Can someone tell me what type of data structure #:question{...} and #:category{...}
are here?
[#:question{:text "How do you squeeb a thlob?",
:answer "You have to bibidibop it first"}
#:category{:title "Technical"}]
Are they just maps? They seem to behave like maps
Ok, I used type
to figure out the answer - it's a clojure.lang.PersistentArrayMap
.
β―β―β― clj
Clojure 1.10.1
user=> (apropos "namespace")
(clojure.core/*print-namespace-maps* clojure.core/namespace clojure.core/namespace-munge clojure.pprint/*print-suppress-namespaces*)
user=> *print-namespace-maps*
true
user=> {:a/b 1 :a/c 2}
#:a{:b 1, :c 2}
user=> (set! *print-namespace-maps* false)
false
user=> {:a/b 1 :a/c 2}
{:a/b 1, :a/c 2}
user=>
its just a shorthand for printing. {:a/b 1 :a/c 2} -> #:a{:b 1, :c 2}
rather than repeating the namespace a
multiple times it prefixes the map with it
Aaaah - that makes sense.
and you can turn it on and off as you need with *print-namespace-maps*
. (note how i found it with apropos
)
Yes, perfect - thanks! Now that I know what it means, it's fine to have it on, hah.
There are lots of functions in core.clj which contain what seems to me like declarative information which unfortunately are implemented programmatically. E.g.,
(defn sequential?
"Returns true if coll implements Sequential"
{:added "1.0"
:static true}
[coll] (instance? clojure.lang.Sequential coll))
(defn sorted?
"Returns true if coll implements Sorted"
{:added "1.0"
:static true}
[coll] (instance? clojure.lang.Sorted coll))
(defn counted?
"Returns true if coll implements count in constant time"
{:added "1.0"
:static true}
[coll] (instance? clojure.lang.Counted coll))
It seems to me that these functions should have been created declaratively to allow programs to reason about the types. For example an expression such as (fn [x] (and (sequential? x) (not (list? x))))
sometimes returns true, however (fn [x] (and (list? x) (not (sequential? x))))
will not (if I understand correctly). Why? because clojure.lang.IPersistentList
is a subtype of clojure.lang.Sequential
, this means that clojure.lang.IPersistentList
and (not clojure.lang.Sequential)
are disjoint. If that data had been entered a program would be able to do such reasoning.the suggestion to use source-fn
and parse the output with read-string
works pretty well. Here is what I'm able to extract from clojure.core
.
([decimal? BigDecimal]
[seq? clojure.lang.ISeq]
[fn? clojure.lang.Fn]
[vector? clojure.lang.IPersistentVector]
[boolean? Boolean]
[char? Character]
[sequential? clojure.lang.Sequential]
[float? (or Double Float)]
[set? clojure.lang.IPersistentSet]
[reversible? clojure.lang.Reversible]
[map? clojure.lang.IPersistentMap]
[volatile? clojure.lang.Volatile]
[var? clojure.lang.Var]
[string? String]
[uri? java.net.URI]
[double? Double]
[map-entry? java.util.Map$Entry]
[int? (or Long Integer Short Byte)]
[associative? clojure.lang.Associative]
[keyword? clojure.lang.Keyword]
[tagged-literal? clojure.lang.TaggedLiteral]
[indexed? clojure.lang.Indexed]
[counted? clojure.lang.Counted]
[future? java.util.concurrent.Future]
[class? Class]
[sorted? clojure.lang.Sorted]
[record? clojure.lang.IRecord]
[ident? (or clojure.lang.Keyword clojure.lang.Symbol)]
[reader-conditional? clojure.lang.ReaderConditional]
[integer?
(or Integer Long clojure.lang.BigInt BigInteger Short Byte)]
[ratio? clojure.lang.Ratio]
[delay? clojure.lang.Delay]
[ifn? clojure.lang.IFn]
[uuid? java.util.UUID]
[list? clojure.lang.IPersistentList]
[rational?
(or
(or Integer Long clojure.lang.BigInt BigInteger Short Byte)
clojure.lang.Ratio
BigDecimal)]
[number? Number]
[symbol? clojure.lang.Symbol]
[coll? clojure.lang.IPersistentCollection])
π
I'm tempted to write a function to parse core.clj and reverse engineer all these relationships.
how often does core.clj change?
Is there a programmic way for me to get the code for a function such as associative?
without me having to parse core.clj
there is a macro in clojure.repl namespace (preloaded for you in repl session)
(source associative?)
Would this be available even in a batch clojure?
what do you mean?
You can look at the commit history yourself. It changed quite rapidly during 2006-2011 time frame, but has slowed down its rate of change quite a lot since then. The Clojure core developers give a lot of thought to keeping later versions of Clojure as backwards compatible as possible with earlier versions.
if it is part of the repl, I feared it might not be available from a non-interactive session ???
clojure.repl
namespace comes with clojure
so if you want it β add to require
It exists in Clojure, whether you are using a REPL or not. It will print the text of the source code of a function or macro, I think to whatever output stream is the current value if *out*
btw, clojure does not differentiate interactive and non-interactive sessions
oh, it prints the text, doesn't return it as an s-expression π
then use reader to read it into form
looks like source-fn
actually returns the function sexpression
the code for source is
(defmacro source
"Prints the source code for the given symbol, if it can find it.
This requires that the symbol resolve to a Var defined in a
namespace for which the .clj is in the classpath.
Example: (source filter)"
[n]
`(println (or (source-fn '~n) (str "Source not found"))))
> looks likeΒ `source-fn`Β actually returns the function sexpression no, it will return a text
Oh, it returns the string? ouch
there is another macro in the same namespace which you might find usefull)
(doc clojure.repl/source-fn)
Is there a way to convert this string to an s-expression with the symbols in the correct namespace? For example, I could bind ns to nil before calling source to have all namespaces printed in long form?????
source-fn return literate source code as it was written in dedicated file
alas that doesn't work: π
clojure-rte.core> (clojure.repl/source-fn 'list?)
"(defn list?\n \"Returns true if x implements IPersistentList\"\n {:added \"1.0\"\n :static true}\n [x] (instance? clojure.lang.IPersistentList x))"
clojure-rte.core> (binding [*ns* nil] (clojure.repl/source-fn 'list?))
Execution error (NullPointerException) at java.util.concurrent.ConcurrentHashMap/get (ConcurrentHashMap.java:936).
null
clojure-rte.core>
what if I bind ns to the namespace of the symbol before reading the text. I think that won't work either because as I understand the clojure reader does not attach namespaces to symbols it reads π
maybe you could explain what you want to achieve?
But anyway, I can start the experimentation with a hacky parser to find out what I want.
What do I want, I want to reverse engineer the functions like list?
and sequential?
to find out which types they are a predicate for. E.g., the code for list?
is
(defn list?
"Returns true if x implements IPersistentList"
{:added "1.0"
:static true}
[x] (instance? clojure.lang.IPersistentList x))
I would like to write a function which takes list?
as an argument and returns clojure.lang.IPersistentList
.as list?
is a type predicate.
clojure.lang.IPersistentList
this is not a type but interface thowmore correctly: the symbol clojure.lang.IPersistentList
is a type designator. It designates a set of values.
the value of clojure.lang.IPersistentList
is java.lang.Class
https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/IPersistentList.java
similarly integer?
maps to (or Integer Long clojure.lang.BigInt BigInteger Short Byte)
curiouse, how you get that?
I may be able to find a more correct way later, but using source-fn
is certainly enough for a proof of concept. π
proof of concept for what?)
You can take a look if you like: https://gitlab.lrde.epita.fr/jnewton/clojure-rte It is not yet released explicitly, although the repo is publicly readable. It is a port of a Common Lisp package into clojure. One of the challenges (and one of the most interesting parts of the project) is to impose a simple type system onto the java system of classes as viewed from clojure.
I was thinking more about an academic-ish conference dealing with clojure programming.
There have been several Clojure conferences, like the Conj, but they tend to be more practitioner focused than academic. But some of the talks do get into the design and/or implementation of some libraries that add significant functionality to Clojure, sometimes in odd ways.
There are conferences like International Conference on Functional Programming, but I think that tends to focus on Haskell and similar languages, but I'm guessing on that as I haven't looked at its accepted papers for quite a while.
isnβt that achievable with clojure.spec?
That's a good question. There is certainly some overlap. rte, however, is based in finite-automata-theory, and strives to minimize runtime checks. It matches sequences WITH NO backtracking.
as I understand spec is based on backtracking.
also. If you declare to spec that something is both number and also integer, will spec check both? if not, how does it know whether integer? implies number?
Here is an application of rte: https://gitlab.lrde.epita.fr/jnewton/clojure-rte/-/blob/master/dsc.md. I'm not sure how you'd do that with spec.
the overlap of spec and rte is a matter of ongoing research. I'm actually looking for a student to take on that research.
Having trouble finding a student interested in doing clojure related research.
another question about so-called private functions, defined with (defn- ...)
, are they allowed to appear in the expansion of a public macro?
they can, but generally they won't be resolvable
I get an error loading a file that uses a macro which expands to code which references ensure-fns-index
which is private.
hi Alex, sorry I don't understand what you mean by, won't be resolvable....
yeah, that's the "not resolvable" part
do I have to make public, all functions which appear in the expansion of a macro
macros expand into ordinary code, so if ordinary code can't use the private function...
generally, yes
forms spliced in by a macro usually become part of the public API
I came across a bunch of lein projects that seem to easily mix java with clj by adding :java-source-paths
to their project.clj
and where good to go. Can this be done with tool.deps too?
I couldn't find a working example anywhere.
I'm asking because it would be just a single java file, and building a separate java project would be overkill right now.
hmm.. that's surprising. I don't have to like it but I have to accept it.
π
OK, can you remind me how to reference a private symbol, such as within a test case?
I'll put a comment on the docs of defn-
` when I understand it well enough to do so.
not out from the box atm but with most recent clojure cli you can craft something yourself consider this β https://github.com/EwenG/badigeon/blob/master/API.md#badigeonjavacjavac
huh sweet, thank you!
you can invoke through the private var, instead of via the symbol
can't say I've ever done that with a macro, but you'd need to emit code that resolved the symbol to the var
ahh you mean use #'the-name in the macro definition rather than just the-name.
I have a surface-macro which expands to a lower level macro. I didn't really want to make the lower level macro public as its interface involves knowing about the inner-workings of the system. the public macro is designed for the public.
how's this? https://clojuredocs.org/clojure.core/defn-#example-5f74a0fce4b0b1e3652d73c7
off the top of my head, I don't that that works with macros
as I understand it compiling java is permanently out of scope for tools.deps itself, but anyone can write a t.d task that compiles java
same goes for nearly any other lein feature / plugin
Alright makes sense. Thanks!
I'm playing with spec in cljs app and can't make :ret
work with instrument
(s/def ::int int?)
(defn inc2 [x] "not int")
(s/fdef inc2 :args (s/cat ::int int?)
:ret ::int)
(stest/instrument `inc2)
<http://humaid.app|humaid.app>> (inc2 "i")
#error {:message "Call to #'<http://humaid.app/inc2|humaid.app/inc2> did not conform to spec.", ...
<http://humaid.app|humaid.app>> (inc2 3)
"not int"
What am I doing wrong?instrument doesn't check ret specs
Thanks!
apparently it doesn't π looks like private functions are half baked. Perhaps it's better just to use a project specific naming convention?
that is the more common answer - foo* or foo-impl or whatever
π:skin-tone-2:
@jimka.issy I do not know if this affects what you are doing, but note that a single JVM object can implement many Java interfaces, not merely one. The JVM relationship between "class A extends class B" is a tree with "java.lang.Object" at the root, but the JVM relationship between "class A implements interface X" is potentially one-to-many, as well as "interface X extends interface Y" is also potentially one-to-many, so the graph of such relationship when including interfaces can be a pretty much arbitrary directed acyclic graph, I believe.
For example:
user=> (source counted?)
(defn counted?
"Returns true if coll implements count in constant time"
{:added "1.0"
:static true}
[coll] (instance? clojure.lang.Counted coll))
nil
user=> (counted? [1 2 3])
true
user=> (counted? #{1 2 3})
true
user=> (counted? '(1 2 3))
true
So, I have a map that as one of the key values, contains a function. i.e {:foo (fn [] blah-de-blah-blah)}
. I wish to invoke this function, and I end up doing this ((:foo my-map))
. That doesn't feel right. Is there a better way of invoking that function?
seems fine to me
double left parens always look weird
but you get that with higher order stuff returning fns
np π thank you π
That is definitely one of those times when you might realize how absolutely significant parentheses are in Clojure (and other Lisps). A lot of people new to Lisps don't immediately realize that adding extra parens changes the meaning of code (unlike, say, a C/C++/Java/Python arithmetic expression on the right hand side of an assignment, where in most cases extra parentheses are redundant but harmless)
everytime on emacs-lisp or another lisps, I stop to think why they used 2 parens on let
π
, never liked this use-case
Obviously, Rich Hickey didn't like that syntax either π
while I mostly was exposed to emacs lisp first, I am glad I was exposed to clojure's let
first. Likewise never grew to like the other kind, and always have to do a double take when I'm working with them
I would say the same about, say, cond, but I think there are cases where I do like the original cond, although if I remember it more had to do with I preferred how I could manipulate it with lispy
at times, I forget now
Hi! Do you think Luminus is a good framework to start with? I need to make a generic website with database, authentication, a dozen entities and about 50-70 pages.
Yes, this is different from Common Lisp in this way. I always got a kick out of how well CL navigated the duality of contexts.
@gr.evocatus Well, the Luminus template includes a lot of moving parts -- a lot of libraries (Luminus isn't a framework) -- so you'll need to understand most of those libraries to some degree in order to build the app. Depending on where you are in your Clojure journey, that might be okay but it also might be extremely challenging.
I see a lot of beginners try to get started with Clojure by using Luminus to build a "simple web app" and get horribly stuck -- so I always recommend building a "simple web app" using just Ring, Compojure, and maybe Selmer first so they understand some of the basics.
If you're reading Web Development with Clojure -- which features Luminus for the examples -- you might be alright.
@gr.evocatus have you looked at https://github.com/seancorfield/usermanager-example (or reitit/integrant version linked from the readme)?
@hiskennyness hi kenny, I didn't know you were doing clojure. I have lots of questions about things that are confusing in clojure
Iβm keeping an eye on you! Yer doin fine.
Ping me any time!
where do you live ? i.e. which time zone?
I am US east coast. Often on line 3-4am, tho. Crash by 9ish
This is an area of confusion. I need to figure out a way to explain my approach.
What is the best and friendliest forum for presenting clojure stuff, especially bizarre stuff?
I mean, you can publish a document to Github and send a link to it to #off-topic, or #clojure if it is Clojure-related. I'm not sure that I am the best audience for the work you are doing -- I simply wanted to point out the property of JVM interfaces in case you were unaware of it, since several of the Clojure functions you were asking about check whether the argument implements an interface.
Why when I try to add a dependency (that I know is on maven) I get error about not being able to find artifact? Normal internet works in same enviornment...
Could not find artifact io.parsingdata:metal:jar:7.1.0 in central (<https://repo1.maven.org/maven2/>)
Could not find artifact io.parsingdata:metal:jar:7.1.0 in clojars (<https://repo.clojars.org/>)
This could be due to a typo in :dependencies, file system permissions, or network issues.
If you are behind a proxy, try setting the 'http_proxy' environment variable.))
And by 'normal' i mean like ping / wget / ssh from same shellHi all! I'm new to Clojure and the community, hope to make friends and learn lots with you all π
@mksybr This seems to be because it is a pom
artifact and not a jar
artifact...
So you'll get this error:
(! 706)-> clj -Sdeps '{:deps {io.parsingdata/metal {:mvn/version "7.1.0"}}}'
Downloading: io/parsingdata/metal/7.1.0/metal-7.1.0.pom from central
Error building classpath. Could not find artifact io.parsingdata:metal:jar:7.1.0 in central (<https://repo1.maven.org/maven2/>)
https://search.maven.org/artifact/io.parsingdata/metal/7.1.0/pom
It has two modules, core
and formats
, so you'll need to depend on those directly I think instead of the pom version...
(! 710)-> clj -Sdeps '{:deps {io.parsingdata/metal-core {:mvn/version "7.1.0"} io.parsingdata/metal-formats {:mvn/version "7.1.0"}}}'
Downloading: io/parsingdata/metal-core/7.1.0/metal-core-7.1.0.pom from central
Downloading: io/parsingdata/metal-core/7.1.0/metal-core-7.1.0.jar from central
Downloading: io/parsingdata/metal-formats/7.1.0/metal-formats-7.1.0.pom from central
Downloading: io/parsingdata/metal-formats/7.1.0/metal-formats-7.1.0.jar from central
Clojure 1.10.1
user=>