Fork me on GitHub
#clojure
<
2021-07-22
>
vemv00:07:42

guess some things are truly immutable

8
ChillPillzKillzBillz07:07:26

I am not sure where to put this.... but has anybody combined the development environment of a server like pedestal and cljs environment like figwheel? How do you do that?

p-himik07:07:07

What do you mean by "combine"? What kind of experience do you want?

dgb2308:07:46

#fulcro does that

ChillPillzKillzBillz09:07:13

so... say when I launch the fig-wheel .. instead of launching its own server... it launches the pedestal server which will be serving the frontend... and the repl will still be available...

ChillPillzKillzBillz09:07:14

@U01EFUL1A8M I'll check it out... many thanks!

p-himik12:07:54

You can do it, at least with shadow-cljs, by just calling the same code your CLJS build tool calls when you use its CLI.

p-himik12:07:34

I used to do that, but went back to two separate processes - simply because having the same classpath for backend runtime and for UI compilation doesn't make sense and is incredibly brittle.

👍 2
joost-diepenmaat08:07:37

my java knowledge has pretty much corroded at this point. I just found I wanted to proxy an interface that has a default method implementation - which I’d never seen before. Seems that just not implementing the method will not work; the default implementation is then not found. Any way to do this? Specifically, I want to proxy this class: https://bitbucket.org/atlassian/swagger-request-validator/src/master/swagger-request-validator-core/src/main/java/com/atlassian/oai/validator/model/Request.java which has a default getRequestBody method that I would like to be used (I don’t want to implement my own version).

thegeez10:07:11

reify interface works with default methods

Jim Newton09:07:59

I have a function with declared like this:

(defn bdd-to-dot 
  "Create (and possibly display) a graphical image rendering the given Bdd
  For Mac OS, the :view option may be used to display the image
  interactively."
  [bdd & {:keys [title view verbose pen-width draw-false-leaf]
          :as all-optionals
          :or {title "no-title"
               view false
               verbose false
               pen-width 2
               draw-false-leaf true}}]
 ...
and within the function I want to call another function passing along the same all-optionals, I tried with (apply second-function arg1 all-optionals) and I get a java exception about a missing argument. Is this supposed to work, or am I completely on the wrong track? Do I really need to redundantly re-type all the :key value pairs at the call site?

delaguardo09:07:47

when hashmap is used for applying it will be turned into sequence of pairs. try to call it like this (apply second-function arg1 (mapcat identity all-options))

2
delaguardo09:07:54

and if you are using clojure 1.11-alpha you can avoid using apply at all. Just call second function with two arguments arg1 and all-options

Jim Newton09:07:07

I'll give it a try. if it works, it would be great to add to https://clojuredocs.org/clojure.core/apply, which I'll gladly do.

Jim Newton09:07:58

I say a syntax in the clojure doc about (apply fun arg arg arg & args). What is that for?

delaguardo09:07:32

internal optimisations

delaguardo09:07:47

look at (source apply)

Jim Newton09:07:06

ahhh, I misunderstood the doc. I though I could use the & at the call-site. no, it's just part of the declaration. gotcha

Jim Newton09:07:46

If I call the second function as follows, it works correctly but this seems unnecessary to me:

(second-function arg1 
  :title title
  :view view
  :verbose verbose
  :pen-width pen-width
  :draw-false-leaf draw-false-leaf)

joost-diepenmaat09:07:33

Followup: this code in seems to be what’s preventing the default method from being found:

;add methods matching interfaces', if no mapping -> throw
      (doseq [^java.lang.reflect.Method meth ifaces-meths]
                (gen-method meth 
                            (fn [^GeneratorAdapter gen ^Method m]
                                (. gen (throwException ex-type (. m (getName))))))))
    
                                        ;finish class def
In other words, proxy will fill in all declared methods in the interface, overriding any defaults.

Jim Newton09:07:19

I added an example to the apply docs. If anyone sees something I said incorrectly, let me know. I'm happy to change it to make it correct. https://clojuredocs.org/clojure.core/apply#example-60f9450ce4b0b1e3652d7519

Ed10:07:28

is anyone aware of someone doing something like graalvm compiling rebel-readline + cider connect or something similar into a terminal repl connect type tool?

borkdude10:07:40

@l0st3d I have a native version of reply if that sounds useful

