Fork me on GitHub
#clojure
<
2019-07-02
>
gordon00:07:03

@jeff.terrell probably not, though I ran into this when I was calling map from a macro which was making use of a helper function to create gensyms, and it's possible that the resulting code would have been problematic. I write macros so very rarely...

jeff.terrell00:07:24

Makes sense. Yeah I think I can see how that could be a problem in some cases. BTW in case it's helpful: https://jeffterrell.tech/posts/2016-04-29-macros/

jeff.terrell00:07:07

Also, it occurs to me that a rule of thumb about not using an auto-gensym'd symbol outside of its enclosing backtick would avoid problems—at least the problems I can think of.

gordon01:07:44

I'm very closely following your tip #1 👍

😆 4
gordon01:07:30

in this case, I'm creating a drop-in replacement for an existing defn-like as a compatibility shim that wraps the body/arities in some tracing code, so a macro is unavoidable

👍 4
theeternalpulse05:07:52

Anyone use a clojure based migration tool, or do you just follow simple conventions?

seancorfield05:07:43

@theeternalpulse We basically built our own Clojure based tool to replace an earlier script&ant based version.

theeternalpulse05:07:31

I saw one just called "migrations" on github

seancorfield05:07:41

I think if we'd been starting that piece of the project after we had adopted Clojure, we'd probably have used one of the existing libraries.

theeternalpulse05:07:19

at least I have a few choices to pick from

theeternalpulse05:07:11

I think these are all good enough. thanks for the options

Ahmed Hassan05:07:50

I don't understand what are trade-offs of selecting one of these.

theeternalpulse05:07:35

ragtime seems to allow you to specify the commands within edn itself, instead of sql files, which is nice

seancorfield05:07:01

Yeah, that seems like a nice approach. We've ended up with SQL migrations and code migrations run separately (and manually coordinated). Migratus would have allowed us to integrate that. It also tries to solve the distributed development issue of migrations being added on multiple branches.

seancorfield05:07:37

It's like our homegrown system -- and that does sometimes require careful coordination and renaming 😐

seancorfield05:07:02

If we were starting over, I think we'd pick Migratus.

✔️ 4
deleted05:07:30

it reminds me a lot of rails migrations, except I've always thought those should be written as sql

Ahmed Hassan05:07:55

Miagratus of Ragtime?

theeternalpulse06:07:46

migratus does seem to have completeness over simplicity

seancorfield06:07:55

Today we have about 800 migrations and our system supports dev-only as well as all-tier migrations so we can automate testing data setup as well as general schema changes etc. We've never needed down migrations tho', just up.

theeternalpulse06:07:21

my goal is to coordinate some docker containers with compose and up one db container with the migrations

theeternalpulse06:07:57

I'm still learning that whole thing so i'm slow to start my project, but it's always something I wanted to learn

theeternalpulse06:07:18

the whole deploy coordination stack

deleted06:07:27

I've been using ruby forever and even on non-ruby projects we use capistrano since it's so nice

Ahmed Hassan06:07:28

Do you use it from Clojure?

pinkfrog07:07:39

there are so many cider-jack-in* commands

pinkfrog07:07:59

what are the difference?

sogaiu07:07:41

don't know, but perhaps the #cider channel might be of some use?

Ahmed Hassan07:07:36

What approach do you feel is better between Mount and Integrant/Duct?

Ahmed Hassan08:07:48

I'm curious about trade-offs of both approaches.

mloughlin08:07:43

I recommend you write a small webserver app with all 3, Mount, Integrant and Duct

mloughlin08:07:19

it really doesn't take long, but it should illustrate the differences

jaihindhreddy08:07:15

Duct is a way to structure web apps with Integrant being one of its opinions.

Ahmed Hassan08:07:50

@mloayzag I've wrote web app with Mount and Pedestal, now I'm reading Duct/Integrant codebase.

Ahmed Hassan08:07:03

