clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
hipster coder 2021-04-15T00:24:53.056700Z

I also found @bruno.bonacci repo. it adds a bin/sh wrapper to the jar... it also works... but it would be nice to use @borkdude interpreter... so I can start using Clojure for a lot of my devops scripts.

hipster coder 2021-04-15T00:26:12.057900Z

yuhan 2021-04-15T00:26:49.058Z

Generally if i have abstractions X and Y defined in separate namespaces, where should the function X->Y belong? ie. does X require Y and define to-Y , or does Y require X and define from-X

hipster coder 2021-04-15T00:31:42.058600Z

@qythium where can I find the namespace docs on defining to-Y and from-X?

hipster coder 2021-04-15T00:32:38.059Z

or, is that a convention? not a built in function

yuhan 2021-04-15T00:34:07.059300Z

oh just a convention

hipster coder 2021-04-15T00:34:52.060400Z

it's an interesting question. you are asking how to setup the dependency... compose the 2 functions

hipster coder 2021-04-15T00:35:29.061500Z

my first thought is, how to gain maximum flexibility... so x->y or y->x

yuhan 2021-04-15T00:35:43.061900Z

there's conceptually only one function from X to Y, I can't define it in both namespaces otherwise it becomes a circular dependency

hipster coder 2021-04-15T00:35:45.062200Z

so you can call them either way

yuhan 2021-04-15T00:35:52.062400Z

assume the transformation is one-way only

hipster coder 2021-04-15T00:36:00.062800Z

ahhh ok

hipster coder 2021-04-15T00:36:10.063100Z

thinking...

hipster coder 2021-04-15T00:36:49.064Z

still, the 1 way transformation isn't the actual dependency

hipster coder 2021-04-15T00:37:01.064400Z

for example, let's say this

hipster coder 2021-04-15T00:37:14.064900Z

x multplies by 2, y adds by 3

yuhan 2021-04-15T00:37:14.065Z

apple.clj define to-juice vs. juice.clj define from-apples

yuhan 2021-04-15T00:37:51.066Z

now I think about it, probably juice.clj defines Juicable protocol and apple.clj extends it

hipster coder 2021-04-15T00:39:01.066400Z

ok... you could have apples, oranges and pears... but 1 juice

2021-04-15T00:39:46.066700Z

I usually have a single namespace where all defprotocols live

➕ 1
hipster coder 2021-04-15T00:39:47.066800Z

I would use "from"

hipster coder 2021-04-15T00:39:52.067100Z

it makes your code more extendable

hipster coder 2021-04-15T00:41:05.067500Z

I think of it likes this...

hipster coder 2021-04-15T00:41:15.067900Z

juice.from(any object)

hipster coder 2021-04-15T00:41:34.068500Z

versus... explicit http://object.to(juice)

hipster coder 2021-04-15T00:42:00.069200Z

then, you can define how th juicing is done, on the object

hipster coder 2021-04-15T00:42:16.069600Z

it's losely coupled

yuhan 2021-04-15T00:43:04.070300Z

What it the situation is the other way and you have a single X that needs to be transformed into multiple different "output format"s

hipster coder 2021-04-15T00:43:33.070700Z

like apples to juice, smashed, chredded ?

2021-04-15T00:43:36.070800Z

Once you start mixing code written against a protocol with the definition you have a pretty good chance of introducing cyclic dependencies between namespaces

hipster coder 2021-04-15T00:45:06.072300Z

@hiredman do you mean, there's nothing enforcing the 1 way transformation... so the two could call each other, circular ?

hipster coder 2021-04-15T00:45:26.072700Z

does protocal mean... interface

✔️ 1
yuhan 2021-04-15T00:45:50.073Z

Yeah, that's the issue I'm facing now, refactoring a gigantic namespace into different parts and realising there were all these implicit cyclic dependencies

hipster coder 2021-04-15T00:46:07.073400Z

the protocal is the 1 way transformation? the enforced rule

hipster coder 2021-04-15T00:46:29.073700Z

that apples must go to juice... juice can't go to apples

2021-04-15T00:48:11.073800Z

I assume the juice thing is a for example and not an exacting specification

hipster coder 2021-04-15T00:49:29.074Z

