Fork me on GitHub
#beginners
<
2020-09-30
>
soxley04:09:15

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"}]

soxley04:09:51

Are they just maps? They seem to behave like maps

soxley04:09:02

Ok, I used type to figure out the answer - it's a clojure.lang.PersistentArrayMap.

dpsutton04:09:29

❯❯❯ 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=>

dpsutton04:09:18

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

soxley04:09:10

Aaaah - that makes sense.

dpsutton04:09:50

and you can turn it on and off as you need with *print-namespace-maps*. (note how i found it with apropos)

soxley04:09:02

Yes, perfect - thanks! Now that I know what it means, it's fine to have it on, hah.

jimka.issy08:09:42

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.

jimka.issy10:10:58

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])

jimka.issy08:09:14

I'm tempted to write a function to parse core.clj and reverse engineer all these relationships.

jimka.issy08:09:32

how often does core.clj change?

andy.fingerhut08:09:25

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.

jimka.issy08:09:22

Is there a programmic way for me to get the code for a function such as associative? without me having to parse core.clj

delaguardo08:09:07

there is a macro in clojure.repl namespace (preloaded for you in repl session) (source associative?)

jimka.issy08:09:15

Would this be available even in a batch clojure?

delaguardo08:09:34

what do you mean?

jimka.issy08:09:26

if it is part of the repl, I feared it might not be available from a non-interactive session ???

delaguardo08:09:43

clojure.repl namespace comes with clojure so if you want it β€” add to require

andy.fingerhut08:09:30

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*

delaguardo08:09:02

btw, clojure does not differentiate interactive and non-interactive sessions

jimka.issy08:09:42

oh, it prints the text, doesn't return it as an s-expression 😞

delaguardo08:09:08

then use reader to read it into form

jimka.issy08:09:20

looks like source-fn actually returns the function sexpression

jimka.issy08:09:38

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"))))

delaguardo08:09:55

> looks likeΒ `source-fn`Β actually returns the function sexpression no, it will return a text

jimka.issy08:09:16

Oh, it returns the string? ouch

delaguardo08:09:31

there is another macro in the same namespace which you might find usefull) (doc clojure.repl/source-fn)

jimka.issy08:09:56

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?????

delaguardo09:09:02

source-fn return literate source code as it was written in dedicated file

jimka.issy09:09:03

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> 

jimka.issy09:09:50

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 😞

delaguardo09:09:40

maybe you could explain what you want to achieve?

jimka.issy09:09:53

But anyway, I can start the experimentation with a hacky parser to find out what I want.

jimka.issy09:09:37

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.

jimka.issy09:09:49

as list? is a type predicate.

delaguardo09:09:16

clojure.lang.IPersistentList
this is not a type but interface thow

jimka.issy09:09:55

more correctly: the symbol clojure.lang.IPersistentList is a type designator. It designates a set of values.

jimka.issy09:09:31

the value of clojure.lang.IPersistentList is java.lang.Class

delaguardo09:09:37

curiouse, how you get that?

jimka.issy09:09:21

similarly integer? maps to (or Integer Long clojure.lang.BigInt BigInteger Short Byte)

jimka.issy09:09:45

I may be able to find a more correct way later, but using source-fn is certainly enough for a proof of concept. πŸ™‚

delaguardo09:09:19

proof of concept for what?)

jimka.issy09:09:50

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.

jimka.issy09:09:50

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.

delaguardo09:09:23

isn’t that achievable with clojure.spec?

jimka.issy09:09:15

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.

jimka.issy09:09:31

as I understand spec is based on backtracking.

jimka.issy09:09:41

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?

jimka.issy09:09:16

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.

jimka.issy09:09:34

the overlap of spec and rte is a matter of ongoing research. I'm actually looking for a student to take on that research.

jimka.issy09:09:49

Having trouble finding a student interested in doing clojure related research.

andy.fingerhut19:09:36

@ 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.

andy.fingerhut19:09:35

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

jimka.issy22:09:38

This is an area of confusion. I need to figure out a way to explain my approach.

jimka.issy22:09:11

What is the best and friendliest forum for presenting clojure stuff, especially bizarre stuff?

andy.fingerhut22:09:35

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.

jimka.issy10:10:25

I was thinking more about an academic-ish conference dealing with clojure programming.

andy.fingerhut14:10:55

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.

andy.fingerhut14:10:30

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.

jimka.issy13:09:48

another question about so-called private functions, defined with (defn- ...), are they allowed to appear in the expansion of a public macro?

alexmiller13:09:19

they can, but generally they won't be resolvable

jimka.issy14:09:35

hi Alex, sorry I don't understand what you mean by, won't be resolvable....

jimka.issy14:09:57

do I have to make public, all functions which appear in the expansion of a macro

alexmiller14:09:28

generally, yes

jimka.issy14:09:49

