Fork me on GitHub
#beginners
<
2022-04-14
>
j09:04:40

I'm trying to build a local library so that I can reuse code. I see that the library is now in my local .m2 repository, and would like to use it via deps.edn in apps that I'm building. I'm not publishing the library to clojars. The local library I'm trying to build is standalone and will have other clojure apps calling it. What's the correct coordinates for :deps{} in my apps' deps.edn?

pavlosmelissinos12:04:22

Why don't you put it on a git forge like github/gitlab and use it like a normal git dependency?

dorab16:04:50

If you are just sharing this lib with your other projects, then you can access the shared library via the local/root coordinate. See https://clojure.org/guides/deps_and_cli for more.

Serafeim Papastefanos09:04:45

hello friends, I've written a post on using clojure from windows with cmd.exe https://www.spapas.net/2022/04/14/clojure-windows/

👍 1
🎉 1
leifericf12:04:02

I’m trying to determine the absolute minimum for a Clojure application using deps.edn. As far as I can tell, this is it:

.
├── deps.edn
└── src
    └── core.clj
deps.edn only contains an empty pair of curly braces: {}. core.clj doesn’t contain anything yet. So one subdirectory (`src`) and two files: deps.edn and src/<whatever>.clj. Is that all required to fire up a REPL using the clj command using deps.edn?

leifericf12:04:49

Awesome! That’s remarkably clean and simple. I don’t know why I spend so much time messing around with Leiningen, deps-new, templates, etc.

Alex Miller (Clojure team)12:04:41

I don't use any of those /shrug

👍 1
leifericf12:04:18

By the way, kudos for writing https://clojure.org/guides/deps_and_cli. It was straightforward to follow. I just wanted to make sure I had understood correctly how little is required to get rolling.

practicalli-johnny15:04:21

@U064X3EF3 Am I correct in assuming that the Clojure CLI install has the equivalent of :path ["src"] , so that the src/core.clj file in lelfericf's example is found on the class path without needing to be specified in the project deps.edn

Alex Miller (Clojure team)15:04:29

Yes, that's the default in the root deps.edn

👍 1
practicalli-johnny15:04:20

Ah yes, I also found /usr/local/lib/clojure/deps.edn on my file space. Thank you.

Alex Miller (Clojure team)16:04:42

The file on disk is there for legacy but it's not used

Alex Miller (Clojure team)16:04:16

The root is a resource loaded from the tools.deps jar (really the uberjar in the cli)

👍 1
Alex Miller (Clojure team)12:04:27

You don't even need the deps.edn technically

👍 1
leifericf12:04:53

Yeah, I could fire up the REPL with clj without deps.edn. But I think it’s rare that there will be no dependencies for my projects, so I view it as necessary. I tried running clj with an empty deps.end, but that resulted in an error. It was happy as long as it got a pair of {}.

Daniel Shriki14:04:20

Is anyone has a suggestion wether to use amazonica or aws-api (cognitect) for aws integration? - note that we have to use presigned-url (s3) functionality which looks like it is still missing from aws-api, but maybe there are more things to consider. our mainly uses are S3, SQS, and cognito services. happy to hear your thoughts. thanks! 🙂

hiredman16:04:23

The algorithm for signing is well documented, and all the crypto code for it ships with the jvm, so it is a nice little exercise to sit down and write. Zero dependencies.

Adrian Smith16:04:31

I’m trying to replace boot but it’s such a large job I need to do it piece meal, so I want to use new libraries like io.github.clojure/tools.build in the same program as boot for a while but boot can’t pull coordinates off git sha which is the only way that library makes itself available, is there a work around to be able to load libraries from git via boot even if manually?

hiredman17:04:39

You could use

hiredman17:04:05

Might be a chicken and egg problem though

hiredman17:04:46

(the readme does show gitlibs as being in a maven repo)

hiredman17:04:31

But I guess that wouldn't get you transitive deps of tools.build

Adrian Smith09:05:54

Hi sorry to disturb @U064X3EF3 any news on this one?

Alex Miller (Clojure team)12:05:59

Sorry, what's the question?

Adrian Smith15:05:33