easier, than x or y

hipster coder 2021-04-15T00:49:46.074400Z

I still think the juice.from(objectType) is better

hipster coder 2021-04-15T00:50:14.074900Z

is there some design pattern for this? protocals? interfaces?

2021-04-15T00:50:34.075100Z

Protocols are not java interfaces though

2021-04-15T00:50:48.075400Z

They are functions that live in a namespace

2021-04-15T00:50:57.075600Z

Not methods that live on an object

hipster coder 2021-04-15T00:51:24.076400Z

k, and, they are the outward facing functions that need to be avalable?

hipster coder 2021-04-15T00:51:42.076900Z

like... apple would need a "protocal" function named "juice" ?

2021-04-15T00:51:46.077Z

To invoke the protocol functions you have to depend on the namespace where they are defined

yuhan 2021-04-15T00:52:02.077300Z

Yeah the apple juice was just an example, my actual domain is related to compilers

yuhan 2021-04-15T00:53:12.077900Z

but also I'm interested in general architectural patterns separate from domains

hipster coder 2021-04-15T00:55:26.079700Z

ahhh, I see

2021-04-15T00:55:26.079800Z

The protocols are all defined in one file, which only has protocol definitions, which keeps it separate from implementations, which avoids circular dependencies

hipster coder 2021-04-15T00:55:39.080100Z

this is a good idea...

2021-04-15T00:55:49.080400Z

The same pattern and issues applies to defmulti based interfaces as well

hipster coder 2021-04-15T00:55:53.080600Z

it's an extra layer, between the object and behavior

hipster coder 2021-04-15T00:56:14.081Z

yes, defmulti, polymorphics, based on types

hipster coder 2021-04-15T00:57:58.081400Z

@qythium I think @hiredman is right... that's the best way

hipster coder 2021-04-15T01:02:29.082300Z

well, that was very intesting... it's like the good parts of inheritance... interfaces... but for functional paradigm

hipster coder 2021-04-15T01:02:49.082900Z

Golang took the good parts too. replace inheritance based polymoprhism with interfaces

yuhan 2021-04-15T01:05:58.084200Z

let me try and extend my example:

(ns fruit.protocols)

(defprotocol FruitConvert
  (make-juice [fruit])
  (make-tart [fruit]))
(ns fruit.juice)

(defrecord Juice [color])
Won't it still have to declare a dependency on the output type?
(ns fruit.apple
  (:require [fruit.protocols :as p]
            [fruit.juice :as j]))

(defrecord Apple [name]
  p/FruitConvert
  (make-juice [_]
    (j/->Juice "red")))

hipster coder 2021-04-15T01:10:40.085200Z

@qythium can you push a small demo of that code to your github?

yuhan 2021-04-15T01:12:52.085700Z

Maybe I'm still thinking of this wrong - trying to understand the core.async code

hipster coder 2021-04-15T01:13:27.086200Z

I'd like to actually see how protocals makes the code maintainable... when you add in more types

hipster coder 2021-04-15T01:13:57.086700Z

I was just studying this stuff, yesterday. functional style, types, monads

yuhan 2021-04-15T01:15:15.087500Z

I think the Expression Problem refers to something like this

hipster coder 2021-04-15T01:21:54.088200Z

@qythium I am reading it... my first thought is... following domain driven design... Juicing is the most domain specific part of this problem

hipster coder 2021-04-15T01:23:11.088600Z

this is important.... because Juicing is the dependency... on the outside world

hipster coder 2021-04-15T01:23:51.089200Z

and, in the real world, when I juice veggies, fruits... I use the same behavior. I use my OmegaVert juicer

hipster coder 2021-04-15T01:24:15.089500Z

everything else should be deterministic

hipster coder 2021-04-15T01:25:00.089900Z

that's why I like the juice.from(apple) approach

hipster coder 2021-04-15T01:26:25.090700Z

but a protocal is used, to tie 2 APIs together... so juice is the protocal... and each type is repsonsible for defining how they are juiced

hipster coder 2021-04-15T01:27:19.091100Z

I am going to code up something, now... you have my mind going

yuhan 2021-04-15T01:33:08.093100Z