hmm.. that's surprising. I don't have to like it but I have to accept it.

jimka.issy14:09:26

OK, can you remind me how to reference a private symbol, such as within a test case?

jimka.issy14:09:23

I'll put a comment on the docs of defn-` when I understand it well enough to do so.

alexmiller14:09:35

you can invoke through the private var, instead of via the symbol

alexmiller14:09:16

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

jimka.issy15:09:58

ahh you mean use #'the-name in the macro definition rather than just the-name.

jimka.issy15:09:08

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.

alexmiller15:09:02

off the top of my head, I don't that that works with macros

jimka.issy16:09:55

apparently it doesn't 😞 looks like private functions are half baked. Perhaps it's better just to use a project specific naming convention?

alexmiller16:09:08

that is the more common answer - foo* or foo-impl or whatever

jimka.issy16:09:38

:+1::skin-tone-2:

jimka.issy14:09:54

I get an error loading a file that uses a macro which expands to code which references ensure-fns-index which is private.

hiskennyness22:09:35

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.

jimka.issy22:09:31

@ hi kenny, I didn't know you were doing clojure. I have lots of questions about things that are confusing in clojure

hiskennyness22:09:05

I’m keeping an eye on you! Yer doin fine.

hiskennyness22:09:32

Ping me any time!

jimka.issy22:09:11

where do you live ? i.e. which time zone?

hiskennyness22:09:54

I am US east coast. Often on line 3-4am, tho. Crash by 9ish

alexmiller14:09:37

yeah, that's the "not resolvable" part

ghadi14:09:15

macros expand into ordinary code, so if ordinary code can't use the private function...

ghadi14:09:05

forms spliced in by a macro usually become part of the public API

rob70314:09:21

I came across a bunch of lein projects that seem to easily mix java with clj by adding :java-source-paths to their project.cljand 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.

delaguardo14:09:07

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

rob70314:09:42

huh sweet, thank you!

noisesmith15:09:11

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

noisesmith15:09:36

same goes for nearly any other lein feature / plugin

rob70315:09:56

Alright makes sense. Thanks!

jehaby15:09:58

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)

> (inc2 "i")
#error {:message "Call to #' did not conform to spec.", ...
> (inc2 3)
"not int"
What am I doing wrong?

alexmiller15:09:39

instrument doesn't check ret specs

dharrigan19:09:24

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?

alexmiller19:09:38

seems fine to me

alexmiller19:09:50

double left parens always look weird

alexmiller19:09:16

but you get that with higher order stuff returning fns

dharrigan20:09:15

np πŸ™‚ thank you πŸ™‚

andy.fingerhut20:09:54

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)

d.ian.b20:09:37

everytime on emacs-lisp or another lisps, I stop to think why they used 2 parens on let πŸ˜…, never liked this use-case

andy.fingerhut20:09:17

Obviously, Rich Hickey didn't like that syntax either πŸ™‚

zdot10120:09:15

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

zdot10120:09:56

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

gr.evocatus22:09:23

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.

seancorfield22:09:29

@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.

seancorfield22:09:45

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.

seancorfield22:09:18

If you're reading Web Development with Clojure -- which features Luminus for the examples -- you might be alright.

seancorfield22:09:37

@gr.evocatus have you looked at https://github.com/seancorfield/usermanager-example (or reitit/integrant version linked from the readme)?

mksybr23:09:00

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 ()
Could not find artifact io.parsingdata:metal:jar:7.1.0 in clojars ()
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 shell

seancorfield23:09:18

@ This seems to be because it is a pom artifact and not a jar artifact...

seancorfield23:09:34

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 ()

seancorfield23:09:08

It has two modules, core and formats, so you'll need to depend on those directly I think instead of the pom version...

seancorfield23:09:15

(! 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=> 

mksybr00:10:49

Ahhh, I see. (I'm not exactly sure of what a pom is, but) I got it working with your advice to include metal-{core,format} (instead of metal) thanks ❀️

seancorfield00:10:58

pom (and bom) artifacts on Maven mean: "I am a list of artifacts" (rather than "I am an artifact"). Some systems know how to take a pom (or bom) and figure out the list of things to fetch automatically. Unfortunately for use, neither Leiningen nor the CLI/`tools.deps.alpha` know how, so we have to do it manually.

seancorfield00:10:54

If you go look at the URL I posted (http://search.maven.org with the version and pom), you'll see it shows a big ol' XML file and the relevant part is

<modules>
    <module>core</module>
    <module>formats</module>
  </modules>

seancorfield00:10:59

so that tells me there will be -core and -formats variants of the artifact that I should be able to download instead.

mksybr00:10:51

Thanks! That explanation helps a lot.

avidrucker23:09:22

Hi all! I'm new to Clojure and the community, hope to make friends and learn lots with you all πŸ™‚