Fork me on GitHub
#beginners
<
2020-09-29
>
bcaccinolo09:09:11

is it possible to embed a clj interpreter (like sci) in a java project to evaluate clj formulas stored in database ? 🙂

borkdude09:09:34

yes, you can embed sci in java

borkdude09:09:50

or you can just use Clojure directly, if you trust the code

bcaccinolo09:09:05

the idea is to create a formula engine

bcaccinolo09:09:07

clj looks great for that cause it's easy to parse the code stored in a string

bcaccinolo09:09:51

@borkdude cool , good to know 🙂

borkdude09:09:19

@benoit.caccinolo yes. you can use load-string from Clojure or eval-string from sci. Sci had a Java interface, but since nobody was using it, I deleted it. https://github.com/borkdude/sci/commit/6bbccc3eec050dd0d4fc9cf017b89577e0269494

borkdude09:09:34

but it's not so hard to make it yourself

borkdude09:09:12

Most of the work in the Java API was to create Option classes and convert them into maps. Not a lot of fun

borkdude09:09:37

So I figure if you want to use sci, make a little Clojure wrapper with your options and then call that from Java

bcaccinolo09:09:26

Do you mean it would be easier with vanilla clojure ?

borkdude09:09:52

Sci is easier to use from Clojure than from Java. So make a little Clojure wrapper around sci (specific for your use case) that you call from Java, if you want to use sci.

bcaccinolo09:09:09

ok I look at this

bcaccinolo09:09:48

thx for the fast answer, I'll look at this.

borkdude09:09:18

I'm considering removing the JS api from sci as well. Most people need specific options. It's easier to just call it from CLJS specific to a project and then compile that to JS

Audrius10:09:56

Hi, I have an algorithm to implement that "buys" goods from list of maps [{:name "good 1" :price 12} {:name "good 2" :price 17} ...]. Every good may have a different price. And I want to keep buying until it has no more cash left (some finite amount). Is there an idiomatic way to do it in Clojure? Something like reduce-until 🤔

jsn10:09:51

I don't think so -- you'll need to carry the state (e.g. cash left) around, it's cumbersome. I think loop is a good fit.

Audrius11:09:40

I also did not find anything elegant here so my solution:

(def cash-spent (atom 0))
(defn accumulate-price [item limit]
  (swap! cash-spent #(+ % (get item "price")))
  (>= limit @cash-spent))

(take-while #(accumulate-price % cash-available items))

markmarkmark11:09:50

you can used reduced to duck out of a reduce early. I'm not completely sure that this does what you want, but something like:

(reduce                                  
  (fn [cash-spent item]                  
    (if (>= limit cash-spent)            
      (reduced cash-spent)               
      (+ cash-spent (get item "price"))))
  0                                      
  items)                                 

Stas Makarov12:09:15

What's the magic behind map->ResponseFormat here? https://github.com/JulianBirch/cljs-ajax/blob/master/src/ajax/interceptors.cljc#L223 I expected to find a regular function with such name defined somethere in cljs-ajax codebase but couldn't. Is it some sort of a convention?

delaguardo12:09:57

each defrecord defines few functions: ->RecordName and map->RecordName https://github.com/JulianBirch/cljs-ajax/blob/master/src/ajax/interceptors.cljc#L93

Jim Newton12:09:08

Is there a way to ask the Clojure testing env to tell me which tests are running rather than waiting to the end? One of my tests sometimes takes a very long time. I could do a binary search to find it, but it would be nice if I could just turn on a verbose mode

andy.fingerhut13:09:58

If you are using the clojure.test library for writing tests, there are some ways to write code that is executed at the beginning or end of each deftest form. An example of using that to print a message when each deftest begins, and to print how long in elapsed time each one took to finish, is here: https://github.com/clojure/core.rrb-vector/blob/master/src/test/clojure/clojure/core/rrb_vector/test_utils.clj#L52-L64

andy.fingerhut13:09:34

It has been a while since I wrote that code, but if I recall correctly, I put that code, and some other "test utility functions", into one namespace defined in that file, and then :require it from all other namespaces in that project that have deftest forms.

practicalli15:09:47

Kaocha test runner will tell you which tests too the longest to run https://cljdoc.org/d/lambdaisland/kaocha/1.0.700/doc/1-introduction There is also a reporter plugin to show progress, although my tests run so fast I cant tell if its doing them one by one (I think it does)

Jim Newton16:09:12

Is the idea of kaocha that I still have the code using the normal testing macros, deftest, is, testing, but I need to require something different?

practicalli17:09:25

Kaocha will run clojure.test unit tests and many other things. No changes are required to the tests. Some basic examples here https://practicalli.github.io/clojure/testing/test-runners/kaocha-test-runner.html

cljnoob15:09:58

Hello all, first time joining, so forgive me if this is the wrong place to ask. I am trying to query postgres using clojure.java.jdbc and then trying to put the results into a vector which I then send inside of a json object. When I try to do this, the results vector sent back is empty. Could someone help me identify the problem? My thoughts were that this is a side effects issue, but I tried enclosing things in a do block and I still had the same issue. Here is the code: (defn get-meets [request]

practicalli15:09:39

Try sharing the code in a GitHub gist file or in a Git repository if you can, it will make it easier (unless its just a few lines of code) Recommend you use the https://github.com/seancorfield/next-jdbc as this replaces clojure.java.jdbc There is also a #sql channel here where you may get specific help. I am also building a website using next.jdbc that talks to H2 and postgresql database which I am documenting here: https://practicalli.github.io/clojure-webapps/projects/banking-on-clojure/ There is also a video series of how I am working through creating this application. Its work in progress, but hopefully you will find it useful

gon15:09:05

clojure data is immutable, (reduce conj results rs) does not fill up the results vector you have defined above, and the resultset is lost,

(defn get-records [request]
  (let [{limit :limit, offset :offset, type :type} (:params request)
        results []]
    (response
     (try
       (cheshire/generate-string {:results (doall
                  (jdbc/query db-spec
                   ["SELECT * FROM records ORDER BY on_date LIMIT ? OFFSET ?;" (Integer/parseInt limit) (Integer/parseInt offset)]              ))})
       (catch Exception e (cheshire/generate-string {:error (.getMessage e)}))))))
try this, I didn't try but it should work

cljnoob16:09:32

Wow that worked! Thank you. Also @U05254DQM I will keep up with your video series as i'm sure it wil be useful, thank you for that as well.

cljnoob16:09:49

and from now on I will send things as github files, sorry for clouding up the chat

practicalli18:09:03

Code of a handful of lines is okay to post here, yours was fine. If you want to post a lot of code then Gist or repo is better.

cljnoob15:09:08

Sorry, the copying got messed up. Trying again.

cljnoob15:09:53

(defn get-records [request] (let [{limit :limit, offset :offset, type :type} (:params request) results []] (response (try (jdbc/query db-spec ["SELECT * FROM records ORDER BY on_date LIMIT ? OFFSET ?;" (Integer/parseInt limit) (Integer/parseInt offset)] {:result-set-fn (fn [rs] (reduce conj results rs))}) (cheshire/generate-string {:results results}) (catch Exception e (cheshire/generate-string {:error (.getMessage e)}))))))

Jeff Evans16:09:31

can someone help me figure out how to stop this macro from wrapping the entire result in an extra () ?

(defprotocol MyProtocol (foo [x]))

(defmacro my-extend-helper [prot & extensions] (let [protocol (-> prot resolve deref)
                                                     m (-> protocol :sigs keys first)
                                                     params (-> protocol :sigs first last :arglists first)]
                                                 (for [[atype x] extensions]
                                                   `@(extend-protocol ~prot ~atype
                                                       (~m ~params ~x)))

                                                 ))

(macroexpand '(my-extend-helper MyProtocol [clojure.lang.PersistentVector (first x)]))
=> ((clojure.core/extend-protocol MyProtocol clojure.lang.PersistentVector (:foo [x] (first x))))

teodorlu16:09:48

@jeffrey.wayne.evans unquote-splicing might be of help, if you haven't seen it: https://clojuredocs.org/clojure.core/unquote-splicing

Jeff Evans16:09:05

yeah I was playing with that, but couldn’t quite get it. will refocus there again. thanks!

marciol18:09:10

I was doing experiments with reducers and somethink caught my attention, the return of reducer functions doesn’t return a IPersistentCollection, so someone knows why? Example:

user=> (let [result (clojure.core.reducers/flatten [1 2 [3 4]])]
         (println "is collection: " (coll? result))
         (println "type: " (type result)))
is collection:  false
type:  clojure.core.reducers$folder$reify__5949
nil

marciol18:09:33

And the docs of reducers/flatten states that it returns a foldable collection:

user=> (doc clojure.core.reducers/flatten)
-------------------------
clojure.core.reducers/flatten
([] [coll])
  Takes any nested combination of sequential things (lists, vectors,
  etc.) and returns their contents as a single, flat foldable
  collection.
nil

bronsa18:09:12

a foldable collection, not a collection. i.e. folding/reducing it will produce a collection

bronsa18:09:50

user=> (into [] (clojure.core.reducers/flatten [1 2 [3 4]]))
[1 2 3 4]

marciol18:09:58

> a foldable collection, not a collection. i.e. folding/reducing it will produce a collection (edited) Yes, it’s clear right now, paying attention to foldable qualifier 😄

marciol18:09:58

> you might want to peruse https://clojure.org/news/2012/05/08/reducers https://clojure.org/news/2012/05/15/anatomy-of-reducer Thank you @alexmiller, I’ll dig deep into this

Daniel Stephens18:09:28

hi, I'm trying to get started with some deps.edn stuff, I have a problem finding standard dependencies. In my ~/.m2/settings.xml I have just one repository set up which is authenticated for some work stuff. When I try to get a dep in my deps.edn it falls over with a timeout because the configured repo doesn't have the dependency. I've been using lein until now and it finds the dependency fine, I assume that's some hidden defaults so seems a reasonable reaction from deps. Question is, can I add some other repositories to look at into deps.edn, I tried with :mvn/repos which I was hopeful for but still seems to timeout looking in the wrong place.

{:paths ["src"]
 :deps {org.clojure/clojure {:mvn/version "1.10.1"}}
 :aliases {:test {:extra-paths ["test"]
                  :extra-deps {eftest {:mvn/version "0.5.9"}} ;tried with eftest/eftest as well
                  }}
 ;still no luck after adding these 
 ;:mvn/repos {"central" {:url ""}
 ;            "clojars" {:url ""}}
 }
> clojure -M:test
Error building classpath. Failed to read artifact descriptor for eftest:eftest:jar:0.5.9
    at ...
Caused by: org.eclipse.aether.resolution.ArtifactResolutionException: Could not transfer artifact eftest:eftest:pom:0.5.9 from/to central (): connect timed out

alexmiller19:09:52

the "looking in the wrong place" is probably just a misleading error message - it searches all of the repos in order and will typically just report one of those errors (so that can look confusing, but it should be checking all of them)

alexmiller19:09:53

the central and clojars repos are included by default

alexmiller19:09:34

clj -Sdeps '{:deps {eftest/eftest {:mvn/version "0.5.9"}}}' worked for me - downloaded eftest from clojars

alexmiller19:09:09

I think you've got something funky in your ~/.m2/settings.xml that is causing the problems

Daniel Stephens19:09:49

ahh interesting thanks, in ~/.m2/settings.xml I have something like:

<settings xmlns=""
          xmlns:xsi=""
          xsi:schemaLocation=" ">
 
  <servers>
    <server>
      <id>x</id>
      <username>y</username>
      <password>z</password>
    </server>
  </servers>
 
   <mirrors>
    <mirror>
      <id>x</id>
      <url></url>
      <mirrorOf>*</mirrorOf>
    </mirror>
  </mirrors>
 
</settings>
sorry I can't share it more fully

alexmiller19:09:31

yeah, I think it's timing out accessing that mirror

Daniel Stephens19:09:32

I'll try and look into the xml side more @alexmiller though this was just requested by my work to be used

Daniel Stephens19:09:09

that makes sense, I was hoping I could override that in the deps.edn without changing my xml but I guess not

Daniel Stephens19:09:23

I suppose lein is just ignoring that setup

alexmiller19:09:25

if that's a company nexus repo or something like that, you may need to do some other process to get it retrieved and loaded in your nexus

alexmiller19:09:47

not uncommon to proxy all maven traffic through things like that

Daniel Stephens19:09:13

yeah, this was a little personal project I was doing on the side to try out deps, but might not work out

Daniel Stephens19:09:44

well thanks for the help, sounds like nothing wrong with deps at least!

Daniel Stephens19:09:15

mv ~/.m2/settings.xml ~/.m2/definitely-not-settings.xml working great now 😂

Daniel Stephens19:09:23

Thanks again Alex

x3qt19:09:20

Hi there! Can someone explain to me why this is not working?

((resolve (symbol (str "Math" "/sqrt"))) 25)

hiredman19:09:31

The big reason is Math/sqrt is a java static method, which the compiler statically figures out

hiredman19:09:48

It is not a var in a namespace which is what resolve looks up

x3qt20:09:01

Oh, got it, thank you

hiredman20:09:43

The / is also not part of the symbol name, it just distinguishes the namespace from the name when printing a symbol

x3qt20:09:18

The reason I am trying to do it is to create a macro to dynamically define routes the following way –

(ns arborist.handler
  (:require [compojure.core :refer :all]
            [compojure.route :as route]
            [arborist.users :as users]
            [ring.middleware.defaults :refer [wrap-defaults site-defaults]]))

;; TODO: map over collection of http verbs to generate routes and actions
;; with something like ((resolve (symbol "+")) 5)
(defmacro rest-resource [resource]
  (context (str "/" resource) []
    (GET "/" [] (users/index))
    (POST "/" [] (users/create))
    (GET "/:user-id" [user-id] (users/show user-id))
    (PATCH "/:user-id" [user-id] (users/update user-id))
    (DELETE "/:user-id" [user-id] (users/update user-id))))

(defn greet [name] (str "Hello, " name "!"))
(greet "Yury")

(defroutes app-routes
  (GET "/" [] "Welcome to Arborist")
  (GET "/request" request (str request))

  (rest-resource "users")
  (rest-resource "notes")

  (route/not-found "Not Found"))

(def app
  (wrap-defaults app-routes site-defaults))

x3qt20:09:30

So I want to generate routes from the resource name passed

x3qt20:09:37

The code above is working

hiredman20:09:30

Like, you aren't getting any errors trying to use it, but you have sort of fallen into a crevasse

hiredman20:09:44

Where pretty much any change you make will break it

x3qt20:09:58

Can you show me how this macro should look?

hiredman20:09:22

You don't entirely understand how macros work and mixing up your compile time and runtime

x3qt20:09:37

Yeah, I got it the first time I tried to do it

hiredman20:09:48

I would recommend rewriting it without a macro

hiredman20:09:04

You don't need one

x3qt20:09:01

I am coming from ruby and in rails it is very convenient to generate routes and the rest just from the resource name

hiredman20:09:06

rest-resource will work just like it does now if you make it regular function

hiredman20:09:32

And you won't entangle yourself in runtime vs compile time

x3qt20:09:41

I’ve tried to write it the following way –

(defmacro rest-resource [resource]
  `(context (str "/" ~resource) []
    (GET "/" [] (users/index))
    (POST "/" [] (users/create))
    (GET "/:user-id" [user-id] (users/show user-id))
    (PATCH "/:user-id" [user-id] (users/update user-id))
    (DELETE "/:user-id" [user-id] (users/update user-id))))

x3qt20:09:56

But it give me clojure.lang.Compiler$CompilerException Syntax error macroexpanding clojure.core/let at (arborist/handler.clj:24:3).

hiredman20:09:30

It is just a normal function, you don't need or want a macro

x3qt20:09:23

You are right, defn is working too

x3qt20:09:59

So, is there a way to resolve resource name in a place like this?

(GET "/" [] (users/index))

dharrigan20:09:13

If I may suggest (and if you're not fixed to use only compojure) here's an example of using dynamic routes with reitit - another (also popular) routing library. I haven't used this myself (the dynamic routing), but the example seems quite straight-forward. There's also a comparison with compojure:

x3qt20:09:36

I started clojure just a few days ago, trying to learn by doing a something simple like todomvc

x3qt20:09:49

Thank you for the proposal, maybe I’ll try it later

dharrigan20:09:30

Sure! It's lots of fun. You'll learn lots! 🙂 I put together a simple example of reitit, some db tech and a few other bits and bobs here: https://git.sr.ht/~dharrigan/startrek/tree. It's a very small project. There's also a startrek-ui which is a nice javascripy frontend that also uses reitit and cljs.

dharrigan20:09:03

(scroll a bit down to dynamic routing)

x3qt20:09:09

I’ve tried to use resolve, but I am getting null ptr exception

(GET "/" [] ((resolve (symbol (str resource "/index")))))

hiredman20:09:57

I would really recommend spending some time with some clojure tutorial (not sure what would be best)

x3qt20:09:27

I’ve done a few chapters from the brave clojure

x3qt20:09:40

It’s not much, but gave me something %)

x3qt20:09:09

So, is there a way to make it work?

(GET "/" [] ((resolve (symbol (str resource "/index")))))

hiredman20:09:15

have you read the docstring for symbol?

x3qt20:09:39

Do you have a link?

hiredman20:09:48

do you know how to get doc strings in the repl?

hiredman20:09:03

(doc symbol)

x3qt20:09:10

Yeah, it is

x3qt20:09:14

Thank you for reminder

x3qt20:09:58

user=> ((resolve (symbol "+")) 1 2)
3

x3qt20:09:09

I just can’t get why example above is working and my is not

hiredman20:09:45

what arguments can the function symbol take according to its docstring?

x3qt20:09:24

Returns a Symbol with the given namespace and name. Arity-1 works on strings, keywords, and vars.

x3qt20:09:48

Is not “users/index” a string too?

x3qt20:09:57

Because now I see the following

;; Works
    (GET "/" [] (users/index))
    ;; Not works
    (GET "/" [] ((resolve (symbol (str resource "/index")))))

x3qt20:09:49

(rest-resource "users")

x3qt20:09:56

resource is a string

x3qt20:09:04

/index is a string

x3qt20:09:16

But after coercion it is not converted to symbol

hiredman20:09:29

the key thing you are missing here is the significance of / in a symbol

hiredman20:09:48

it absolute is converted to a symbol

hiredman20:09:57

just not the symbol you want

x3qt20:09:09

How can I get the symbol I want?

x3qt20:09:19

Ie get it dynamically from the name

hiredman20:09:34

sorry I forget, symbol actually was extended at some point to make that work

hiredman20:09:46

so what I thought was the issue isn't

hiredman20:09:00

which means the issue is you aren't loading the code

hiredman20:09:39

e.g. for resolving to actually look something up the definition has to have been loaded

x3qt20:09:22

I am not sure I understand

x3qt20:09:43

How can’t it be loaded in such case?

x3qt20:09:52

If the first example works

hiredman20:09:27

when you print out resource, what does it print when it doesn't work?

hiredman20:09:30

are you seeing stack traces when it doesn't work?

x3qt20:09:15

Resource was not a string at this point

x3qt20:09:30

(GET "/" [] ((resolve (symbol (str (str resource) "/index")))))

x3qt20:09:32

Now it works

x3qt20:09:37

Thank you very much

x3qt20:09:31

It is strange a bit, because I am passing it to a method as a string

(rest-resource "users")

x3qt20:09:01

Maybe compoujure somehow converted it

x3qt20:09:19

Can you explain a bit more please?

x3qt20:09:28

Is not a string was passed to a function?

hiredman20:09:23

it could get screwed up in a lot of different wants, hard to say without more information

hiredman20:09:22

my guess would be you some how ended up in a mishmash state of the world after I suggested switching from a macro to a function

hiredman20:09:44

something was still quoted, or something wasn't evaluated right

hiredman20:09:04

so a symbol was being passed in instead of whatever the symbol resolved to

hiredman20:09:13

(str (str resource) "/index") is, by the definition of the str function identical to (str resource "/index")

hiredman20:09:24

so changing one for the other is not what fixed things

hiredman20:09:56

but in changing one for the other you had to re-load the code which is likely what fixed things

x3qt20:09:56

You are right, now it’s broken again

x3qt20:09:06

Code reloading works a bit strange

hiredman20:09:20

I would suggest starting a brand new repl

hiredman20:09:32

that will start in you in a clean known state

hiredman20:09:08

it is very possible to end up in a state where things are broken in a clean known state but work after reloading

hiredman20:09:25

that is usually the result of having circular dependencies / definitions

hiredman20:09:13

a very common way people get themselves in a very broken place is by forgetting that the repl accumulates state. each definition you send, you have to assume it depends on the state of the repl. which is a very different kind of dev cycle if you are used to running things from scratch everytime. there are some tools that try and do things to reset the repl to a known state, but it limited what they can do

x3qt20:09:21

So it seems to be running ok in repl but not on the server, even after restart

x3qt20:09:04

¯\(ツ)

hiredman20:09:23

that isn't how resolve works

hiredman20:09:47

so you have some other code somewhere (maybe a plugin or something) that is screwing with you

hiredman20:09:54

resolve never returns a string

x3qt20:09:10

Resolve retuned a function

x3qt20:09:18

After a call it is returned a string

x3qt20:09:31

Mind the braces

hiredman20:09:57

resolve returned a var

hiredman20:09:04

#' is var quote

hiredman20:09:46

what you say it runs ok in the repl but not on the server, what is the server?

x3qt20:09:28

I am just running lein ring server-headless

hiredman20:09:38

meaning, what is are the differences in context between those two things

x3qt20:09:04

Repl is lauched by cider

x3qt20:09:07

And server from cli

hiredman20:09:31

how does the server know to call you code?

x3qt20:09:12

I am created a project with lein new compojure arborist

x3qt20:09:27

I thought it knows how to load itself

x3qt20:09:37

With lein ring server-headless

x3qt20:09:33

I’ll try to defn something in the same scope and try to call it with resolve

hiredman20:09:23

are you sure the server is calling your code at all? visiting '/' on the server gets what you'd expect?

hiredman20:09:58

lein ring has a bad habit of forcing aot compilation, so you might try running lein clean in case something is stale

x3qt20:09:19

Yeah, it works correctly without resolve

hiredman20:09:22

you should get a stacktrace if your code is actually being hit and the resolve is failing