Ah we might have come to a work-around internally on this, we’re trying to use tools.build from a boot system but tools.build is only available as a git sha at the moment we’d like it to be on maven ideally I think our work around is going to be to create a library that uses tools.build, then turn that into a jar, then require that jar into our other repos

Alex Miller (Clojure team)15:05:54

oh, gotcha. haven't gotten around to it.

👍 1
Alex Miller (Clojure team)17:04:16

I'm planning to make it available as a maven artifact too, just haven't gotten around to it

👍 1
ossosoi loso19:04:43

Is there a way to bind a value to a symbol similar to intern? I.e. how would I define intern-locally ?

(do (defmacro m [x exp] `(intern-locally [~x "foo"] ~exp)) (m 'y y)) => "foo"

dpsutton20:04:31

if you want a macro to introduce a locally bound function you can do that (and they are called anaphoric macros)

qp=> (defmacro anaphor [& forms]
       `(let [~'x 1]
          ~@forms))
#'nocommit.qp/anaphor
qp=> (anaphor (+ x 1))
2

ossosoi loso20:04:30

Thanks, I also want to provide a symbol as an argument which the forms in your example would use.

noisesmith20:04:48

isn't that just going to be let ? unless I'm missing something

noisesmith20:04:56

(ins)user=> (defmacro m [x exp] `(let [~x "foo"] ~exp))
#'user/m
(ins)user=> (m y y)
"foo"
user=>

noisesmith20:04:21

it actually gets more complex if you want y to be quoted in the input list

noisesmith20:04:08

anyway, let and fn are the things that bind bind a value to a symbol in a lexical scope

ossosoi loso20:04:21

The actual problem involves parsing forms and that’s how I get a symbol. I think I’ve thought of a workaround though which doesn’t answer my original question though.

noisesmith20:04:44

if the symbol isn't known at compile time, a macro is not the right way to do this

ossosoi loso20:04:09

Essentially intern does what I want where you provide a symbol argument and assign a value to it.

noisesmith20:04:10

you'll need eval, or to write an interpreter for your DSL

noisesmith20:04:28

yes, because namespaces are mutable, and intern isn't a macro

ossosoi loso20:04:04

I’m not sure I follow about the symbol being available at compile time. I’ll try to give an example of what i’m trying to do.

ossosoi loso20:04:53

(let [a 1
      b (+ a 1)
      ;; the line below is generated by the macro (pos? b) is the form provided to the macro
      b (if (pos? b) "foo" "bar")]
  b) => "foo"

ossosoi loso20:04:12

ill do this in an editor 😄

ossosoi loso20:04:28

so when traversing the let form b is available as a symbol (on line 4).

noisesmith20:04:40

OK so that's the expansion, what would the input look like?

ossosoi loso20:04:13

the input is the form (pos? b)

noisesmith20:04:32

no, I mean the whole thing, including the macro call

ossosoi loso20:04:06

(let [a 1
      b (+ a 1)
      :do-test (pos? b)]
  b)

andy.fingerhut20:04:43

The only macro call in what you wrote is let

andy.fingerhut20:04:10

Macro calls always look like (macro-name <zero or more expressions here>)

ossosoi loso20:04:25

So the macro I want to write takes that form and generates the snippet at the beginning of this thread.

ossosoi loso20:04:33

(defmacro m [&forms]
…)
(m 
(let [a 1
      b (+ a 1)
      :do-test [(pos? b) "foo" "bar"]]
  b))
=>
(let [a 1
      b (+ a 1)
      ;; the line below is generated by the macro (pos? b) is the form provided to the macro
      b (if (pos? b) "foo" "bar")]
  b) => "foo"

andy.fingerhut20:04:36

You want to write a macro, for example named mylet, and then put mylet in place of the let in the expression you wrote?

ossosoi loso21:04:08

Apologies for the lack of clarity. I’m reaching here…

andy.fingerhut21:04:10

It seems odd to want to write a macro where the input is almost exactly the same length and complexity as the output

andy.fingerhut21:04:49

but maybe you have other use cases in mind where the output is trickier or more tedious/long to type?

ossosoi loso21:04:07

It involves monads 😄

andy.fingerhut21:04:31

So it looks quite doable to write a macro that would take that let form as input, and transform it into the let form you show as the result, by writing appropriate Clojure code that turns one of those expressions into the other.

ossosoi loso21:04:40

More precisely it involves clandestine types with values wrapped in tag-tuples

andy.fingerhut21:04:31

The :do-test should always look for the most recently bound symbol, and bind it again, I suppose?

ossosoi loso21:04:52

That’s right!

andy.fingerhut21:04:52

So the most recently bound symbol is right there in the vector of expressions after the symbol let. Should be straightforward to look for 3rd, 5th, 7th, etc. elements of that vector equal to :do-test, and if they are equal to that, replace it with the element 2 elements earlier?

ossosoi loso21:04:59

Perhaps, I’m concerned about the row above only in my case.

ossosoi loso21:04:26

I’m traversing the pairs of symbol--values in reverse order.

andy.fingerhut21:04:28

Do you want to allow, or signal as an error, an input with two "consecutive" :do-test symbols? I don't know the meaning you are going for here, but if you want to allow that, perhaps going in forward order rather than reverse order would be easier.

ossosoi loso21:04:14

It could be that the order does not matter. I haven’t thought about validating input yet.

ossosoi loso21:04:57

I’m working with for rather than let I might add.

noisesmith21:04:31

it shouldn't be hard to translate into :let clauses in the for - but definitely go in forward order rather than back to front

noisesmith21:04:15

watch out for emitted code size being too large for a method body though - for is a beast and can produce huge output already

ossosoi loso21:04:40

Thanks for the tip. I created this problem for myself because of how challenging the implementation of for was to understand.

noisesmith21:04:56

right - for is a beast, but I think you can just expand into a for invocation, rather than re-implementing it

ossosoi loso21:04:10

I am using the sequence monad from algo.monads instead already actually. Simplifying the syntax using macros was the last hurdle before finishing up this project and the write-up.

ossosoi loso21:04:54

Thanks for all the help! I’ll have to return to this problem later.

noisesmith21:04:44

given that your problem statement is "I have a thing that works, and want a better syntax", it might help to think of macros differently here - fundamentally they are a function that takes a form, and returns a new form to be compiled. if you are worrying about semantics you are probably going astray, they are a syntax -> syntax transform

noisesmith21:04:47

so eg. instead of looking for a mechanism that creates new local bindings, you'd look for a transform that creates the form that establishes those bindings

ossosoi loso21:04:32

I see what you mean. My aim originally was to have an extended version of core/for, which could be used interchangeably with core/for. This somewhat puts constraints on the syntax used for the resulting macro.

noisesmith20:04:00

macros are expanded during compilation, if the symbol is not known when compiling, the only way a macro is useful is if you add a new, later compilation stage after the binding of the symbol (eval)

Konrad Claesson21:04:38

Why do I get an integer overflow when I do this?

(defn fib-add [[x y]] [y (+ x y)])
(iterate fib-add '[1 2])
I'm trying to generate a Fibonacci sequence like (1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...)

Benjamin09:04:41

btw you don't need to quote '[1 2]. [1 2] is the literal vector. It's only lists and symbols that are special

daniel.flexiana13:04:03

Because iterate is a function that returns a collection that has an unlimited quantity of elements. You have to use some other function to collect the results, like the 'take' function for example. See the docs and examples here https://clojuredocs.org/clojure.core/iterate Start trying (take 1 (iterate fib-add [1 2])) Then take 2 etc.

noisesmith14:04:34

millionth fibonacci result

noisesmith14:04:51

also, if you prefer heap exhaustion rather than integer overflow (or, more seriously, if you want results too large to fit in a Long), you can use +' instead of +

Alex Miller (Clojure team)21:04:35

iterate returns an infinite sequence which will get pretty big eventually :)

Konrad Claesson21:04:11

I see. Indeed just doing (take 5 (iterate fib-add [1 2])) fixes it

andy.fingerhut22:04:28

+' instead of + should allow it to use big integers instead of 64-bit integers, when the numbers get big enough to warrant it.

👀 1