Fork me on GitHub
#beginners
<
2022-08-08
>
quan xing02:08:37

In the next.jdbc I use jdbc/with-logging output: next.jdbc/execute-one! ["UPDATE bz_project SET sgt_review_no = ? WHERE ID=?" "A001" 100] is it can output the full SQL directly? like this : ["UPDATE bz_project SET sgt_review_no = "A001" WHERE ID=100"

hiredman03:08:17

Doesn't exist, so cannot be logged

hiredman03:08:04

next.jdbc never constructs a strong like that, the sql and the parameters get turned into a java.sql.PreparedStatement, which is handled internally in the jdbc driver for the database you use

hiredman03:08:53

And there is a good chance the jdbc driver you use doesn't construct such a string either, since databases often have some kind of prepared query with parameters feature in their wire protocol

seancorfield03:08:53

@U025AG2H55F Per the docs https://cljdoc.org/d/com.github.seancorfield/next.jdbc/1.2.796/doc/getting-started?q=with-logging#logging-sql-and-parameters the logging function is passed two arguments: a symbol indicating the operation and the vector containing the string and arguments. Your logging function can do whatever you want with those two things, but to produce inline SQL from parameterized SQL you would have to take care of that inlining yourself.

quan xing03:08:22

Ok, I got it. thanks. I can use honeysql with {:inline true} so I don't have to take care of that inlining myself.

Pedja Zolinsky15:08:15

What is the go-to IDE/ local dev setup for Clojure devs? I am seeing the Emacs, but as someone who exclusively uses the VIM, it just feels wrong

sheluchin15:08:25

#vim is fine. Emacs is favored - I think - because it's written in a lisp. Calva, Cursive, and IntelliJ are also popular options. For vim, you might want to check out https://github.com/rafaeldelboni/nvim-fennel-lsp-conjure-as-clojure-ide

lispyclouds15:08:53

I use nvim exclusively too and neovim + https://github.com/Olical/conjure or vim + https://github.com/liquidz/vim-iced worked fairly well for me

πŸ‘† 1
flowthing16:08:02

There's a whole bunch of options. If you're a Vim user, you might be interested in https://conjure.fun, https://www.spacemacs.org/layers/+lang/clojure/README.html, or https://github.com/tpope/vim-fireplace, for example.

dharrigan16:08:27

I use vim + conjure + coc + clojure-lsp. It's a very workable solution. I live in vim all day πŸ™‚

dharrigan16:08:04

Here's my vim config, it may be helpful (it may not!) πŸ™‚ https://github.com/dharrigan/vimconfig

Ben Sless16:08:44

Emacs has a great vim implementation called Evil πŸ™‚

πŸ’― 3
delaguardo16:08:39

personal preferences imho. emacs is popular among Clojure devs purely because of historical reasons (inferior-lisp mode from the box, cider recently celebrated 10th anniversary, etc.) now there are plugins for almost every popular editor and they all very good for day to day work. just peek the one you know the most

πŸ‘ 2
Kelsey Sorrels16:08:01

Vim & Clojure user here. As long as I have rainbow parens, I'm a happy camper :)

teodorlu17:08:00

VSCode with #calva is also great. Very easy to set up / get started with. REPL-driven workflows in clojure stand out compared to typical development in other languages, so the monolithic Calva experience might be nice to compare with an a la carte Vim setup.

☝️ 1
ahungry19:08:44

I'm not sure of the extensibility of other editors, but on Emacs, in a few lines of code I was able to add a feature I wanted to the clojure mode (CIDER) that would let me put a cursor over a function, press a hotkey, and be prompted for input args to execute the function with (as opposed to hard coding a temporary function call or typing the function name in the REPL) - this isn't something special with clojure, its just how Emacs works (you want it to run some custom code/logic that isn't in an existing package, you can just whip it up) - and the things you may enjoy with clojure (an iterative REPL approach) is exactly how you develop for Emacs (as opposed to writing some ugly vim macro language commands, or some external JS package that you then have to install in your non-emacs editor)

seancorfield20:08:32

My recommendation to Clojure beginners is to always start with the editor you're already most comfortable with, as long as it has a Clojure integration that allows you to eval code from the editor into a connected REPL so that you can experience the iterative, incremental workflow that makes Clojure pretty unique. Trying to lean Emacs at the same time as Clojure is likely to be a frustrating experience. Stick to your "home" editor, learn how its Clojure integration works and once you're comfortable with that workflow, then maybe take a look at other editors. You can see the spread of editor usage over time here https://clojure.org/news/2022/06/02/state-of-clojure-2022 and you can see that as the community has grown, Emacs has dropped from about 50% to about 40% usage -- still the most widely-used -- with IntelliJ/Cursive steady at 30% and VS Code/Calva growing from 10% to about 20%. vim has remained steady at about 10%. There are channels here dedicated to each of the editors/plugins so you'll be able to get help no matter which you end up choosing @U03P01XST0W

thanks3 2
πŸ™Œ 1
didibus02:08:24

There's no go-to, community is quite split. That's mostly because almost all editors have a very good Clojure story currently. You can get most features in Emacs, Vim, VSCode, IntelliJ, Sublime, etc. I think there's even someone who make a decent Notepad++ Clojure plugin. So really use what you want.

Ben19:08:29

Does clojure have any date/time stuff in its core library, or is it all calls out to java? Where does one start with date & time if we don't know java already? There seems to be some change or updates when googling

pppaul19:08:28

juxt/tick is pretty good

dpsutton19:08:30

built into the jvm is java time. An hour reading the docs and then using these native classes will save you a lot of headache

πŸ‘ 2
pppaul19:08:38

if you use java Time, be sure to note down how to do Date/Instance conversions

Ben19:08:58

Thanks! Gonna be doing some work with the jira API and cycletimes, so converting from strings and doing math. Man I hate dealing with dates and times! πŸ˜„

Mario Trost19:08:28

I just researched a little bit this weekend and found this: https://nextjournal.com/schmudde/java-time

Mario Trost19:08:44

Perhaps it’s helpful

Daniel Craig05:08:30

avoid java.util.Date at all costs. Use java.time πŸ™‚

avillegas20:08:24

Hi, I am writting a toy programming language using clojure. I am writting some code for the tokenizing/lexing phase but I feel is not the best it can be but also I don't know how to make it more idiomatic. I basically have vector of tokens regex and I want to match it against the "source code" once I find one I want to return such token. The code currently looks like this

(def tokens-tys                                                                     
  [[:fun #"^\bfun\b"]                                                               
   [:ident #"^\b[a-zA-Z]+\b"]                                                       
   [:opar #"^\("]                                                                   
   [:cpar #"^\)"]                                                                   
   [:arrow #"^=>"]])                                                                
                                                                                    
(defn next-token [code]                                                             
  (loop [tokens tokens-tys]                                                         
    (let [[ty re] (first tokens)                                                    
          matched (re-find re code)                                                 
          next-tokens (next tokens)]                                                
      (if matched                                                                   
        [ty matched]                                                                
        (if (empty? next-tokens) [:error "no token"] (recur next-tokens))))))
Then I try to accumulate all the token in a single vector, and thisi s the part of the code I am less happy about
(defn tokenize [code]                                                               
  (loop [tokens []                                                                  
         code code]                                                                 
    (if (s/blank? code)                                                             
      tokens                                                                        
      (let [[ty lexeme] (next-token code)                                           
            tokens (conj tokens [ty lexeme])]                                       
        (if (= :error ty)                                                       
          tokens                                                                
          (recur (conj tokens [ty lexeme]) (s/trim #dbg (subs code (count lexeme)))))))))

pppaul20:08:38

would highly recommend instaparse

pppaul20:08:25

parsing the output of instaparse with core.match is very nice too. instaparse has it's own issues to deal with, but i think it's a lot more maintainable than using regex.

pppaul20:08:09

also, if you just want people to pick apart your code, you have to say why you are unhappy about it. people who use clojure for their job are used to looking at code that is very messy. your code doesn't look like it is going to be hard to debug.

pppaul20:08:30

the only suggestion i can make is, probably don't use subs, make your own that doesn't throw when given bad args

pppaul20:08:17

your tokenize function doesn't look like it should compile. lexeme is out of scope. actually that's just because my screen word wrapped the code. nvm.

hiredman20:08:54

with some rewriting you may be able to do something like (map :token (iterate tokenize {:token nil :code code}))

hiredman20:08:45

e.g. make not loop and instead take and return {:token token :code rest-of-code}

pppaul20:08:11

@U0NCTKEV8 iterate magic... i need to use that more often. however language parsers generate trees, do you think that iterate would also work in that scenario? like the tokenize function would have an iterate inside it as well.

hiredman20:08:08

tokenize doesn't generate trees

hiredman20:08:22

it generates tokens, the parser will look at tokens and build a tree

avillegas20:08:35

yeah I am not building the AST yet, just tokenizing. will take a look at iterate. Yeah I am aware of instaparse, but I want to do it without external libs as much as possible, just for learning.

pppaul20:08:49

ok, i got that messed up. more general idea is if people use iterate to generate trees

hiredman20:08:47

in theory you can, it isn't as elegant because iterate returns a seq of values, so each result would have a partial tree that the next result expands

hiredman20:08:24

which really only makes sense for LR (bottom up) parsing

hiredman20:08:43

where most people do LL I think

pppaul20:08:00

@UCDGC44GP i know the feeling, i tried something like this as well, and after making almost no progress i used instaparse and had a lot of fun learning to write grammars and building something useful after many attempts to make a grammar not ambiguous. highly recommend if you like playing with regex, cus you basically get a language where all your vars are regex bits

avillegas20:08:41

What I don't like about the code is that it has a many nested if expressions. which I feel should be simplified. Also I tend to use let alot because I am used to introduce variables in other languages, but I feel there should be a better way in clojure

avillegas20:08:12

probably defining my process using a thread macro and a bunch of small functions or something like that.

pppaul20:08:18

@UCDGC44GP to deal with nested ifs, i use cond/case, if cond looks ugly then i try core.match

avillegas20:08:24

but could come up with something myself

pppaul20:08:35

core.match is good if you don't care about speed, and care that your code is self documenting

pppaul20:08:57

i like code that has a lot of lets over code that has a lot of nested logic

pppaul21:08:04

you can use if-let and when-let some-let sometimes they make code a bit better

pppaul21:08:41

for your code, you could make your lets top level, and use cond instead of nested ifs, it means you do logic before your if, but it's not much

pppaul21:08:41

(empty? next-token) replace with (not-empty next-token)

pppaul21:08:13

that way you can just test next-token, as not-empty returns nil when empty

pppaul21:08:36

i always prefer not-empty

pppaul21:08:05

(not-empty (next tokens))

pppaul21:08:16

next will also return nil, wont it?

pppaul21:08:44

empty? is redundant

pppaul21:08:58

(defn tokenize [code]
  (loop [tokens []
         code   code]
    (let [[ty lexeme] (next-token code)
          tokens                          (conj tokens [ty lexeme])
          next-code                       (s/trim (subs code (count lexeme)))
          next-tokens                     (conj tokens [ty lexeme])]
      (cond
        (s/blank? code) tokens
        (= ty :error)   tokens
        :else           (recur next-tokens next-code)))))

❀️ 1
pppaul21:08:59

my preference is to not do a bunch of logic in my return val, i sorta care about it's shape, or having control flow be self documenting. so i do all my logic in my let. it also means that debugging is pretty easy

pppaul21:08:00

because clojure is mostly lazy, it's ok to do expensive stuff in the lets, as the control flow determines what code gets run

pppaul21:08:29

but it means you need to figure out names for somewhat meaningless things. depends on the situation

Toni Vanhala05:08:33

Instaparse is a great suggestion. If you want to do this the hard way, building a tokenizer from scratch, you could follow the book https://craftinginterpreters.com/ (free html version) The book uses Java, though. I've worked on implementing the first parts in Clojure: https://github.com/tonivanhala/cljblox

avillegas19:08:03

I have implemented many tokenizers and parsers from scratch, it's kind of my go to project to try out a language. Was I was trying was to get some ideas in idiomatic clojure. My tokenizer in clojure ended up being very procedural which is what I was trying to avoid, just to get a better understanding of clojure

hiredman21:08:27

If you haven't heard of parsec that might be interesting to check out, it is a Haskell parser combinator library (very functional) with some interesting design decisions. I have a toy clojure port (nothing published anywhere) that I've found to be pretty good. A lot of parser combinator libraries try to handle any grammar and have unlimited look ahead, and don't really have the concept of lexing, but parsec by default restricts grammars to ll(1), but for the parsers that parse individual lexemes you mark them as having unlimited look ahead, which makes everything work very similarly to a lexer+parser

hiredman21:08:38

https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/parsec-paper-letter.pdf is the original parsec paper, http://panicsonic.blogspot.com/2009/12/adventures-in-parsec.html?m=1 is a blog post that describes replacing a lot monadic case analysis with basically continuations

avillegas20:08:18

if you have any recommendation on how can I make it better it is welcome

zakkor20:08:36

is there any way to get weavejester/hashp to not sort map keys alphabetically? I'd like them printed in the same order as something like clojure.pprint does

rolt21:08:30

(untested) (alter-var-root #'puget.printer/*options* dissoc :sort-keys)

sheluchin23:08:30

If a function is defined in both clj and cljs in a library (not just as cljc), are there any safe assumptions you can make about it? Should they be considered roughly equivalent or treated as totally separate?

seancorfield23:08:19

I would check the library's docs, then its source code. I wouldn't assume equivalency unless the docs explicitly say that somewhere.

seancorfield23:08:08

(but I wouldn't start with the source so if the docs say (foo x) behaves in a particular way -- and the library indicates it's both clj and cljs compatible -- and there are no caveats about foo's behavior across platforms, I probably wouldn't even think to check whether foo is defined in clj, cljs, or cljc)

sheluchin12:08:15

@U04V70XH6 interesting. It sounds like the safe approach is to always check, but in practice it matters less so. Thanks for your input.