Ed10:07:20

yeah ... that sounds useful ... 👍

borkdude10:07:19

actually no. I have it as a private project, but I can invite you

borkdude10:07:28

I didn't open source it because it's hacky

borkdude10:07:34

what is your github name

Ed11:07:45

great ... thanks ...

borkdude10:07:08

reply is the REPL tool used within leiningen

borkdude10:07:28

it might be possible to include rebel-readline in there as well

Jim Newton10:07:38

I'm using concat to lazily generate a potentially exponentially explosive list. I think I'm only generating pairs as needed. Is there a function I can used on a lazy seq to ask "how many element have been produced, ignoring the ones that haven't been produced yet?"

borkdude10:07:37

to decide the count of a lazy seq you must walk the lazy seq from head to tail

borkdude10:07:56

but if you are generating the elements yourself you could just update a counter somewhere imperatively

2
👍 2
Martynas Maciulevičius11:07:58

I think he wanted this for debugging. So I think a plain (println _) would also do.

Martynas Maciulevičius10:07:18

You can do it but the collection will still remain in memory. There are macros that consume the collection and don't retain the head of it. You could have a counter where you consume your list. I think it's possible to do it via realized? to find the next item that's still not created but I don't think it's a good idea. Edit: I was thinking about actual solution for prod code and not debugging.

Jim Newton10:07:02

The pattern I'm using is something like the following.