Another annoying consideration is that my current data structures are maps with mostly qualified keys - if I have to turn them into defrecords to implement a protocol against, record fields can't be namespaced..

yuhan 2021-04-15T01:34:44.093800Z

Multimethods might make more sense there

2021-04-15T01:42:58.093900Z

You can consider :extend-via-metadata on your protocols to use them with maps https://clojure.org/reference/protocols#_extend_via_metadata

hipster coder 2021-04-15T01:47:35.094500Z

I am trying to figure out... is the main advantage of the protocal... that you can use namespaces?

hipster coder 2021-04-15T01:48:05.095100Z

opposed to... "AD HOC" polymorphic with multi methods.

yuhan 2021-04-15T01:52:27.096600Z

Protocols implement ad-hoc polymorphism, because the functions dispatch only on the type of the first arg. Multimethods have more flexibility

yuhan 2021-04-15T01:54:26.098100Z

I never quite trusted metadata 😅 You have to take extra care everywhere to not accidenatally drop it

2021-04-15T02:11:50.098300Z

I'd put it all into one namespace and call it x->y

2021-04-15T02:12:14.098500Z

Otherwise it weirdly feels like you're using namespaces like classes, which is wrong

yuhan 2021-04-15T02:21:04.099500Z

That's actually the current situation in the code I'm trying to refactor- the issue is that Y is conceptually in a different domain, and there will be other implementations such that x->w, x->z

2021-04-15T02:32:22.099700Z

Hum... What I normally do is define my entire domain in a single namespace I call domain.clj. What I put in there is only the data schemas, so it would be data specs or records. Then the rest of my application would be defining operations over this domain data. If I want conversions between my entities for example, I could define a conversion.clj namespace. Now honestly, I wouldn't use protocols for this, I'd just have normal functions called X->Y, X->Z, Y->Z, etc. Its much clearer to me that way. Do you have a reason to want a protocol? Like do you need to have a generic conversion where you don't know what you are getting out of multiple possible type and need it converted to a Z ? That would be the question I think that can answer your question. If you want "one of many possible types"->Z then I'd just create a protocol with a ->Z definition or a make-z-from kind of thing. I'd put this protocol in the conversion.clj namespace and I'd extend all the types to implement it in that namespace as well, but the records would all be in the same domain.clj namespace.

💯 1
2021-04-15T02:34:42.100Z

If you needed the other way, like a Z->"one of many possible types", then I'd have another protocol which defines a to-z function.

2021-04-15T02:35:14.100200Z

And if you needed it both ways, like a "one of many types"->"one-of-many-types", then I'd make it a multi-method.

2021-04-15T02:37:34.100400Z

So what happens is the namespaces just group conceptually similar kind of functionality. They don't group all functionality related to a particular record. The latter would be what OO does, OO will say, ok group all functions that operates on a given type together. This just seems wrong to me in Clojure. So I don't put convertions from/to Z in the Z namespace. Instead I put all conversions from any entity to any other in a conversion namespace.

💯 1
yuhan 2021-04-15T02:56:25.101500Z

Thanks! I think I've seen the "Namespaces group functionality, not abstractions" advice elsewhere, but this made it finally click 🙂

🎉 1
hipster coder 2021-04-15T05:46:22.103400Z

when I am running tests... the most useful part of the error is at the top... then the stack trace... is there a way to reverse the stack trace... so I can see the error message at the bottom...

hipster coder 2021-04-15T05:47:16.103500Z

hipster coder 2021-04-15T05:47:33.103900Z

the actual line #, with error, is at the top, past the screen

hipster coder 2021-04-15T05:48:21.104Z

2021-04-15T15:40:37.127500Z

Not a fix to your problem, but I usually do is to search for ERROR or FAIL (in terminal, using cmd+f)

hipster coder 2021-04-15T05:48:47.104600Z

if I scroll up inside my tmux... I can see it... Are all of you using your monitors in vertical position?

seancorfield 2021-04-15T06:07:07.107300Z

I consider this a pretty bad usability problem with clojure.test but until it is spun out as a separate Contrib project, I don’t think it ranks very highly on the list of things to “fix”. There’s a solid argument against not hiding or munging the stacktrace but in terms of reporting errors to the user, it’s pretty awful output. It could take a leaf from the CLI’s book and write stacktraces to temporary EDN files by default and just report the message/cause to the screen (and have an option to report the full stacktrace to the screen, as now).