I like Mount approach better.

Ahmed Hassan08:07:08

@jaihindh.reddy I've seen Duct only with Integrant. What do you mean by it's opinion?

jaihindhreddy08:07:11

Duct is a set of opinions on building a web app (much like Luminus), and one of the choices it made is Integrant

jaihindhreddy08:07:18

Integrant and Component require you to treat namespaces as purely code organisation constructs (which is what they're meant for), and indicate application state explicitly by implementing the protocol(s) and multimethods respectively.

jaihindhreddy08:07:42

Whereas Mount uses nses for both code organisation and state mgmt.

jaihindhreddy08:07:45

I found Integrant easiest to understand and the most data-oriented. Haven't built an app with Mount so my opinion is just that, an opinion.

👍 4
Ahmed Hassan08:07:12

Mount is much less opinionated than Integrant.

👍 8
jaihindhreddy08:07:33

Agreed. I was just saying the opinions of Integrant made sense to me.

Ahmed Hassan08:07:31

>Keys in a Duct configuration are expected to initiate into functions that transform a configuration map. There are two broad types: profiles, which merge their value into the configuration, and modules, which provide more complex manipulation. https://github.com/duct-framework/duct#structure What is difference between Profiles and Modules concepts in Duct?

jaihindhreddy09:07:07

Modules are explained here: https://github.com/duct-framework/core#modules Haven't used Duct so can't speak to what profiles are. There were some design discussions about having something less expressive than modules, so maybe profiles are the result of that. I'm sure you'll get more help in #duct

mloughlin09:07:50

if anything Mount is more opinionated, it forces you to use the namespace system for its own purposes

mloughlin09:07:24

Integrant is a config map and init functions 🤷

💯 16
fdserr09:07:46

Hi there. What's the tools.deps command equivalent to lein deps? (just resolve and download project dependencies, do not start a REPL, do not start the app).

mgrbyte11:07:00

I use olical/depot after watching @U04V70XH6’s videos on his dotclojure setup https://github.com/seancorfield/dot-clojure Can't find the link to the video now, but that's the repo ☝️ You end up just doing: clj -A:outdated

🙏 4
Olical12:07:10

If it's just to download ahead of time I think people use things like clojure -Spath > /dev/null or similar. Just do some small operation and it'll sync all of your dependencies to do so.

seancorfield13:07:46

Note that, just like you occasionally needed lein clean if your dependencies got stale / messed up, with deps.edn you very occasionally need rm -rf .cpcache to remove cached deps/paths. You only really need lein deps if you're working with containers (to create a cache of dependencies) and the same is true with deps.edn -- clj -Spath > /dev/null is really only needed with containers.

seancorfield13:07:25

(And @U08715BSS depot is great but not relevant to this question)

mgrbyte13:07:16

Sorry, misread the question (!) 🙈

fdserr14:07:01

Thanks guys 🙏 went for my old clojure -e "(System/exit 0)", but admittedly clojure -Spath > /dev/null is a smart upgrade :spock-hand:

reefersleep10:07:25

Is there a recommended way to serialize a Clojure data structure to a string in a way that ensures that it can be deserialized properly at a later point? We’ve tried with prn-str and clojure.edn/read-string, but encountered a problem where the printing would include a tagged literal representing an object, which could not be read by read-string at a later point. I wonder if there’s a standard way to print Clojure data structures that would prevent such things from happening.

Alex Miller (Clojure team)12:07:07

There are several ways to tell the reader what to do if it encounters an unknown tagged literal. Supporting this was an explicit design goal of tagged literals. While it’s good to ask questions about the printing side, also don’t be so quick to give up on the reading.

flefik10:07:59

what is the problem you are seeing?

Dan Nicolici11:07:52

hi. guys, lein doesn't seem to run my test.check test...

lein test

lein test playground.core-test

Ran 0 tests containing 0 assertions.
0 failures, 0 errors.
(ns playground.core-test
 (:require [clojure.test.check :as tc]
           [clojure.test.check.generators :as gen]
           [clojure.test.check.properties :as prop]))

(def sort-idempotent-prop
  (prop/for-all [v (gen/vector gen/small-integer)]
                (= (sort v) (sort (sort v)))))

(tc/quick-check 100 sort-idempotent-prop)

Dan Nicolici11:07:01

what am I missing?

aisamu12:07:14

run-tests can't find sort-idempotent-prop because it doesn't have :test metadata (which deftest, defspec add for you)

Dan Nicolici11:07:43

same with repl...

Dan Nicolici11:07:46

playground.core=> (clojure.test/run-tests)

Testing playground.core

Ran 0 tests containing 0 assertions.
0 failures, 0 errors.
{:test 0, :pass 0, :fail 0, :error 0, :type :summary}

Dan Nicolici12:07:29

ok, I'll try that, but lein test should work... weird... any clue there?

Dan Nicolici12:07:30

...or maybe I got this wrong...

aisamu12:07:56

> lein test should work... Why? I'd expect it to operate in a similar fashion, given its tight integration with clojure.test

Dan Nicolici12:07:02

tc/quick-check won't be run by the clojure test runner... is this it?

aisamu12:07:10

IIRC, defspec wraps the test with a call to quick-check, parses the result and reports accordingly

Dan Nicolici12:07:57

I understood the problem now

Dan Nicolici12:07:09

you helped me make it work

simple_smile 4
Dan Nicolici12:07:16

have a great day!

👋 4
otwieracz12:07:11

I have quite complicated (I believe) data transformation task to perform:

aisamu12:07:14
replied to a thread:what am I missing?

run-tests can't find sort-idempotent-prop because it doesn't have :test metadata (which deftest, defspec add for you)

otwieracz12:07:17

{:swagger {:post {:tags                ["resources"]
                    :summary             "Define new resource"
                    ::swagger/parameters {:body ::resource-definition}
                    ::swagger/responses  {201 {:description "Resource created"}
                                          400 {:generate-handler :handle-not-found
                                               :description "Malformed resource definition"}}}
             :get {:tags                ["resources"]
                   :summary             "Define new resource"
                   ::swagger/parameters {:body ::resource-definition}
                   ::swagger/responses  {201 {:description "Resource created"}
                                         400 {:generate-handler :handle-not-found
                                              :description "GET Malformed resource definition"}}}}}
  ;; transform into
  {201 {:post {:description "Resource created"}
        :get {:description "Resource created"}}
   400 {:post {:description "Resource created"}
        :get {:generate-handler :handle-not-found
              :description "GET Malformed resource definition"}}}

otwieracz12:07:41

using standard clojure reduces it doesn seem very readable and maintainable.

otwieracz12:07:08

I thought about using spectre, but I know nothing about it - and it requires quite amount of time invested into learning.

otwieracz12:07:39

Is there anyone familiar with specter, or other similiar utilities, to tell me if this will be doable and I should learn how to do this?

aisamu12:07:58

There's also meander and tracks!

carkh12:07:30

or just dump it all in a datascript database and pull what you need from it (if performance is no issue)

💯 4
carkh12:07:13

with a well defined schema, the actual dumping is a single line of code, normalization happens automatically

otwieracz12:07:20

It does not sound right for very simple library… and this code will be executed inside macro, at compilation time

carkh12:07:43

alright then, there are ways to execute pull syntax directly on your data structure, there was a gist about that somewhere

carkh12:07:50

and that's quite small

tavistock12:07:51

Its not so bad as a map, merge:

(defn reorder [resource]
  (->>
   (:swagger resource)
   (mapcat (fn [[k v]] (map (fn [[rk rv]] {rk {k rv}}) (::swagger/responses v))))
   (apply merge-with merge)))

tavistock12:07:21

the function in the mapcat is the isolated transform, you might be able to make it less weird, but I believe even in a dsl (like spectre) it will be weird and you will need to learn a dsl

jsa-aerial14:07:15

I'm not sure what you are trying to do here, but if the basic task/goal is to transform or query a heavily nested map (or a multi-type nested datastructure) then Specter is definitely what you want to reach for. Check out #specter

jsa-aerial14:07:12

Also, specter does not require a lot of investment to get various tasks working just fine. If you want to understand the whole thing, yeah, that will take some time and effort, but you don't need to do anything like that for most tasks. Things that look really hard/messy in plain Clojure are often trivial in specter.

otwieracz15:07:40

Yeah, I managed to do this in specter.

otwieracz15:07:51

(in some way)

otwieracz15:07:00

But thank you for all the answers!

jaihindhreddy18:07:08

With a helper called compress, assuming your input is x, this is what I came up with. Doesn't seem too bad:

(defn compress
  "compresses a seq of paths into a map"
  [paths]
  (reduce #(assoc-in % (butlast %2) (last %2)) {} paths))

(compress (for [[method desc] (:swagger x)
                [status status-desc] (::swagger/responses desc)]
            [status method status-desc]))

gordon17:07:53

anyone know why the multi-arity version of defn allows an attr-map at the end? are there historical reasons for this? it's apparently merged with the attr-map toward the beginning if it's supplied From the defn docstring: >name doc-string? attr-map? ([params*] prepost-map? body) + attr-map?

Alex Miller (Clojure team)17:07:06

I asked Rich this when I discovered it while writing the spec

Alex Miller (Clojure team)17:07:41

the idea was that the meta could potentially get large (this pre-dated some of the modern meta and defn syntax) so the idea was to put the meat of the function at the beginning and push the meta to the end, syntactically

👍 8
Alex Miller (Clojure team)17:07:05

I have only ever seen this used once or twice (I found those cases when writing the spec :)

seancorfield17:07:37

If you have an attr-map in both places, are they merged?

seancorfield17:07:58

(I guess I could easily test that in the REPL but figured you might answer faster 🙂 )

seancorfield17:07:36

Answer: yes.

👍 4
seancorfield17:07:07

user=> (defn ^:foo foo {:bar 13} ([x] (* x x)) {:quux 42})
#'user/foo
user=> (meta #'foo)
{:bar 13, :ns #object[clojure.lang.Namespace 0x4fed80ad "user"], :name foo, :file "NO_SOURCE_PATH", :column 1, :line 5, :foo true, :quux 42, :arglists ([x])}
user=> 
Weird 🙂

gordon17:07:35

Thanks, Alex!

eggsyntax18:07:22

> the idea was to put the meat of the function at the beginning and push the meta to the end, syntactically If that was the motivation, why keep the attr-map at the beginning as well? For backward compatibility?

noisesmith18:07:37

I'd assume so, RH has strong opinions about compatibility

💯 4
👍 4
mikekap22:07:10

Hey folks, how do we sign up for jira? I tried creating an account, but all I get is “Your email address <mailto:[email protected]|[email protected]> doesn’t have access to http://clojure.atlassian.net

Alex Miller (Clojure team)22:07:20

if you are looking just to vote or comment tickets, I'd ask that you wait as we are working on something new for that. if you're looking to add a new ticket, you can do that at https://clojure.atlassian.net/servicedesk/customer/portal/1 without an account. if you're looking to do work on tickets, provide patches, etc, you should fill out the CA at https://clojure.org/dev/contributor_agreement and then request an account at the same portal

✔️ 8
mikekap22:07:58

got it - i got to the jira through https://clojure.org/dev/creating_tickets which doesn’t mention the service desk piece - thanks!

Trevor23:07:03

Does this exception make sense to folks?

java.lang.ClassCastException: com.badlogic.gdx.physics.bullet.collision.SWIGTYPE_p_p_btCollisionObject cannot be cast to com.badlogic.gdx.physics.bullet.collision.btCollisionObject
Is the SWIGTYPE version of btCollisionObject something clojure came up with that makes it so it can't compare itself against the java version of the class?

hiredman23:07:53

swig is a tool for generating java bindings to c code

hiredman23:07:37

my first guess would be the SWIGTYPE class is the class generated by the swig tool, and the non-swig type is a wrapper class to make it easier to use

hiredman23:07:25

you can even find generated java docs for the swigtype if you google it (the docs are pretty empty)

Trevor23:07:26

Ah okay, so it's very possible the wrong kind of object is being instantiated to be compared against?

Trevor23:07:46

This is happening in a when block, so perhaps a special case for the SWIG?

hiredman23:07:11

you are trying to do something with the swig type that you can only do with the non-swig type

hiredman23:07:07

the type error is exactly that, the jvm saying this type you are trying to pass to some method doesn't match the signature of the method

hiredman23:07:21

the rest of the stacktrace will tell you which method

Trevor23:07:27

Sure, here's the block of code where the exception is being thrown:

(when entities
    (doseq [^btCollisionObject body (get-bodies screen)]
      (when-not (some #(= body (-> % :body :object)) entities)
        (cond
          (instance? btRigidBody body)
          (bullet-3d! screen :remove-rigid-body body)
          (instance? btSoftBody body)
          (bullet-3d! screen :remove-soft-body body)   ;; <== This line
)
        (.dispose body))))

hiredman23:07:58

the type hint

hiredman23:07:10

and the call to dispose

hiredman23:07:25

get-bodies is returning the swig type

hiredman23:07:02

you are type hinting it as the wrong type, so when you call the dispose method it generates the cast, which throws the exception

Trevor23:07:14

So the exception says it's thrown at this line: (bullet-3d! screen :remove-soft-body body) But so if I understand correctly that means the bullet-3d! is not able to handle the SWIGTYPE?

hiredman23:07:18

there also could be something inside bullet-3d!

Trevor23:07:44

Yeah, here's what we got there:

(defmacro bullet-3d!
  "Calls a single method on a `bullet-3d`."
  [screen k & options]
  `(let [^btDynamicsWorld object# (:object (u/get-obj ~screen :world))]
     (u/call! object# ~k ~@options)))

hiredman23:07:46

likely you are misreading the exception

Trevor23:07:23

Ah! Let me show you the rest of the exception, that'll be helpful I'm sure:

Exception in thread "LWJGL Application" java.lang.ClassCastException: com.badlogic.gdx.physics.bullet.collision.SWIGTYPE_p_p_btCollisionObject cannot be cast to com.badlogic.gdx.physics.bullet.collision.btCollisionObject
	at play_clj.g3d_physics$fn__1414.invokeStatic(g3d_physics.clj:311)
	at play_clj.g3d_physics$fn__1414.doInvoke(g3d_physics.clj:289)
	at clojure.lang.RestFn.invoke(RestFn.java:423)
	at clojure.lang.MultiFn.invoke(MultiFn.java:234)

hiredman23:07:52

ah, I didn't realize bullet-3d! is a macro

hiredman23:07:03

you have a SWIGTYPE_p_p_btCollisionObject, and are trying to tell the jvm that is a btCollisionObject (it is not) and you are trying to call methods that type doesn't have

hiredman23:07:46

call! is likely also a macro, this code is likely all a mess

Trevor23:07:12

lol, yeah I"m seeing if I can update the play-clj library which wraps libGDX

Trevor23:07:21

all the example games except this one work.

Trevor23:07:52

Yup call is a macro that appears to just wrapping calling things on a java object.

Trevor23:07:41

But ultimately I'm guessing one of the issues is we're instantiating a SWIGTYPE where we should be using the other non-SWIGTYPE.

Trevor23:07:56

Ah! yup it's definitely cause something broke in the new libgdx versions. Original code works fine before my upgrades