Fork me on GitHub
#beginners
<
2022-02-08
>
James Amberger01:02:50

I been diving in hard over the last week or so but when I felt ready to port a neglected but in-production django app, I started getting disoriented

seancorfield01:02:35

Clojure's approach to web applications is very, very different to something like Django so it's going to depend a lot on what the app does and what features of Django you are relying on.

seancorfield01:02:08

I think Django is one of those frameworks that are based around an ORM, yes? That's not an approach you see in Clojure (because we favor plain data instead of objects and an associated class hierarchy).

James Amberger01:02:27

Thanks for reply. I’m experiencing some research fatigue. I’ve read up on Ring and Compojure and the approach seems fine to me

James Amberger01:02:48

But then I sit down to get started and, oh yeah, I’m gonna need user authentication

James Amberger01:02:58

and there doesn’t seem to be a consensus on how to do simple password-based auth without doing it from scratch

James Amberger01:02:53

I’ve come across buddy and friend and they each have some level of disclaimer that they aren’t live projects going forward

seancorfield01:02:04

Buddy (and Friend) are still fine to use -- but I think the feeling is generally that auth tends to be much more of an app-specific thing than something a generic library can really solve.

James Amberger01:02:52

I guess tomorrow I’ll have to dive in deeper

seancorfield01:02:11

Also, quite a few libraries get to a "complete" state and then get very little active maintenance and can be used for years "as-is".

seancorfield01:02:04

As Adrian said in the other thread, if you can frame concrete questions around what you need to get done, that will make it easier for us to provide recommendations.

James Amberger01:02:08

that is great. If I see a js lib that hasn’t had anything signficant in the commit log for five years I just move on

James Amberger01:02:20

I will surely bee back tomorrow then.

seancorfield01:02:33

Yeah, that's good advice for JS -- but doesn't apply for Clojure 🙂

James Amberger01:02:09

Is anyone present who cares to discuss porting a Django app?

noisesmith01:02:08

You'll get better information if you go ahead and ask a concrete question. I'd start with a ring based web app, maybe using the luminus template (batteries included template for apps using ring with optional clojurescript frontend)

noisesmith01:02:54

the thing is, even when using luminus you are going to be using a pile of libraries which provide your features, rather than a single turnkey monolith

James Amberger01:02:58

Thanks. I expected that I’d be using a pile of libraries, but it’s proving a little overwhelming choosing among them. For instance, password-based auth with a db backend

James Amberger01:02:27