seancorfield 2021-04-15T06:07:34.107600Z

(I have a lot of feelings about clojure.test 🙂 )

hipster coder 2021-04-15T13:35:25.112Z

do you use the property testing library? I saw that one... looked interesting.

seancorfield 2021-04-15T14:20:51.119400Z

@chrisblom This one: https://github.com/clojure-expectations/clojure-test — I initially switched from clojure.test to the “classic” version of Expectations after seeing it presented at Clojure/West one year but the custom tooling/lack of broad support in tooling made it quite frustrating to use, so I created a clojure.test-compatible version of Expectations and we probably have about 80% of our tests based on the expect-style tests.

seancorfield 2021-04-15T14:21:35.119900Z

And, yes, we also use Clojure Spec for some tests — both to generate random, conforming example data and to do actual generative aka property-based tests.

2021-04-15T21:48:29.148800Z

I think you can hook to clojure.test reporting, and report wtv you want.

2021-04-15T21:48:54.149Z

I normally run my tests inside Cider, so this isn't a problem, since cider displays things in the editor

2021-04-15T21:49:16.149200Z

But I remember a library that gives you nice commmand line reporting for clojure.test

2021-04-15T21:52:04.149400Z

Found it: https://github.com/venantius/ultra

seancorfield 2021-04-15T22:45:58.150500Z

Yeah, humane-test-output does a pretty decent job of providing better diffs but I can’t remember how it handles exception reporting. Several IDEs also hook into clojure.test reporting and they are typically all incompatible (because they all override the same multimethod). See https://github.com/pjstadig/humane-test-output#ides

seancorfield 2021-04-15T22:46:57.150900Z

Ultra can be extremely problematic because it messes with so many things — I would never recommend Ultra to anyone who isn’t fully aware of how all this stuff hangs together.

seancorfield 2021-04-15T22:47:23.151100Z

(Ultra is probably the #1 plugin problem I’ve seen people trip over in #beginners)

2021-04-15T23:40:15.152100Z

I never used it, just remembered it.

hipster coder 2021-04-15T06:17:14.108600Z

@seancorfield let me see if I can pipe the test responses, reverse it, but I will need to play with the n number of lines

yuhan 2021-04-15T07:00:14.109900Z

are you using Evil? , should just be the comma key in normal state - there's a #cider or #emacs channel if you want to bring it there

hipster coder 2021-04-15T13:34:46.111800Z

I found it... they just mixed up the commands... it's t T

yuhan 2021-04-15T06:36:49.108900Z

If you use Emacs just run the test with Cider - it does all this filtering for you and more

hipster coder 2021-04-15T06:40:25.109100Z

I have cider running in Spacemacs... checking now for that ability

hipster coder 2021-04-15T06:43:48.109300Z

I am new to Spacemacs... not sure what the , comma key is

hipster coder 2021-04-15T06:44:58.109500Z

hipster coder 2021-04-15T06:45:02.109700Z

, T t

roklenarcic 2021-04-15T14:10:44.112900Z

Does AOT compiling the code affect alter-var-root ? I keep forgetting if there’s any interaction there.

borkdude 2021-04-15T14:11:36.113300Z

@roklenarcic Direct linking (`clojure.compiler.direct-linking=true`) will affect this.

borkdude 2021-04-15T14:11:54.113700Z

since many of the indirections will already be gone by the time you execute alter-var-root

borkdude 2021-04-15T14:12:22.114100Z

but AOT in general does not affect this since the indirection through the var will still be there

roklenarcic 2021-04-15T14:14:08.115100Z

direct linking doesn’t touch dynamic vars though, right?

alexmiller 2021-04-15T14:14:44.116100Z

right

borkdude 2021-04-15T14:14:51.116400Z

Direct linking only affects 1) non-dynamic vars or vars that aren't marked with ^:redef, 2) if those vars are used in call position, like (foo x y) but not if used like (apply foo x y)

alexmiller 2021-04-15T14:14:54.116500Z

or those marked redef

roklenarcic 2021-04-15T14:16:17.117300Z