(letfn [(f [node lineage]
          (cond condition-1
                [(g node)]

                condition-2
                []

                :else
                (concat (f (:left node) (cons node lineage))
                        (f (:right node) (cons (h node) lineage))))]
  (f some-node ())

Jim Newton10:07:10

@invertisment_clojuria I only need this for debugging, to do a sanity check that my algorithm does what I think it does.

Martynas Maciulevičius11:07:35

Why not println then? And yes, a separate counter would do. Then I'd do an atom or an agent which you'd update from the f.

3
Martynas Maciulevičius10:07:16

Why don't you simply do (take 20 coll)? Why do you need a count?

Martynas Maciulevičius10:07:39

Also you could do lazy-seq in your concat so that the collection wouldn't blow up your RAM. Edit: probably concat aleady does this. I confused it with cons + lazy-seq.

Jim Newton10:07:41

should I be using concatenation of lists or of vectors [] here? if I use vectors, won't the head of the vector be retained no matter how I iterate? whereas with lists, once I've popped it of, it can be GC'd right?

Martynas Maciulevičius10:07:28

Well you return [(g node)] from your method. Which means that your vector contains one item.

Martynas Maciulevičius10:07:56

So you concatenate many of those (1-len vecs) and get one long seq of small things. So I don't think it's a problem for your memory.

Jim Newton10:07:58

yes, but then its concatenated on the right-hand-side with another vector of length one, right?

Martynas Maciulevičius10:07:33

But concatenation is lazy, not immediate. You don't create a vector from those vectors. You create a lazy-seq via (concat [:a] [:a])

Martynas Maciulevičius10:07:58

If you'd get vector back you'd blow up your ram at the moment you call f.

Jim Newton11:07:12

ahhh. I'm not creating a vector!! that's even better than I thought.

Martynas Maciulevičius11:07:10

If you want a vector you can do this (please don't): (into (f ...) (f ...)) . This will blow up your ram for sure.

😜 2
Martynas Maciulevičius11:07:10

@jimka.issy Also you use (cons node lineage) which creates a seq but it's not lazy. But this shouldn't impact your code as you don't do any calculation to get lineage sequence. It would make a difference if you'd do this: (cons node (f ...)) , which wouldn't lazily calculate f. Then you'd be better off if you'd have this: (cons node (lazy-seq (f ...))) .

Jim Newton13:07:31

Here's the code after your suggestions. Thanks.

(defn satisfying-type-designators
  "Create a lazy list of type designators which satisfy the Bdd.  I.e.,
  one element of the list for each type corresponding to the nodes
  from the top of the Bdd to the true leaf."
  [bdd]
  (assert (clojure.core/or (instance? Boolean bdd)
                           (instance? Bdd bdd)))
  (letfn [(satisfying [node lineage]
            (if (= false node)
              []  ;; if reached a false leaf-node
              (let [td (gns/canonicalize-type lineage :dnf)]
                (cond (= :empty-set td)
                      ;; if the lineage canonicalizes to an empty intersection, then we prune this recursion
                      [] 

                      (= true node)
                      ;; if reached a true leaf-node
                      [td] 

                      :else
                      ;; otherwise, we generate a lazy conatenation of the left and right traversals
                      (concat (satisfying (:positive node)
                                          (gns/create-and [td
                                                           (:label node)]))
                              (satisfying (:negative node)
                                          (gns/create-and [td
                                                           (gns/create-not (:label node))])))))))]
    (satisfying bdd :sigma)))

Jim Newton13:07:50

BTW I discovered I can prune the recursion in some cases also I don't really need to cons the lineage. I can just incrementally compute the accumulated value as the recursion occurs.

emccue13:07:47

are there any "syntax omnibus" type things for deps.edn?

emccue13:07:39

I'm yet again frustrated by maven and gradle on the java/scala side and don't see a compelling reason the config format can't be used to build those projects also

Jon Boone13:07:23

What are the pain points with maven & gradle?

emccue13:07:51

beyond the part of the file that is just <dependencies> it is just hard to know what is happening where, why, and by whom

emccue13:07:20

like, I understand kinda maven's lifecycles. everyone i've met seems to really like gradle because its "simpler" (read - easier)

emccue13:07:49

no one likes sbt

Jon Boone13:07:56

Oh — I see

emccue13:07:54

but the only commonalities between them - and the only thing I really care about in that tool in particular - is 1. fetch deps, 2. compile all files in directory, 3. build jar, 4. run tests (5. work with IDE, but one thing at a time)

emccue13:07:57

I would much rather if generating code for say, a WSDL interface or protobuf, had me calling a command line tool (that I knew would be available ...somehow) and just composing tools like that

emccue13:07:05

instead of "oh magic maven plugin"

Jon Boone13:07:56

So are you looking to switch tool chains for projects over to deps.edn entirely? Or to provide a translator front end that produces the appropriate config for maven/gradle/abt

emccue13:07:06

switch tool chains

emccue13:07:31

with the inevitable ability to generate a pom for using those nonsense plugins

Jon Boone13:07:53

Interesting sounding project…

emccue13:07:17

i'm full of those, they just fizzle out when the adrenaline is gone

Jon Boone13:07:50

While your adrenaline is still pumping, could you write up a example for one the tool chains? Just so that there is a document with the flavor of what you have in mind. I’d be happy to collaborate…

emccue13:07:31

can you ping me at eod to remind me?

Jon Boone14:07:57

Which TZ are you? EDT?

Jon Boone14:07:17

Hmm — so is it 09:25 or 10:25 for you, most recently?

Jon Boone14:07:30

I’ll set a reminder for 17:00 EDT (21:00 UTC)

emccue01:07:40

Okay so basically, with a deps.edn file you have a structure that looks like this

emccue01:07:16

{:deps {some/lib {:mvn/version "123"}}
 :aliases {:uberjar {:replace-deps {com.github.seancorfield/depstar {:mvn/version "2.1.267"}}
                     :exec-fn hf.depstar/uberjar
                     :exec-args {:aot true}
           :other-tool {...}}

emccue01:07:30

you have your dependencies declared, and aliases to run specific tools

emccue01:07:45

:test ; for a testing context
  {:extra-deps {com.gfredericks/test.chuck {:mvn/version "0.2.10"}
                expectations/clojure-test {:mvn/version "1.2.1"}
                org.clojure/test.check {}}
   :jvm-opts ["-Dclojure.core.async.go-checking=true"
              "-Dclojure.tools.logging.factory=clojure.tools.logging.impl/log4j2-factory"
              "-Dlogged-future=synchronous"
              "-XX:-OmitStackTraceInFastThrow"
              "--illegal-access=warn"]}

  :runner ; to run tests (test:<subproject>-test:runner) -- the <task> called test
  {:extra-deps {org.clojure/tools.namespace {}
                org.clojure/tools.reader {}
                com.cognitect/test-runner
                {:git/url ""
                 :sha "b6b3193fcc42659d7e46ecd1884a228993441182"}}
   :jvm-opts ["-Dlog4j2.configurationFile=log4j2-silent.properties"]
   :main-opts ["-m" "cognitect.test-runner"
               "-r" ".*[-\\.](expectations|test)(\\..*)?$"]}

emccue01:07:34

with gradle you have the same dependencies list

emccue01:07:58

but instead of your tools being "include this jar, call this function" you have "plugins"

emccue01:07:12

plugins {
    // Apply the scala plugin to add support for Scala
    id "scala"
    id "com.github.maiflai.scalatest" version "0.23"
}

repositories {
    // Use jcenter for resolving your dependencies.
    // You can declare any Maven/Ivy/file repository here.
    jcenter()
    mavenCentral()
}

def scala_minor_version = "2.12"

dependencies {
    implementation project(":dao")

    // Automatic resource management
    implementation "io.tmos:arm4s_${scala_minor_version}:1.1.0"

    // Elasticsearch client
    implementation "com.sksamuel.elastic4s:elastic4s-http_${scala_minor_version}:6.5.7"

    // Json serialization
    implementation "com.typesafe.play:play-json_${scala_minor_version}:2.6.10"

    // Slick. Needed for 'MappedTo' macro in DAO entities
    implementation "com.typesafe.slick:slick_${scala_minor_version}:3.3.0"

    // Google guava for caching
    implementation "com.google.guava:guava:27.1-jre"
}

emccue01:07:26

what is the command to run tests?

emccue01:07:30

to make an uberjar?

emccue01:07:04

plugins {
    // Apply the scala plugin to add support for Scala
    id "scala"
    id "org.scoverage" version "2.5.0"
    id "com.github.maiflai.scalatest" version "0.23"
}

repositories {
    // Use jcenter for resolving your dependencies.
    // You can declare any Maven/Ivy/file repository here.
    jcenter()
    mavenCentral()
}

def scala_minor_version = "2.12"

dependencies {

    implementation project(":dao")

    // Automatic resource management
    implementation "io.tmos:arm4s_${scala_minor_version}:1.1.0"

    // Json serialization
    implementation "com.typesafe.play:play-json_${scala_minor_version}:2.6.10"

    // Stream editing
    implementation "com.github.rwitzel.streamflyer:streamflyer-core:1.2.0"

    // Slick DB dependencies
    implementation "com.typesafe.slick:slick_${scala_minor_version}:3.3.0"
    implementation "com.typesafe.slick:slick-hikaricp_${scala_minor_version}:3.3.0"
    implementation "mysql:mysql-connector-java:8.0.15"

    // Use Calcite parser
    implementation 'org.apache.calcite:calcite-core:1.26.0'
    implementation 'org.apache.calcite:calcite-server:1.26.0'

    implementation "com.h2database:h2:1.4.199"

    scoverage "org.scoverage:scalac-scoverage-plugin_${scala_minor_version}:1.4.0-M5",
              "org.scoverage:scalac-scoverage-runtime_${scala_minor_version}:1.4.0-M5"
}

checkScoverage {
    minimumRate = 0.75
    coverageType = "Branch"
}

emccue01:07:23

how does scoverage know to run? when does it run?

emccue01:07:25

ThisBuild / scalaVersion := "2.13.6"
ThisBuild / organization := "com.example"

val scalaTest = "org.scalatest" %% "scalatest" % "3.2.7"

lazy val hello = (project in file("."))
  .settings(
    name := "Hello",
    libraryDependencies += "com.eed3si9n" %% "gigahorse-okhttp" % "0.5.0",
    libraryDependencies += scalaTest % Test,
  )

lazy val helloCore = (project in file("core"))
  .settings(
    name := "Hello Core",
    libraryDependencies += scalaTest % Test,
  )

emccue01:07:34

this is maybe a bad sbt example, but it is a lot of semantics to understand to make a hello world

emccue01:07:53

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.5.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <encoding>UTF-8</encoding>
                <generatedSourcesDirectory>${project.build.directory}
                  /generated-sources/</generatedSourcesDirectory>
                <annotationProcessors>
                    <annotationProcessor>
                        com.baeldung.annotation.processor.BuilderProcessor
                    </annotationProcessor>
                </annotationProcessors>
            </configuration>
        </plugin>

    </plugins>
</build>

emccue01:07:47

by itself its not that bad - "here be the part that compiles my java"

emccue01:07:39

but where this immediately hurt my brain was making a multi module project - absolutely needed to develop an annotation processor since you need a class compiled already and available at compile time to run it

emccue01:07:22

now maybe i'm the jerk here

emccue01:07:29

but It feels like deps.edn has the simpler mental model

emccue01:07:35

{:paths ["src" "target/classes"]
 :deps/prep-lib {:alias :build
                 :fn compile
				 :ensure "target/classes"}}

emccue01:07:18

if that makes sense

emccue01:07:07

so instead of like a magic mvn:test

emccue01:07:01

{:deps
 {some.dep {:mvn/version "123"}}

 :aliases
 {:test {:extra-paths ["test"]
         :extra-deps { {:mvn/version "123"}}
         :main-opts ["--dirs" "src/test/" --report-dir" "somewhere"]
         :exec-class org.junit.TestRunner}}}

emccue01:07:37

then

runner -X:test

emccue01:07:13

extremely clear link to the method that will run (static main method of class, or in the case of the clojure ecosystem the clojure fn) and the configuration given to it

emccue01:07:22

I feel only 50% coherent in that explanation, but its kinda how i'm feeling

emccue01:07:41

I also don't think it is needed to write stuff in anything but clojure for a proof of concept or even as a final stage, but EDN is just a data format. There is no intrinsic reason only clj can handle it

Jon Boone02:07:36

Thanks — I’ll look into it more tomorrow

emccue13:07:20

(its fascinating that when my job was java I would write clojure on the side, but now the situation is reversed)

😭 2
Martynas Maciulevičius13:07:53

I think you write java in a different way. Not so much mutable state, probably persistent data structures too. And maybe more static methods as well. It's not the same anymore.

Martynas Maciulevičius13:07:13

At least these were the observations that I noticed about myself.

Martynas Maciulevičius13:07:55

Also from time to time I do some golang. Which is completely something else.

emccue13:07:29

I was stupid enough to force FP on my first java project when I was still too much of an idiot to do it well. I still feel sorry for those people living in that codebase

emccue13:07:50

after that I got better, but most of my java projects involved immutables from day 1

emccue13:07:17

and the other stuff was spark/scala so it also kinda matched the style

emccue13:07:10

I don't think I use more static methods. Java still works best with having methods targeted - but I am more prone to using mixins than the average bear.

emccue13:07:23

personal favorite pattern I can't wait to be the only one in the world using

public sealed interface ThingOps permits Thing {
    default void doX() {
        var self = switch (this) { case Thing t -> t };
        System.out.println(self.x());
    }
}

public final class Thing implements ThingOps {
    public int x() {
        return 5;
    }
}

emccue13:07:00

peep that pathological type safety

West13:07:17

Anybody here have access to github copilot? If so, how well does it work with Clojure?

Martynas Maciulevičius13:07:21

Ah, so that's why they bought github...

dominicm13:07:44

Someone on my team has access to it, they haven't been blown away by it for Clojure.

Martynas Maciulevičius14:07:53

What about other languages? Which ones were they blown away by? What about JS?

dominicm14:07:53

Well, it doesn't compare to the demos.

dominicm14:07:49

One thing it does do is get context really well, it seems to often predict:

(let [foo 1
     bar 2
     baz 3] |)
into
(let [foo 1 bar 2 baz 3]
  {:foo foo
   :bar bar
   :baz baz})
apparently. That's my understanding anyway.

dominicm14:07:02

It's not been trained on a huge set, so I guess there's a lot of code like that about.

djm14:07:53

https://6b.eleuther.ai is similar (GPT-J instead of GPT-3), and runs in the browser, but is slooow. Maybe fun to play around with

dominicm14:07:57

Apparently they're down 😄

djm15:07:57

Interesting…

dominicm15:07:48

back again, trying something 🙂

dominicm15:07:18

haha, I typed (defn distinct-by and it suggested some implementations.

dominicm15:07:54

That's where I think things like CoPilot will shine: utility functions. But we don't have many in Clojure.

dominicm15:07:55

(defn distinct-by [f s] gave me

(defn distinct-by [f s]
  (zipmap (map #(f %) (partition 2 s))
          (reduce (fn [acc r]
                    (let [xs (map #(f %) (partition 2 r))]
                      (assoc acc xs)))
                  {}
                  s)))
Where assoc has the wrong number of arguments.

Martynas Maciulevičius15:07:00

I always wanted to have (partition-by f coll). Probably now it will happen easily :))

Martynas Maciulevičius15:07:47

Actually what I'd prefer more is that it would allow to find snippets of code which I could delete or replace. Not to write in the first place. Because if it will do some repetitive code in a sub-par way in which other devs do it then its easier to produce a wreck faster if you don't understand what it does. i.e. I could generate getters and setters for all objects using intellij but in some languages they choose not to have them. So why would I want to generate factories/singletons/repetitive-fetching if I decide not to have those?

ChillPillzKillzBillz18:07:12

what is the point of a fancy AI coder if you need to check its code output manually?

Martynas Maciulevičius19:07:04

To train the actual AI coder that does both things 😉 It was never meant to help you 😉

ChillPillzKillzBillz12:07:33

well let me not help them train any AIs then...

Martynas Maciulevičius12:07:19

Yeah... I'm so tired of this is the same feeling when I read the terms and decide that I don't want to be a part of something. I wonder when we'll reverse to the world that actually tries to genuinely help you without hidden intents... Probably it never existed outside of my imagination...

Jim Newton14:07:14

cl-format is cool! I just discovered ~& and ~@. I added a note to https://clojuredocs.org/clojure.pprint/cl-format#example-60f98682e4b0b1e3652d751b

Racheal14:07:26

I have a function that creates a string from a map like so

(def export-options {:query {"_submission_time" {"$gte" "2021-04-20T00:00:00", "$lte" "2021-04-21T23:59:59"}}})

(clojure.string/join "&"
                     (for [[option val] export-options]
                       (str (name option) "=" val)))          
this however returns a string with backslashes like so
query={\"_submission_time\" {\"$gte\" \"2021-04-20T00:00:00\", \"$lte\" \"2021-04-21T23:59:59\"}}

Racheal14:07:19

I would like it to look like this

query={"_submission_time" {"$gte" "2021-04-20T00:00:00", "$lte" "2021-04-21T23:59:59"}}

Racheal14:07:10

Any ideas on how I can do that? I realized it's because of the str function. Wondering if there are other alternatives

lukasz14:07:31

Is the expected result snippet wrong? It looks like your input data

Racheal14:07:49

no it's correct, i am basically changing the map into a string with an equal sign

dpsutton14:07:32

it does look like that

lukasz14:07:57

ah, then it's working - the output is escaped but if you were to print it the backslash won't be rendered

Racheal14:07:00

input is a map

{:query {"_submission_time" {"$gte" "2021-04-20T00:00:00", "$lte" "2021-04-21T23:59:59"}}}
Expect output is a string
"query={"_submission_time" {"$gte" "2021-04-20T00:00:00", "$lte" "2021-04-21T23:59:59"}}"

dpsutton14:07:18

remember, to indicate a string data type, strings are inside of quotation marks ". And one marks the end of the string. So how do you indicate that a quotation mark should be a member of that string?

lukasz14:07:26

e.g. "foo=\"bar\"b\""

Racheal14:07:34

@U11BV7MTK sorry i have updated my answer @U0JEFEZH6 if i am understanding you correctly the escaped characters are because of the quotations?

dpsutton14:07:46

your expected output doesn't make sense

dpsutton14:07:59

consider the string "bob is "happy""

dpsutton14:07:20

see how the delimiters (the " at the beginning and end are the same as the quotation marks inside of the string?

Racheal14:07:55

oohhh i get it!

dpsutton14:07:38

right. so we need a way to distinguish the " as a delimiter of a string when printing and writing it, and " as a valid character of a string, not as the kinda symbols we use to delimit the string

dpsutton15:07:08

and the slash is that mechanism to indicate that. it's the escape character

dpsutton15:07:18

there are others as well. n is obviously just the letter n, but \n when escaped is a newline

dpsutton15:07:47

(println "these characters\n are \"special\"") run that in your repl

Racheal15:07:27

thanks, I think I understand what the problem is

👍 2
hiredman17:07:17

you can use the fully qualifed name

hiredman17:07:26

whatever.thensis/foo

ChillPillzKillzBillz19:07:56

What are the webframeworks used with clojure that are good/preferred by the community? I am currently trying to use pedestal... but there isn't much support on it. So I am struggling a bit.... What other options do I have... ?

p-himik19:07:23

Might be too harsh a change, but you can do things without any frameworks. :) Think of what kind of pieces you need to achieve some goal, and search for libraries that fit the descriptions of those pieces. E.g. I generally prefer using yada. With hugsql if a DB is needed. With sente if websockets are needed. With buddy if auth is needed. And so on.

seancorfield19:07:02

Frameworks in general are not considered very Clojure-y -- folks generally use a combination of their preferred libraries. That said, you might find Luminus (a template that gathers together many web-related libraries) helpful, or you might look at Coast.

👍 2
seancorfield19:07:47

Ah, yes. I tend to forget Fulcro because I think of it as very frontend-heavy and I don't do cljs/js/frontend stuff!

seancorfield20:07:39

Ring + Compojure is the most common approach when getting started I think.

seancorfield20:07:06

(I'm seconding Cora's responses here as a good place to start and learn)

partywombat 2
ChillPillzKillzBillz20:07:54

yes I do like clj and deps.edn. I use clj-new already...

ChillPillzKillzBillz20:07:14

thanks for all your help guys... n gal. I'll check out the links!!

sharkdance 2
ChillPillzKillzBillz20:07:19

BTW @U04V70XH6 is there a tutorial on how to tweak clj-new to create a custom template? - maybe this should be a separate thread... I'll put this in clj-new thread as well...

2
vemv20:07:02

FWIW pedestal is a fine and actively supported framework, I keep knowing of companies launching their new projects in it its only problem is that it's 'boring' (stable) which for a good chunk of us is perfect!

vemv20:07:15

there are online docs, there's also a template and #pedestal . More than enough to get started and often get questions answered by experts/authors Perhaps for a super specific problem you won't find the answer in the online docs, but honestly there comes a point where that's just part of software engineering. Reading the source and all that it's not all paint-by-numbers :)

borkdude20:07:17

I personally like #yada too, it has good docs as well

vemv20:07:42

IDK, some frameworks/libs/... prioritize getting users above everything else and others don't. It seems to me that Pedestal falls into the latter category. There are so many things that can be improved other than user count, e.g. performance, design, or even a nice work-life separation for their authors. I do perceive some user acquisition obsession in some JS projects.

vemv20:07:46

sure, neither of us needs to extrapolate what the other supposedly suggested. Sorry if I brought the thread to bit of a tangent.

seancorfield21:07:29

I think that, for a lot of Clojure developers who've been working with the tech and the ecosystem for several years, it's very hard for them to put themselves in the shoes/mindset of a beginner and even be able to judge whether a project and its docs are approachable for beginners. I know I have a very hard time recommending good beginner-friendly material a lot of the time. For example, I thought depstar was pretty straightforward to use but as more and more new developers are coming to the Clojure CLI and deps.edn stuff, I found I was getting a lot of questions about stuff that I didn't realize beginners didn't know -- so I ended up adding this page to the docs https://cljdoc.org/d/com.github.seancorfield/depstar/2.1.267/doc/terminology-convention which seems to have helped with that (somewhat). Sometimes I even have a hard time understanding what a beginner is asking because I inherently make assumptions about what they already know... 😞

seancorfield21:07:28

I'm always happy to try to field Qs about the deps.edn/tools stuff -- via DM if folks prefer, where I can got into really long-form discussions.

seancorfield21:07:06

I thought there was a glossary on http://clojure.org but I can't find it now. There's an FAQ (under Guides) but it isn't very extensive yet. There are several suggestions for additions to the FAQ (and improvements to the site in general) here: https://github.com/clojure/clojure-site/issues

seancorfield21:07:02

No, I that's very reasonable. And thank you for linking to my guide. I'd love to be able to remove mine and just point to the equivalent stuff on the official site 🙂

clojure-spin 2
ChillPillzKillzBillz10:07:00

@U45T93RA6 There is not very good documentation on pedestal unfortunately. You can look at examples but unless the example does exactly what you want to do... it is hard to discern things... e.g. 2 questions I posted on the pedestal clojurians thread.. 1. Why is my response not rendering into HTML Ans: You need to explictely set the header content type to html... I challenge you to find this in any pedestal documentation. 2. How can I map multiple folders from my development environment to the hosted sources... Ans: Some very bizzare combination of :paths and a new :html-filepath setting in the service record... again challenge you to find this info in documentation. I grant you I am a total newbie and complete numpty on most days.... so maybe these things are obvious to everybody else... but so far my journey with pedestal has not been good... I tried luminus as well... it was too much for me. I'll try yada... as I looked at it yesterday and the documentation looked quite exhaustive... lets see.

vemv10:07:09

1 - Honestly that's basic web dev knowledge. You can experience that problem under any framework; a web developer will know to inspect a web response (under your browser's devtools or curloutput) and make sense out of it. It's not any framework's responsibility to teach you webdev. Same for classpath, while we're there. There's googling, and also there's schools, or friends, mentors etc. Else we could take the whole notion of explaning everything ad nauseam. Where's the limit? Should http://clojure.org teach me about physics?

vemv10:07:25

2 - you got a precise answer for your question. Personally I'd simply feel happy for that and wonder if I'm to qualified to describe it as bizarre while I also describe myself as a total newbie . Food for thought perhaps, if not feel free to disregard my ramblings!

ChillPillzKillzBillz12:07:27

look I am not here to pick a fight. I am merely telling you my experience. If someone new picks up pedestal... and follows their own examples from their own pages... and ends up with mere text instead of html... well that is not a good start. Also I got a precise answer to my point 2 because I was on here. I struggled by myself for almost 6 months before getting on here. Clojure is a hard enough ecosystem to learn without having to chase ones own tail trying to find answers.... and I know people do lots of complex things with a webframework... but the most simplest thing is hosting static resource... everybody probably starts from that... I think/hope... anyways... I am merely voicing my experience. I totally accept that I am not a seasoned web dev.

seancorfield17:07:46

@U45T93RA6 I'm going to wade in here and say that is the sort of gatekeeping that really puts new developers off Clojure: "basic web dev knowledge" -- a new developer isn't going to understand a lot of the concepts and isn't going to be able to make sense of what they find on Google/Bing a lot of the time because it's hard to discern the audience for a lot of the articles out there. On the flip side @U022LK3L4JJ this is exactly why I recommend new developers ask questions in #beginners instead of #clojure because in #beginners people have opted in to help new developers and don't come back with this sort of "just Google it" answer...

seancorfield17:07:44

(and, yes, there is an assumption that in the #clojure channel, developers do have some "basic ... knowledge" and know enough to be able to navigate search results etc -- I just want to point out how/why that is not helpful for beginners)

vemv18:07:34

As food for thought, an acknowledged piece like https://gist.github.com/richhickey/1563cddea1002958f96e7ba9519972d9 can also been seen as "gatekeeping" More often than not I help all sorts of people, that's visible work for people who hang out in #clojure or github and don't appreciate the whole rethoric of shaming and name-calling when one doesn't follow a very specific implementation of how one 'ought' to welcome beginners We live in splendid times for being a beginner, I'd simply be wary of taking this welcoming culture to implementational extremes

vemv19:07:56

Let's leave it at that, I'm simply sharing my views on a topic that I perceive from time to time regardless of the individuals involved Personally I just enjoy questioning the nature of things, sometimes nice exchanges arise from it

seancorfield19:07:06

At the risk of going further into #off-topic land here, Rich's piece is not about "gatekeeping" at all -- nothing about what he says creates a barrier to entry for new developers coming to Clojure. What he (rightly) complains about in that piece is the entitlement that some folks show in regards to how they think he should run his own project and the "demands" they make of OSS maintainers (and as a maintainer of OSS for close to thirty years now, I have to say I agree with him).

👍 3
seancorfield19:07:42

But, yeah, we should probably draw a line under this thread -- and take it elsewhere if we wish to continue.

deleted19:07:26

compojure is pretty popular

seancorfield19:07:00

This is a library that I recommend to beginners so they can understand how routes map to code but I think @U022LK3L4JJ is looking for something more akin to web frameworks in other languages?

👍 2
seancorfield19:07:02

Frameworks in general are not considered very Clojure-y -- folks generally use a combination of their preferred libraries. That said, you might find Luminus (a template that gathers together many web-related libraries) helpful, or you might look at Coast.

👍 2
vemv22:07:34

^ As a perhaps interesting topic, it would help so much to have some sort of agnostic test suite for all things security when it comes to web frameworks/templates/compositions. It's often said that that security is lost when you assemble things by hand, that in that sense frameworks can be greater than the sum of its parts. Which of course doesn't need to be the case but in practice, quite plausibly is