This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-11-22
Channels
- # announcements (39)
- # architecture (9)
- # aws (2)
- # babashka (17)
- # beginners (73)
- # calva (6)
- # cider (27)
- # clj-kondo (140)
- # cljdoc (67)
- # cljsrn (1)
- # clojure (99)
- # clojure-dev (4)
- # clojure-europe (35)
- # clojure-nl (7)
- # clojure-spec (19)
- # clojure-uk (2)
- # clojurescript (40)
- # community-development (3)
- # cursive (10)
- # datalevin (2)
- # datavis (2)
- # datomic (27)
- # deps-new (5)
- # events (2)
- # fulcro (38)
- # integrant (6)
- # jobs (3)
- # keyboards (1)
- # leiningen (13)
- # lsp (3)
- # malli (10)
- # meander (5)
- # membrane (1)
- # membrane-term (9)
- # missionary (3)
- # off-topic (29)
- # polylith (3)
- # reagent (3)
- # reitit (5)
- # remote-jobs (2)
- # reveal (7)
- # shadow-cljs (20)
- # tools-build (4)
- # tools-deps (8)
- # vim (10)
- # xtdb (3)
A small conundrum using deftype: I created a protocol IMyType in file, say, api.clj; I then implement that as a deftype MyType in a file, say impl.clj and now I want to use that type and its APi in another file, say core.clj. How do I go about requiring that type in core.clj, something like [myns.impl.MyType :as foo] (which does not work? This is being confusing since the bulk of documentation, including books, all want to show how in lein, which doesn't deal with requiring from files the way files do.
there is a function you can require that acts as a “default” constructor for MyType called ->MyType
I did see that when I typed the namespace. Ok. but what I want to do with myns-impl/->MyImpl is use its functions, e.g. store and fetch.
(:require [myns.impl :as myns-impl]
[myns :as myns])
(let [obj (myns-impl/->MyImpl 1 2 3)]
(println (myns/store obj 123 456))
(println (myns/fetch obj 123)))
Ah, the canonical question: why? I'll admit, it's a hangover from being a very long-time "plugin widget" guy, where the idea is to have a particular API, and then make it available to users without any code change but against completely different context.For instance, one is as a direct postgres driver, and one as an http interface to something remote.
In Java, it's called "design by interface" - spend all your time working out the APIs, then go implement them as needed.
if you just want to dispatch on the first argument - “single dispatch” then a protocol is a good tool
and the usual ways you can provide an implementation of a protocol are reify
, defrecord
and deftype
deftype is most common if the thing you are doing has some internal mutable state and there is a benefit to having a named type
I'm growing suspicious that my difficulties are more those of learning a new ontology than, necessarily, coding conventions. In Java, I see an implemented APi as a type; clojure doesn't let me just say "public class foo implements bar" so it feels somewhat logical to just say (deftype foo [] bar ...)
can you describe more specifically what you are working on? maybe that will help me give better advice
because you are right about the parallel - that is how you get 1-1 with what you would do in java
Sure. I'm migrating a topic map from Java to Clojure, doing a simple version first, but using the simple version to test the needs of a full-monty system later. The first version is to use Postgress driven by Datahike for a Datalog backside; that entails a specific, but simple API, one which can do puts, gets, finds, and deletes. However, there is an immediate need to graduate from the Postgres/Datahike backside to one which is, essentially based on a remote Kafka-based society of agents which, ultimately, does result in a Datalog backside but only after mountains of processing. it's all open source and going up at github. But, I don't want to have to change the internal logic of the topic map system itself when the backside changes. Thus, the design by interface.
The project itself is known as OpenSherlock, which fell out of my PhD research which needed the benefits of topic mapping.
Right now, OpenSherlock is a Java platform which uses the Python NLP platform spaCy for some of the work. All postgres based. That needs to change.
Just in case: a topic map is a kind of "knowledge graph" first invented in 1991 in SGML, then transliterated to XML; mine are mostly JSON; a topic map is like a "concept map" which is nodes with labeled arcs, except that in a topic map, the labeled arcs are actually first-class nodes -- relations are as important as the actors they connect.
Yes. They are very generic, "map-like" which can take on radically different semantics depending on the implementation.
The protocol and the functions it defines are the interface, and they are a group of functions, whose names are namespaced, so when you refer to them by name you usually require the namespace
okay so your current approach makes sense, with the caveat that It probably doesn’t make sense to expose the deftypes as part of the public api
In clojure you would have a function that returns something that satisfies the protocol
(where a factory might be some static method, which is very similar to defn in many ways)
and multiple implementations of said protocol for rocksdb/generic sql/kafka https://github.com/xtdb/xtdb/blob/master/modules/rocksdb/src/xtdb/rocksdb.clj https://github.com/xtdb/xtdb/blob/master/modules/jdbc/src/xtdb/jdbc.clj
but each namespace exposes a ->kv-store
constructor function which is intended to be used instead of the default generated ->RocksKv
and map->RocksKv
functions, which lets it do extra logic besides just setting the initial value of fields
you will also notice that the different implementations are defrecords and not deftypes
there are pros and cons to that - technically speaking when you are a record you gain all the semantics of a map. all your fields can be inspected and swapped directly, your hashcode/equals is based on your fields, and you get a toString
but in this context culturally its a known thing to not muck with the fields directly unless you “know what you are doing”
there are also more “encapsulat-ey” things you can do like make the protocol be the public api for extension, but not the public api for calling.
(defprotocol IStore
(store* [this data]))
(defn store [this data]
(store* this data))
you see this in the apis for clojurescript - and its something you do if you want maximum flexibility later on with external implementors, but is often overkill
(like if you wanted to introduce another dispatch mechanism, you can use the extra bit of separation between the store
and store*
to insert your logic)
also this might be throwing a lot at you early on and definitely falls into the fiddly bits of designing a public library, not what you necessarily have to consider when making a prototype/first draft
> This is being confusing since the bulk of documentation, including books, all want to show how in lein, which doesn’t deal with requiring from files the way files do. Also curious about this
your question didn’t end up having to do with leiningen, but are you using that to build this or are you alluding to you using maven or deps.edn
since if you are starting a project fresh nowadays the prevailing sentiment is to use deps.edn
I'm starting a procedure and will return to this tomorrow morning. Many thanks for great information.
Oh, I meant leiningen but I don't use it to test ideas the way docs do; I'm still in the early phases so I boot stuff I want to test by way of calls from core.cljs through 'lein run'
the nicest way to use clojure isn’t really to run a file, make some changes, run a file, repeat
generally speaking you load your project up and start a long running “REPL” process and write some code, load that code into the REPL, experiment with that code, repeat
if you use emacs or vim there are dedicated wierdos out there for you, but there is also a very good IntelliJ plugin called Cursive and a very good VSCode plugin called Calva
I'm working from intellij/cursive. I did what you say way back when I was hacking Forth some 30+ years ago; got away from that when I migrated through C to Java and got wrecked. I suspect that, in truth, I might be a bit lazy these days and do what moves more freely. I expect I'll get back to that method.
Trying to see if something is doable in a macro. We have a (CLJS) API that takes string arguments, that are parts of a closed set. I would like to error out in compile time if an argument is passed in that is not part of that set. The problem obviously is that while I could define the closed set at compile time, the value that gets passed in is determined at runtime. Is there a clever trick I could use to accomplish this? E.g. an idea I had is to push the macro to the callsite where the arg is still a string literal, then it can be used. But it kinda breaks the ergonomics a bit.
If something isn't available at compile time, you can't use a macro for that. No clever tricks here. Usually such checks are done in run time. Or you can wrap the whole thing in a macro and require your users to pass strings directly into it, without using any intermediaries.
can the API provide introspection as data? (self-describing via an OPTIONS request is what I'm doing) then you could download (or, if it's a monorepo, just reference the API specs, e.g. malli definitions) during build time.
@U2FRKM4TW thanks for confirming, a top-level macro was my hunch too.
@UR5RAGFFG in our case it's just a static map of strings to strings, nothing fancier than that 🙂
In reading the miniKanren thesis I found myself reading Kisleyov's papers on stream fusion, and suddenly, 🤯 those are transducers Moreover, this is what operators fusion does in RxJava, but with 1e6 lines instead of 6 lines like Clojure's transducers. Okay, cool. Then I thought back to my transducers workshop - can we extend reducibility to other things? Flowable can return iterable, then we can reduce it! We don't need all of RxJava, we already have transducers which handle operators fusion for us

(require '[clojure.core.protocols :as p])
(extend-protocol p/CollReduce
Flowable
(coll-reduce [coll f val]
(p/coll-reduce (.blockingIterable coll) f val))
Maybe
(coll-reduce [coll f val]
(p/coll-reduce (.blockingIterable (.toFlowable coll)) f val)))
That's itIs there a way i can get a consistent hash of uberjar builds? I want to skip a deploy step if nothing in a jar has changed
one is that the clojure Compiler isn't always same bits in same bits out, so if you are including the output of that (aot compilation) in your jars that means you just can't hash things
uberjars are of course zip files, which contain things that change like metadata about files
we do have one ticket related to this that I'm trying to move forward in 1.11 https://clojure.atlassian.net/browse/CLJ-1973
probably not the only source of this, but I know it's one of the main sources of differences
If I have a try catch and the last line of the catch is a (log/infof)
function call, will that result to a nil in the return value?
I believe the answer is "yes"
Not sure if this will nil pun correctly.
So end the catch with a nil?
Can anyone point me in a way of "idiomatic" function return in case when I need to return one or two values. Should i look in the direction of "for"? Currently I fixed this by always returning collection that contain one or two elements, but I don't like it for some reason.
But in general, you might think if there's a way to avoid the function returning one or two values? Maybe if you broke it into two functions?
That said, sometimes it's what you have to do, in which case just a vector or map is pretty straightforward.
But what I'd say is good practice, is if you find yourself doing something like:
(if (= 1 (count return)) .. ..)
Instead use a variant which would be something like:
You'd return: [:color :green]
or [:rgb 123 234 111]
instead of :green
or [123 234 111]
Or with a map: {:variant :color-name, :color :green}
or {:variant :rgb :red 123 :green 234 :blue 111}
Basically a variant is a self-typed vector or map. The variant tells you what structure the vector or map has, so someone can branch on the variant to know which variant of vector or map it got.
Its very similar to types, except the real type is still just vector or map. In fact, sometimes I call it :type instead of :variant.
Interesting thoughts may try to return a map instead or a variant. This problem mostly happen when i try to parse a multiple thing from source in one go. It might be better to really split it into separate function. Thank you.
you can also return a map. eg. {:a 42}
or sometimes {:a 42 :aa 43}