Ok so for my caching library that means that those that want direct linking will have to set the cache when creating var, not later 🙂

roklenarcic 2021-04-15T14:16:18.117500Z

thanks

alexmiller 2021-04-15T14:18:11.117900Z

if you have a lib where something should not be direct linked, then that's what ^:redef is for

borkdude 2021-04-15T14:20:09.119Z

@roklenarcic direct linking only affects vars that are called directly as functions. if your cache value is created and passed around as an argument and not used as a function, direct linking won't affect that. but if f is some kind of caching function that should be altered, then yes

borkdude 2021-04-15T14:20:47.119300Z

An example of your API would be better here

roklenarcic 2021-04-15T14:23:30.121900Z

I think I already have both covered. You can write:

(memo #'a-var cache-config)
and it will wrap existing function at a-var into a cache and change the var root. Or you can write:
(memo (fn [] ...) cache-config) which just returns a cached version of the fn

mpemer 2021-04-15T15:38:58.127400Z

Greetings Clojurians. I managed to get myself confused over a syntactic detail today, and I am hoping that someone here might help. If you look at the enclosed screen shot, there is an implementation of the str function. Within that implementation there are some type hints in the signatures, for performance improvements. I am absolutely on board with the ^String hint, But what does the ^some hint do in the line (loop [^some xs xs] ... ??? I have spent some time searching for documentation that explains this particular annotation, but so far I have found no mention of it. I suppose it does not help that search engines seem to ignore the special character ^ . Either way, any information would be appreciated - thank you!

yuhan 2021-04-15T15:45:21.128Z

Did you write this yourself or get it from somewhere? I'm almost certain the ^some doesn't do anything meaningful

djblue 2021-04-15T15:47:54.128200Z

Looks like it's from https://twitter.com/cgrand/status/1382433410724081668

djblue 2021-04-15T15:48:06.128500Z

So it might be meaningful in dart

yuhan 2021-04-15T15:49:56.128700Z

whoa, never heard of ClojureDart before

yuhan 2021-04-15T15:50:34.128900Z

so it's not standard JVM clojure syntax in any case

alexmiller 2021-04-15T15:54:42.130300Z

that's not a JVM clojure thing, must be specific to the Dart stuff

mpemer 2021-04-15T15:55:26.130600Z

ah - ok well that helps

dpsutton 2021-04-15T15:55:36.130800Z

i wonder what it's intent is. My first though was it was annotating that as not-null to perhaps elide a null check but then there's a null check right after

mpemer 2021-04-15T15:55:58.131Z

yes, I took the screen shot from the above - I was reading something else, came across this code example, and got mentally stuck on the ^some part.

mpemer 2021-04-15T15:57:23.131200Z

if it isn't standard Clojure syntax, then I can stop obsessing over it. Still curious how it may be used in dart, but won't lose sleep over it. Thank you for confirming.

Bastien Rivière 2021-04-15T16:02:52.131400Z

It's explained in the thread: https://twitter.com/cgrand/status/1382580332420009986

👍 1
2021-04-15T16:03:50.131700Z

the tweet has the generate dart code, but the generated dart code doesn't look like it runs correctly (the loop unconditionally breaks?) so a work in progress

mpemer 2021-04-15T16:04:53.132200Z

Interesting - thank you!

2021-04-15T16:05:10.132500Z

I have a bit of an odd issue I can’t really pinpoint. I have an app that depends on slf4j-timbre (https://github.com/fzakaria/slf4j-timbre). When I uberjar this app, and start it, I get an exception:

Exception in thread "main" java.lang.NoSuchFieldError: __thunk__0__
(... stacktrace elided ...)
When I remove slf4j-timbre, all is well again and I can start my jar. Now, I found https://clojure.atlassian.net/browse/CLJ-1886, which has the same exception, and the same library but on a different version. The suggestion there is that it has to do with AOT compilation, but I don’t fully understand what I should watch out for in my app that could cause this issue.

2021-04-15T16:05:51.132900Z

Not the most sharp … problem description, sorry about that 😞

2021-04-15T16:06:17.133400Z

the problem is slf4j-timbre is aot compiled

2021-04-15T16:14:36.135600Z

Right, but I have other apps that also have slf4j-timbre, same version, in which the jars do start. So, my assumption is that there is something special about this particular app that doesn’t play nice with slf4j-timbre being AOT compiled. How could I (or should I?) diagnose what is causing issues in this particular app.

❤️ 1
2021-04-15T16:22:49.136500Z

it will be something like an overlap of depedencies between the rest of your app and sl4j-timbre

2021-04-15T16:27:46.137300Z

I’ll take a look whether I can find some misaligned deps then, thanks

2021-04-15T18:53:41.144100Z

Hi all, let's say I'm working in my REPL namespace, and have some other namespace required and aliased. Now that other namespace gets reloaded. It appears that this leaves my REPL aliases referring to stale namespace objects, none of the new interns that I expect to be in the reloaded namespace resolve. However, if I use the the fully qualified namespace prefix, I see those new interns as expected. Is this correct and expected behaviour? Does maintaining REPL aliases require some kind of specific intervention?

seancorfield 2021-04-15T19:44:33.145500Z

@zalky can you be a bit more specific about exactly what you’re doing and what behavior you’re seeing? This doesn’t sound like how things work for me — I have REPLs running for weeks without running into that sort of thing…

2021-04-15T19:56:37.145800Z

@seancorfield, I think this might be one of those c.t.n. "hoops" you alluded to in our previous thread. 😛 My best understanding from looking at the c.t.n. code is that reloading changed namespace and their dependents results in a new set of namespaces objects. For those namespaces that are in source, this is fine because the dependency graph keeps everything consistent. However, I don't think c.t.n. touches the REPL namespace, and so any aliases in the REPL namespace will continue to refer to old namespace objects. If you reference the namespace fully qualified without an alias, you'll get the new one. At least that's what I think is happening.

seancorfield 2021-04-15T20:02:32.146Z

Yup, this is one of those “don’t use a ‘reloaded’ workflow” things. It breaks in mysterious ways.

nilern 2021-04-15T20:14:54.146200Z

It is mentioned in https://github.com/clojure/tools.namespace#warnings-for-aliases

👍 1
2021-04-15T20:16:21.146600Z

Maybe for my understanding, as I’m still arguing with this :’), what should slf4j-timbre have done? They seem to need to AOT compile to generate classes found by SLF4j… (Genuinely curious ^^)

seancorfield 2021-04-15T20:17:59.146800Z

Given that nearly half the readme is given over to https://github.com/clojure/tools.namespace#warnings-and-potential-problems I’m amazed it’s still such a popular approach 😐

nilern 2021-04-15T20:29:15.147400Z

I don't recall having any problems like that. YMMV I guess.

nilern 2021-04-15T20:31:19.147600Z

And some of those caveats apply to any namespace reloading

hipster coder 2021-04-15T20:44:38.147900Z

@seancorfield would using an auto test runner... also be considered "don't use a workflow that reloads" ?

2021-04-15T21:01:38.148200Z

what should they have done? hard to say, the most flexible thing would have been to write a shim in java that loads and executes their clojure code as needed, and not aot the clojure code

seancorfield 2021-04-15T21:02:56.148400Z

I don’t like auto/watch test runners for different reasons 🙂

2021-04-15T21:14:03.148600Z

Yeah alright, that makes sense 🙂.

2021-04-15T21:58:26.149800Z

What you have to do in cases like this is only compile the java stuff, not the clojure

2021-04-15T22:06:25.150Z

So like, you AOT, but then include only the .class files you need in your Jar. That's the correct thing to do if you want to remain all Clojure and use gen-class/gen-interface over a Java shim.

2021-04-15T22:08:39.150300Z

Lein doesn't have support for this though, you need your own build step that packages a Jar this way

cgrand 2021-04-15T22:56:37.151300Z

@hiredman it’s a wip but this code happens to work (there’s a continue which skips the break)

2021-04-15T22:57:38.151500Z

ah of course, been a long time since I've used continue and break

2021-04-15T23:13:34.151700Z

Just a remark on tools.namespace: I find it useful when something defined with a macro like schema is changed and you need to reload all namespaces that use it (https://github.com/plumatic/schema), But yeah, I don’t like the idea of using this reloading stuff as a core part of my workflow either.