Fork me on GitHub
#clojure
<
2021-12-23
>
West03:12:21

Maybe a stupid question, but in clojure.tools.cli what is the best way to fail upon giving no arguments? I thought to do something like this, but the program still seems to run.

(def cli-options
  [[nil "--url" "Any YouTube link."
    :default nil
    :validate [#(some? %) "Must input a URL."]]
   ["-h" "--help"]])
Should this check take place in the main function?

Drew Verlee05:12:03

that nil should be a sting indicating the shorthand name of the argument

Drew Verlee05:12:57

the default might run in the validate fails, it doesn't work in either case.

Drew Verlee05:12:03

so don't have that as nil either

Drew Verlee05:12:55

i also just wouldn't use clojure.tools.cli unless i had to meet some contract to deliver to another developer wasn't going to learn clojure. clojure deps has a thing for running functions for one and secondly i just eval functions in my repl. I know there are lots of cases where you might need to use a CLI program though, just making sure you were aware of the trade offs.

Max06:12:00

I suspect the nil in the shorthand argument position is intentional, it looks like OP knows what he’s doing. drew might be correct though, the :default might be silencing your validation error. Try removing it and see what happens?

Drew Verlee06:12:34

I guess nil could be a way to say "i dont' want to supply that". I prefer to use keys for that to avoid this kind of confusion. then you just dont include the key value pair.

Drew Verlee05:12:03

is

(loop [f_n-2 0 f_n-1 1] (recur f_n-1 (+ f_n-1 f_n-2)))
tail call recursive? I guess i should say, does the machine's memory have to grow in reltionship to the symbol tracking or update the integer changing. i guess the later because i get an inter overflow before anything else.

Max06:12:02

If you can use recur then it’s tail recursive. Using recur in a non tail recursive position will cause an error. then it’s tail recursive.

Drew Verlee06:12:37

thanks max, i should have used the term "tail call optimized". I think the funny thing here is that this will throw an error regardless, just depends on which and how fast it happens.

Drew Verlee06:12:56

But i take your meaning, the compiler wont even accept it in some cases.

dpsutton06:12:04

i don't think this is regular tail call since its not actually calling the function but requiring the programmer to use a goto. tail call optimization would be (defn foo [x y] (foo y (+ x y)) and that would hit the integer overflow not the stack overflow

Drew Verlee06:12:37

this:

(ns foo)
(loop [f_n-2 0 f_n-1 1] (recur f_n-1 (+ f_n-1 f_n-2)))
produces a Integer overflow, which i understand to be the best outcome. A stackoverlow would happen much earlier and likely is slower to unwind to... idk.
1. Unhandled java.lang.ArithmeticException
   integer overflow

              Numbers.java: 1576  clojure.lang.Numbers/throwIntOverflow
              ... 

Drew Verlee06:12:05

i likely have lost track of what it means to be "tail call recursive" again though.

Drew Verlee06:12:32

(dpsutton: err sorry i shouldnt DM especially this late at night)

delaguardo08:12:13

https://clojure.org/reference/special_forms#recur "`recur` is the only non-stack-consuming looping construct in Clojure" for example:

(defn foo [x y]
  (foo y (+ x y)))
will hit IntegerOverflow only because it happens early than StackOverflow with default JVM configuration
(defn foo [x y]
  (foo y (+' x y)))
this will hit StackOverflow (note +' instead of + to autopromote long)
(defn foo [x y]
  (recur y (+' x y)))
and then using recur will block main thread "forever". No StackOverflow or IntegerOverflow. However there is no tail-call optimisation in clojure (as in java 8). non-tail recursive position means:
(defn foo [x y]
  (when (recur y (+' x y)) 42))
this will throw because recur occurs in the middle of the when form

👍 1
souenzzo10:12:05

in practice, "tail call optimization" means that the compiler will emit a "better/faster" code for that recursion In the case of clojure, when using recur it emits a while (that do not accumulate stack, that is faster) when not using recur, it will call the function again, as expected

👍 1
noisesmith20:12:59

> i should have used the term "tail call optimized" tail call optimization is the only thing recur is capable of doing, if you are not in tail position it will error, if you are it compiles into a goto in the byte code (same as a Java for)

Drew Verlee03:12:42

Thanks that makes sense. So when using recur the recursive calls aren't creating a stack. So nothing to consume.

practicalli-johnny11:12:59

looks like recur is working just fine. The error is because an integer is not infinite and the loop doesn't have a condition to stop it looping and so keeps increasing the integer values until overflow.... I'm sure you've spotted that by now though.

olaf11:12:08

Hi, I've this function that runs in the REPL, but not when called inside a .jar file (locally built with lein uberjar). Any idea why?

(defn- get-posts-files []
  (-> "posts"
      (io/resource)
      (io/file)
      (.listFiles)))
java.lang.IllegalArgumentException: Not a file: jar:file:/.../target/uberjar/blog.jar!/posts

olaf12:12:34

> io/resource returns a URL, not a file found a partial https://stackoverflow.com/questions/32232662/clojure-uberjar-not-loading-resource-file. So how I can list all the files in a resource directory? .listFiles accept a File object, but I need to point it to the resources directory

magnars12:12:59

Yeah, they are no longer files when bundled in a jar. I had this same exact issue in Optimus. Check out the code here for some answers: https://github.com/magnars/optimus/blob/master/src/optimus/class_path.clj

👍 2
Dave Russell12:12:00

Heya folks! Is there any good way to test if a collection can be made transient?

Dave Russell12:12:07

Is (instance? clojure.lang.IEditableCollection form) enough?

p-himik12:12:35

Should be, that's how Clojure itself checks it.

🙏 2
Alen19:12:27

Hey folks 👋 What are some popular library choices nowadays for building REST APIs? I remember Pedestal being recommended a lot previously, is that still the case? What do you use? Thanks!

vemv20:12:57

Pedestal is staying strong and "boring" which is a good thing in this churn-y dev world! There's also pedestal 2 in the works apparently from what I heard the other day.

👀 1
seancorfield19:12:14

We just use plain ol' Compojure but our APIs are more "REST-like" than "true REST". I hear compojure-api mentioned a lot and for a while folks were talking about liberator, but I haven't used either and can't comment on them (nor have I used Pedestal).

👍 1
emccue19:12:16

reitit is my preference now adays

☝️ 3
seancorfield19:12:09

Does that have explicit REST support @emccue?

Alen19:12:17

reitit seems to be metosin choice instead of compojure-api 🤷

emccue19:12:34

what do you consider explicit REST support?

seancorfield19:12:00

(I would probably use reitit instead of Compojure for any new projects I started, at this point)

emccue19:12:30

i personally crashed our site by not understanding the defroutes macro in compojure last week so i might be a tad bitter with that rec.

emccue19:12:29

pedestal is actually nice though if you need/want to do websockets or more involved middleware stacks. i think “stock ring with jetty” is good enough for most though.

👍 2
seancorfield19:12:58

> what do you consider explicit REST support? Good question. I would want to be able to declare "resources" and the verbs that are allowed for them and have some automated wiring for that, rather than having to explicitly write out every possible matching route pattern.

p-himik19:12:35

Apart from that, if it's HTTP-based then it's nice to have HTTP be abstracted away to some extent. Like not having to do (assoc response :status 404 :message "Not found") when something is missing - stuff like that. yada does that to some extent.

Joshua Suskalo19:12:07

I've heard that reitit has some issues with reloadability if you aren't using one of the system-state-managers like component, mount, integrant, etc.

athomasoriginal21:12:41

Not that i'm aware of. I initially had a repl friendly reitit setup going without state managers.

Joshua Suskalo21:12:40

I may be incorrect on it, but I didn't have good experiences with reloadability last time I tried it, some while ago.

athomasoriginal22:12:13

This is a more general problem. Making and keeping things REPL friendly in Clojure has a learning curve. Similar to making things HMR friendly in the front end.

Joshua Suskalo19:12:28

That could be one reason to consider using compojure-api over reitit, although reitit is second to none in terms of speed.

Alen19:12:40

do you know by any chance if there are any benchmarks that compare competing libraries?

ikitommi20:12:32

Please write an issue about reloadability, would be easy to add a dev-mode, that compiles the route tree on each request.

borkdude20:12:15

I've had good experiences with #yada but JUXT now has a new project called "site". I haven't used that myself yet.

👀 1
Alen20:12:38

Juxt has some amazing stuff but I think they need help with naming things 😂

1
Alen20:12:36

Thanks for the reference, this looks interesting especially because I was planning to use bitemporal db anyway

alexdavis12:12:39

Malcolm/Jon have a rule that all our libraries must contain only 4 letters, no one knows why and at this point we're too afraid to ask

😂 1
Alex Miller (Clojure team)20:12:41

a new entrant in the "least googgleable project name" competition :)

8
Eugen20:12:05

hi, what is the "equivalent" of an abstract class in Clojure? I'm trying to work with Apache Calcite from Clojure and they extend AbstractTable :

public abstract class AbstractTable implements Table, Wrapper {
  protected AbstractTable() {
  }

  // Default implementation. Override if you have statistics.
  @Override public Statistic getStatistic() {
    return Statistics.UNKNOWN;
  }

  @Override public Schema.TableType getJdbcTableType() {
    return Schema.TableType.TABLE;
  }

  @Override public <C extends Object> @Nullable C unwrap(Class<C> aClass) {
    if (aClass.isInstance(this)) {
      return aClass.cast(this);
    }
    return null;
  }

  @Override public boolean isRolledUp(String column) {
    return false;
  }

  @Override public boolean rolledUpColumnValidInsideAgg(String column,
      SqlCall call, @Nullable SqlNode parent, @Nullable CalciteConnectionConfig config) {
    return true;
  }
}
What is the pattern of reuse for Clojure in this case? I mean the implementations for unwrap, isRolledUp should be the same for most / all my implementation (let's call them MyTable) Should I just define functions for each method and when when I implement each MyTable with reify I need to delegate to the functions ? I feel this will create some boilerplate

Eugen21:12:13

so this is a good use case for macros then. I haven't used macros and not a big fan of rushing into them. I kind of have a feeling it's going to get complicated. The first thing that comes to my mind is: what if I want to implement a method differently one time. Anyway, I decided to re-implement the csv java example they have in clojure and will try to send it upstream if it works. It would serve as an example 🙂

valerauko21:12:32

How does that work? I had to use proxy and then in the end fall back to Java when dealing with certain Netty abstract classes. How do macros help there?

Eugen21:12:03

I am avoiding abstract class and re-implementing the interfaces.

Eugen21:12:37

my question was: how can I deal with copy pasting the implementations for all the methods that make up the abstract class

Eugen21:12:00

since I am going for interfaces, I will have to implement them every time

Eugen21:12:22

macros is a good way to avoid this boilerplate

Eugen21:12:29

or at least that is what I understood

borkdude21:12:45

you can also use functions

(defn foo-build [{:keys [foo-fn bar-fn]}]
  (reify Interface (method [this] (foo-fn this ..)) ...))

👍 3
Ben Sless21:12:43

The abstract implementations are provided by the macro, overriding implementations by the user

Ben Sless21:12:11

The expanded form will have all the methods every time

Eugen21:12:18

@UK0810AQ2: I'm new to this so, do you have an example I can follow? What I understood is that I can write a macro that I can call to define a new Table and the macro will handle default implementations for methos. When I do call the macro I will have the option to provide different implementations for some/all of the methods defined in the macro .

Eugen21:12:11

now that I write it, is kind of makes sense. Thanks, let's see when I get there - now it's project setup 🙂

valerauko22:12:51

Oh okay i misunderstood that.

noisesmith23:12:27

another benefit to using functions in your method implementations is that you can avoid a lot of hassle / weirdness that comes with class reloading, since you can't change methods on instantiated classes, but if you change a function implementation the var lookup does the right thing on each invocation