I read up on luminus; the authors have a new thing, [Kit](https://kit-clj.github.io/docs/clojurescript.html)

john2x05:02:36

If I use something like reaver which is a thin wrapper for Jsoup, when I add org.jsoup/jsoup in my deps.edn, does that override the version used by reaver?

dharrigan06:02:57

I believe so, an explicit version in your deps will override any transitive dependency. You can always check by doing clj -Stree and examining the output.

john2x07:02:32

looks like it does. I can see the older version has :use-top appended to it

Alex Miller (Clojure team)13:02:40

Yes, top level dep versions always win

popeye10:02:56

Is there any doc which gives more info while debugging a macro? or which library can be used to debug macro efficiently?

Ferdinand Beyer14:02:17

Not aware of any libraries. Do you know macroexpand-1 ? Macros can be tricky to get right because they transform their input and evaluate the result. With the macroexpand family of functions you can look at the forms produced by your macro before they are evaluated. In general you might want to do as much as possible with regular functions, and keep your macros small. You can often delegate parts of your macro to a function that is easier to test.

noisesmith22:02:18

yeah - a good simple rule is to put logic in functions, and use macros for syntax

V12:02:29

Have a problem that i currently cannot see any way to solve. I have a vector [:header] and another vector

[[[:A]
  [:B]]
 [[:C]
  [:D]]]
I know its a bit of a mess. However what i want to do is to merge these two lists into this:
[:header 
  [:A]
  [:B]
  [:C]
  [:D]]
Anbody who has a clever trick to do so?

flowthing12:02:53

(into [:header]
  cat
  [[[:A]
    [:B]]
   [[:C]
    [:D]]])
;;=> [:header [:A] [:B] [:C] [:D]]

👍 5
🙏 2
🔥 1
V12:02:13

Oh wow, thank you. I've attempted so many things such as into and apply, but i've never quite got it. Thank you very much!

Chase15:02:47

cat seems pretty useful, not sure how I've missed that. flatten seems to be discouraged so I've been trying to find my own way to do such things.

Stuart15:02:25

> flatten seems to be discouraged Why so ?

flowthing16:02:00

Also, it's just weird:

user=> (flatten :a)
()

Fra15:02:43

Hi, should I use func! if it changes agents/atoms/etc as a side-effect or it’s fine just func? what’s your preference? thanks

Ferdinand Beyer16:02:49

Yes, I would use ! for that. When the (main) responsibility of a function is to change state, then I use !.

Ferdinand Beyer16:02:05

Don’t aim for mathematical purity though. Logging is technically a side effect, but this is an implementation detail and does not warrant !.

Fra16:02:27

👍 thanks @U031CHTGX1T

👍 1
James Amberger17:02:24

Hi, clj question. I’m trying to mess around with buddy in the repl. { :deps {buddy/buddy-core {:mvn/version "1.10.1"} buddy/buddy-hashers {:mvn/version "1.8.1"} buddy/buddy-auth {:mvn/version "3.0.1"}} } That’s my deps.edn (the only file in the working directory). I run clj, the libs download, but then I (require '[buddy/buddy-hashers]) and it fails with Could not locate buddy_auth__init.class I sense I’m missing something basic here.

practicalli-johnny17:02:02

@james.amberger should that be (require '[buddy.buddy-hashers]) , actully it seems it should be (require '[buddy.hashers]) , so ideally I would use an alias, so really it would be (require '[buddy.hashers :as hashers])

James Amberger17:02:51

Thanks john; (require '[buddy.hashers]) did the trick

James Amberger17:02:38

How to get syntax highlighting and tab completion in my repl?

practicalli-johnny17:02:33

For syntax highlighing and tab completion • in a terminal repl, use rebel readline • For emacs use Cider and/or Clojure LSP • For VS Code, use Calva (which uses Clojure LSP) • For intellij, use Cursive • For NeoVim, use Conjure, CoC and Clojure LSP

James Amberger17:02:17

I had rebel readline in my deps.edn before but I did not hvae colors/completion. I tried to look around for necessary additional steps

ghadi17:02:19

I don't focus my comforts on the REPL as much as an editor-connected REPL

ghadi17:02:30

I almost never type into my REPL directly

James Amberger17:02:32

Yeah I have vim-fireplace working

James Amberger17:02:42

It’s very cool

James Amberger17:02:50

ahh, much better. Thanks John

practicalli-johnny17:02:57

When using Neovim, I use the following configuration https://github.com/rafaeldelboni/nvim-fennel-lsp-conjure-as-clojure-ide There may be some ideas you could borrow from it also.

practicalli-johnny17:02:00

Rebel Readline is a very effective terminal UI to the REPL process and very handy when you dont have an editor connected to the REPL. Obviously it wont have all the syntax highlighting and completion available from a complete Clojure IDE or if using Clojure LSP, but they do require a little more investment to setup.

James Amberger17:02:53

online help with rebel readline ?

practicalli-johnny17:02:40

rebel readline has eldoc, so should function arguments as you type. There is also a key binding to show docs - type :repl/key-bindings to see the current key bindings

practicalli-johnny17:02:46

If more than that is needed, then either use a web browser or move to an editor / ide connected REPL setup

1
quan xing19:02:20

I used com.github.seancorfield/next.jdbc   {:mvn/version "1.2.761"} in deps. call create table sql show error: ; Execution error (ClassCastException) at next.jdbc.prepare/create (prepare.clj:94). ; java.lang.Character cannot be cast to java.lang.String

(def mysql-db {
                 :dbtype "mysql"
                 :dbname "bzst_db"
                 :user "root"
                 :password "root"
                 :jdbcUrl "jdbc:"
                 }    )
  
  (def schema-account-holders-table
    ["CREATE TABLE account_holders (id VARCHAR(32) NOT NULL"])
  
  (create-tables! schema-account-holders-table mysql-db)

dpsutton19:02:17

character cannot be cast to string usually means you’ve called something that expects a sequence of strings with a single string

dpsutton19:02:18

you’re using some function called create-tables! which we don’t see the source of here. I’m guessing it bottoms out in some sort of (doseq [table tables] (jdbc/execute! table) and i think you should read the doc string of the jdbc function you are calling

practicalli-johnny19:02:46

Missing closing bracket in schema-account-holders-table

practicalli-johnny19:02:29

Missing closing bracket for the opening bracket before id

dpsutton19:02:45

good point. once you can transact your sql syntax will fail, but you haven’t gotten to a point yet where the contents of those strings matter

practicalli-johnny19:02:48

After NOT NULL and before the "

seancorfield19:02:06

@U025AG2H55F You need to share your create-tables! function for us to help.

quan xing00:02:32

Thanks everyone, I solved it. like @U11BV7MTK said that I used the String pass to Vector

1
mbjarland19:02:32

Umm…so I’m looking around for a clojure way of performing some maths with numbers where numbers could be doubles, ints, rationals, big decimals. Specifically I was hoping for a sqrt (among others) function which would work with big decimals but would not break if called with some other type. Something like:

(with-precision 10
  (.sqrt 3M *math-context*))
works for BigDecimal specifically, but breaks if I send in a double etc. The numerical tower lib coerces big decimals to doubles which is not workable for my case. Is there some lib out there which has basic numeric functions for clojure which handle mixed number types?

mbjarland19:02:26

preferably there should be some attempt at retaining types, i.e. if I use doubles it should produce doubles, if I use BigDecimal it should procuce BigDecimal etc

mbjarland19:02:11

this seems kind of basic but I came up empty after some attempt at googling

mbjarland19:02:47

I guess I can implement this myself via a defmulti and delegation to the java methods

mbjarland19:02:56

just figured there would be something out there

hiredman19:02:33

I think there is kind of no man's land between enterprise kind of systems where math isn't a huge priority, and machine learning systems where math is a huge priority

hiredman19:02:45

like the one is likely to just "solve" this kind of issue by just using bigdecimals, the other is all matrices, possibly off heap, where you want to avoid bigdecimal

hiredman19:02:25

where there is a significant interest in math, it seems to fall more on symbolic manipulation of equations

mbjarland20:02:46

@hiredman thanks for that…a lot of good stuff in sicmutils…will need to stare at that code for a while to see if it covers my needs but definitely looks promising

James Amberger20:02:17

Trying to decode base64

James Amberger20:02:30

This is proving difficult. I have imported Base64$Decoder

James Amberger20:02:48

but I can’t seem to call getDecoder no matter what I do

flowthing20:02:24

user=> (import '(java.util Base64))
java.util.Base64
user=> (def s (.encodeToString (Base64/getEncoder) (.getBytes "Hello, world!")))
#'user/s
user=> (String. (.decode (Base64/getDecoder) s) "UTF-8")
"Hello, world!"

manutter5120:02:19

In clj or cljs?

James Amberger20:02:20

(made me look though!)

manutter5120:02:31

Ok, the easiest way to decode Base 64 is to use the static method: Base64/decodeBase64

R.A. Porter20:02:35

(.decode (java.util.Base64/getDecoder) b) will work, but you'll want to annotate the type of that param.

James Amberger20:02:55

ok, looks like I didn’t actually call getDecoder

manutter5120:02:50

Yeah, that kind of bug is particularly difficult to spot.

James Amberger20:02:40

how can I call Base64/decodeBase64?

R.A. Porter20:02:12

I don't know where that method exists.

manutter5120:02:19

That’s a static method on the class.

R.A. Porter20:02:51

Are you referring to org.apache.commons.codec.binary.Base64?

James Amberger20:02:03

java.util.Base64/decodeBase64 gets me Unable to find static field

manutter5120:02:27

Ah, wait, I’m referring to an old project, and it’s pulling in org.apache.commons.codec.binary.Base64, yeah

manutter5120:02:37

Sorry about that, I forgot I’d pulled that in.

R.A. Porter20:02:48

Try the one I posted above. It should work for you.

James Amberger20:02:54

isn’t this kind of static this, return a ‘Decoder’ that stuff the very sort of thing we hope to avoid by using clojure?

R.A. Porter20:02:59

It is, and you could find or write your own wrapper function for it. That's what I use. But interop is interop.

James Amberger20:02:01

so I would have to make a simple function out of it myself, not hard but

seancorfield20:02:12

dev=> (let [decoder (java.util.Base64/getDecoder)]
 #_=>   (seq (.decode decoder "1234")))
(-41 109 -8)
(edited for readability of the result)

ghadi20:02:15

Clojure is hosted within a platform, and aims to reuse / leverage all the existing correct code

ghadi20:02:17

pro tip: always paste what you tried

James Amberger20:02:33

k thanks, I will

ghadi20:02:06

(The Java Base64 methods are getting SIMD acceleration soon)

ghadi20:02:30

(reuse/leverage >>> uncomfortable interop)

☝️ 1
James Amberger20:02:29

@ghadi can you rephrase that?

ghadi20:02:46

which piece?

James Amberger20:02:48

last, what’s SIMD acceleration, and what’s that >>> relation mean

ghadi20:02:03

SIMD acceleration is a way to improve performance of algorithms like base64: do things a block at a time, rather than byte at a time >>> meaning just "way better than"

seancorfield20:02:40

@james.amberger FWIW, I used to be a big fan of wrapper libraries when I first got started with Clojure years ago but I've come around to preferring direct interop these days -- unless you are writing .cljc code and need portability between Clojure and ClojureScript.

James Amberger20:02:01

yeah, I have seeen ‘cljc’ a couple of times now. What is it?

James Amberger20:02:39

some kind of precomiled

seancorfield20:02:43

.cljs is ClojureScript, .clj is Clojure, .cljc is accepted by both Clojure and ClojureScript

Alex Miller (Clojure team)21:02:01

the "c" at the end of cljc == "common"

seancorfield20:02:06

So you can have conditional (read-time) code in .cljc files to allow you to share logic between frontend and backend.

James Amberger20:02:35

that’s appealing

James Amberger20:02:10

read-time = compile time for cljs?

seancorfield20:02:35

It's more and more possible these days to write portable code -- see https://github.com/seancorfield/honeysql/blob/develop/src/honey/sql.cljc which only has four conditionals in it (see https://github.com/seancorfield/honeysql/blob/develop/src/honey/sql.cljc#L172 for example).

seancorfield20:02:53

Read-time means read-time. Parsing. Pre-compilation.

James Amberger21:02:29

yeah the more I look at this the more it seems that where in Python or TS I would have been reaching for a library, in Clojure I’m supposed to just do it

seancorfield21:02:24

clojure.core is very powerful, and the whole of the Java standard library is also right there "just below the surface". A lot of batteries included.

👍 1