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.
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
@qythium where can I find the namespace docs on defining to-Y and from-X?
or, is that a convention? not a built in function
oh just a convention
it's an interesting question. you are asking how to setup the dependency... compose the 2 functions
my first thought is, how to gain maximum flexibility... so x->y or y->x
there's conceptually only one function from X to Y, I can't define it in both namespaces otherwise it becomes a circular dependency
so you can call them either way
assume the transformation is one-way only
ahhh ok
thinking...
still, the 1 way transformation isn't the actual dependency
for example, let's say this
x multplies by 2, y adds by 3
apple.clj
define to-juice
vs.
juice.clj
define from-apples
now I think about it, probably juice.clj defines Juicable
protocol and apple.clj extends it
ok... you could have apples, oranges and pears... but 1 juice
I usually have a single namespace where all defprotocols live
I would use "from"
it makes your code more extendable
I think of it likes this...
juice.from(any object)
versus... explicit http://object.to(juice)
then, you can define how th juicing is done, on the object
it's losely coupled
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
like apples to juice, smashed, chredded ?
Once you start mixing code written against a protocol with the definition you have a pretty good chance of introducing cyclic dependencies between namespaces
@hiredman do you mean, there's nothing enforcing the 1 way transformation... so the two could call each other, circular ?
does protocal mean... interface
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
the protocal is the 1 way transformation? the enforced rule
that apples must go to juice... juice can't go to apples
I assume the juice thing is a for example and not an exacting specification
easier, than x or y
I still think the juice.from(objectType) is better
is there some design pattern for this? protocals? interfaces?
Protocols are not java interfaces though
They are functions that live in a namespace
Not methods that live on an object
k, and, they are the outward facing functions that need to be avalable?
like... apple would need a "protocal" function named "juice" ?
To invoke the protocol functions you have to depend on the namespace where they are defined
Yeah the apple juice was just an example, my actual domain is related to compilers
but also I'm interested in general architectural patterns separate from domains
My go-to oss example is https://github.com/clojure/core.async/blob/master/src/main/clojure/clojure/core/async/impl/protocols.clj
ahhh, I see
The protocols are all defined in one file, which only has protocol definitions, which keeps it separate from implementations, which avoids circular dependencies
this is a good idea...
The same pattern and issues applies to defmulti based interfaces as well
it's an extra layer, between the object and behavior
yes, defmulti, polymorphics, based on types
well, that was very intesting... it's like the good parts of inheritance... interfaces... but for functional paradigm
Golang took the good parts too. replace inheritance based polymoprhism with interfaces
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")))
@qythium can you push a small demo of that code to your github?
Maybe I'm still thinking of this wrong - trying to understand the core.async code
I'd like to actually see how protocals makes the code maintainable... when you add in more types
I was just studying this stuff, yesterday. functional style, types, monads
I think the Expression Problem refers to something like this
@qythium I am reading it... my first thought is... following domain driven design... Juicing is the most domain specific part of this problem
this is important.... because Juicing is the dependency... on the outside world
and, in the real world, when I juice veggies, fruits... I use the same behavior. I use my OmegaVert juicer
everything else should be deterministic
that's why I like the juice.from(apple) approach
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
I am going to code up something, now... you have my mind going
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..
Multimethods might make more sense there
You can consider :extend-via-metadata
on your protocols to use them with maps https://clojure.org/reference/protocols#_extend_via_metadata
I am trying to figure out... is the main advantage of the protocal... that you can use namespaces?
opposed to... "AD HOC" polymorphic with multi methods.
Protocols implement ad-hoc polymorphism, because the functions dispatch only on the type of the first arg. Multimethods have more flexibility
I never quite trusted metadata 😅 You have to take extra care everywhere to not accidenatally drop it
I'd put it all into one namespace and call it x->y
Otherwise it weirdly feels like you're using namespaces like classes, which is wrong
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
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.
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.
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.
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.
Thanks! I think I've seen the "Namespaces group functionality, not abstractions" advice elsewhere, but this made it finally click 🙂
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...
the actual line #, with error, is at the top, past the screen
Not a fix to your problem, but I usually do is to search for ERROR
or FAIL
(in terminal, using cmd+f
)
if I scroll up inside my tmux... I can see it... Are all of you using your monitors in vertical position?
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).
(I have a lot of feelings about clojure.test
🙂 )
do you use the property testing library? I saw that one... looked interesting.
@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.
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.
I think you can hook to clojure.test reporting, and report wtv you want.
I normally run my tests inside Cider, so this isn't a problem, since cider displays things in the editor
But I remember a library that gives you nice commmand line reporting for clojure.test
Found it: https://github.com/venantius/ultra
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
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.
(Ultra is probably the #1 plugin problem I’ve seen people trip over in #beginners)
I never used it, just remembered it.
@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
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
I found it... they just mixed up the commands... it's t T
If you use Emacs just run the test with Cider - it does all this filtering for you and more
I have cider running in Spacemacs... checking now for that ability
I am new to Spacemacs... not sure what the , comma key is
, T t
Does AOT compiling the code affect alter-var-root
? I keep forgetting if there’s any interaction there.
@roklenarcic Direct linking (`clojure.compiler.direct-linking=true`) will affect this.
since many of the indirections will already be gone by the time you execute alter-var-root
but AOT in general does not affect this since the indirection through the var will still be there
direct linking doesn’t touch dynamic vars though, right?
right
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)
or those marked redef
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 🙂
thanks
if you have a lib where something should not be direct linked, then that's what ^:redef is for
@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
An example of your API would be better here
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
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!
Did you write this yourself or get it from somewhere? I'm almost certain the ^some
doesn't do anything meaningful
Looks like it's from https://twitter.com/cgrand/status/1382433410724081668
So it might be meaningful in dart
whoa, never heard of ClojureDart before
so it's not standard JVM clojure syntax in any case
that's not a JVM clojure thing, must be specific to the Dart stuff
ah - ok well that helps
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
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.
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.
It's explained in the thread: https://twitter.com/cgrand/status/1382580332420009986
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
Interesting - thank you!
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.Not the most sharp … problem description, sorry about that 😞
the problem is slf4j-timbre is aot compiled
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.
it will be something like an overlap of depedencies between the rest of your app and sl4j-timbre
I’ll take a look whether I can find some misaligned deps then, thanks
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?
@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…
@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.
Yup, this is one of those “don’t use a ‘reloaded’ workflow” things. It breaks in mysterious ways.
It is mentioned in https://github.com/clojure/tools.namespace#warnings-for-aliases
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 ^^)
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 😐
I don't recall having any problems like that. YMMV I guess.
And some of those caveats apply to any namespace reloading
@seancorfield would using an auto test runner... also be considered "don't use a workflow that reloads" ?
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
I don’t like auto/watch test runners for different reasons 🙂
Yeah alright, that makes sense 🙂.
What you have to do in cases like this is only compile the java stuff, not the clojure
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.
Lein doesn't have support for this though, you need your own build step that packages a Jar this way
@hiredman it’s a wip but this code happens to work (there’s a continue
which skips the break
)
ah of course, been a long time since I've used continue and break
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.