This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-12-30
Channels
- # adventofcode (7)
- # announcements (9)
- # babashka (20)
- # beginners (182)
- # calva (9)
- # cider (20)
- # circleci (3)
- # clj-kondo (1)
- # clojure (269)
- # clojure-europe (2)
- # clojure-india (1)
- # clojure-italy (6)
- # clojure-nl (5)
- # clojure-uk (50)
- # clojurescript (56)
- # code-reviews (3)
- # core-async (174)
- # datomic (4)
- # duct (1)
- # emacs (3)
- # events (1)
- # fulcro (31)
- # graalvm (10)
- # graphql (8)
- # jobs (1)
- # joker (11)
- # juxt (7)
- # luminus (2)
- # malli (4)
- # off-topic (2)
- # overtone (1)
- # pathom (2)
- # re-frame (24)
- # shadow-cljs (42)
- # sql (1)
- # tools-deps (10)
@patrick.glind If you don't mind paying for some high quality video tutorials, I'd recommend Eric Normand's http://PurelyFunctional.tv -- especially the REPL-Driven Development course.
What’s the series on YouTube that mike fikes and others do? I’m forgetting the name now but they do a repl based problem solving for the second half of their broadcasts. Not sure if still active though
Also not totally clear on the etiquette about when talking about someone but don’t need to ping/bother them
@ludvikgalois Not sure what I'd point you to as an answer to that very open-ended question... I'll be interested to see what folks actually suggest.
Right, I often avoid @ mentions for the same reason—evidently it can trigger on your last name. :thinking_face:
I’d suggest Tim’s Clojure Tutorials but their subject matter is pretty niche - like DIY JITs, interpreters, logic inference and query engines etc.
Heartily agree on that. Tim is super interesting and has a great patient voice as he explains
Ah, I've never watched Tim's videos so I didn't know what was in them. Good to know.
Hi! I'm writing a simple parser to read MP3 tags from input stream, using core.async.
(defn- read-frames
[parse-map contents]
(let [stream (:stream parse-map)
fchan (chan)
result (go (loop [frames {}] ;; conj frames to resulting map
(if-let [f (<! fchan)]
(recur (conj frames f))
frames)))]
(loop [left (:remaining parse-map)
id (d/frame-id stream)]
(if (and (> left 0) id)
(let [n (d/uint stream)
_ (d/skip stream 2)
x (d/ubytes stream n)] ;; read binary data
(put! fchan (or (f/frame id x) {})) ;; convert binary to frames
(recur (- left (+ n 10))
(d/frame-id stream)))
(close! fchan))) ;; close when no more data left
(assoc-in parse-map [:tag :frames] (<!! result)))) ;; wait for result
Previously I tried using (go ... f/frame ...)
instead of (put! ...)
, and it happened to work slower. I want all (f/frame ...)
calls to work concurrently, and in the end they should all conj the result into a map, which will get returned at the end. Can you suggest any improvements to this?Thank you everyone for your suggestions! I did find Eric's videos however are too much for my budget right now, I will check the others
Sounds to me like you might get a lot out of these https://www.youtube.com/channel/UCLsiVY-kWVH1EqgEtZiREJw
hi everyone! , is there an open source project that heavily uses core.async? I would like to study how it is put in use.
@vachichng otarta, an MQTT-library I wrote: https://gitlab.com/eval/otarta/tree/master#otarta
question about clj-time
and specifically timezone shifting: If I shift a date-time
object using the offset functions like this: (time/to-time-zone (time/now) (time/time-zone-for-offset -6))
and then print the value using str
, the value printed contains the correct date. That is, if the offset shift spanned midnight, the earlier date will print post-offset. This all seems correct. However, if I call clj-time.format/unparse
on the date-time
object using a format string which results in just a date, no time (for example yyyyMMdd
) I only get the date post-midnight. I.e., it appears to be unshifted. I'm wondering if I'm doing something wrong or of this is just a misunderstanding on my part about how clj-time
is supposed to work.
Related, I can not use the various local functions in this case as I'm working with multiple timezones and there really is not a notion of local
in this context.
Okay, answered my own question. Didn't see this in the docs, but clearly shown in the code. Looks like there are functions to wrap formatters with timezone information, among other things.
hi, I'm having some trouble trying to use crux with lmdb. I tried to use rocksdb before, but for some reason that didn't work at all (no locks or something) and it blew up the data directory to 200 MB immediately. now with lmdb it seems to work, but it feels like the process is hanging(?). right now my code only submits a few docs and then outputs all of them, lein run works within 20 seconds or so, but then the process just idles. lein uberjar ran for 2 hours and produced no results
time lein uberjar
Compiling rocksdb.core
Dec 30, 2019 3:04:33 PM clojure.tools.logging$eval361$fn__364 invoke
INFO: unknown
Dec 30, 2019 3:04:33 PM clojure.tools.logging$eval361$fn__364 invoke
INFO: Using libgcrypt for ID hashing.
^C
real 115m3.416s
user 2m54.682s
sys 0m18.303s
I'm looking at both the main-figwheel and shadow-cljs tutorials, both advocate using CLI tools instead of lein/boot. CLI tools doesn't seem to provide you with a project structure though. Not that it's a big issue, but being totally new to it - it feels like you are missing out on 'good' practices when it comes to the project layout etc. Is there a boiler-plate project structure for shadow-cljs or figwheel?
@glfinn83 for shadow-cljs there’s https://github.com/filipesilva/create-cljs-app, inspired by create-react-app
Haven’t used it, though bookmarked it when looking for it recently. Went with one of the example-projects instead: https://github.com/thheller/shadow-cljs#examples
What's the best way to convert a seq of vectors (like ([k1 v1] [k2 v2] [k3 v3] ...)
) into a map?
Quick Q: Does anyone know the origin of the Hiccup DSL? I feel like it’s James Reeves, but I just want to be sure i’m not missing someone. Thanks!
I have a question about protocol and defmethods. I have a map that represents a type [for exemple, a payment type] which has some specifics keys in it. Cool. However, I want to create a protocol and extend it to work with this specific map. I thought about using a defprotocol
at first, and I worked my way up to define the interface signatures, however when I started to implement the methods itself I tried to use a defrecord
but for this situation I would have to wrap my entire map in this defrecord. It does not look right to me. I should use a multimethod and dispatch on some key of my original map or there are better alternatives other than wrapping the data?
Probably a good candidate to extend via metadata (see https://clojure.org/reference/protocols)
Just add the metadata to the map that implements the protocol
(def john-doe (with-meta
{:name "John Doe"
:language "us"}
{`clojure.core.protocols/datafy (fn [x] (assoc x :type 'Person))}))
(datafy john-doe)
{:name "John Doe", :language "us", :type Person}
from this website: https://blog.klipse.tech/clojure/2018/12/18/welcome-clojure-10.html?interactive
the idea here is to have the implementation somewhere else and attach it on the fly, right?
Yeah, you attach the implementation to instances, not classes
The only caveat is that sequence functions don't preserve meta, so if you do any tranformation on the map and put it back in a map after, that new map won't have the meta anymore. So you need to manually preserve it in such case.
This caveat applies to records as well, in that any sequence function will turn it into a seq, losing the type. So you need to explicitly make it a record of that type again yourself. With record, ots even worse actually, because a dissoc will also lose the type. So got to be careful.
in the interface there is no way to enforce the return value of an implementation, right? idk if I'm overthinking but this should be desirable?
Multi-method that dispatches on a key/value of the map doesn't have these problems. Granted that you always put that k/v back on your resulting transformed maps
one more edge that I don't know if I'm doing it right is about the client that will use my interfaces. I would like for the user to require only one namespace but it seems that when the implementation lies in another file, I have to require both the interface and the implementation. But how the client would know where the implementation lives?
[sorry, bunch of question prior to holidays hehehe.. very intriguing subject.. I am trying to grok this style of programming for a while now ^^]
You would either put them both in the same namespace. Or have one namespace require the other. And then the client that requires that namespace will have the impls namespace brought in transitively
just to be clear. I have 1) namespace with the interface and 2) namespace with the implementation which already imports 1)
I think in your case you're better off just moving the defmethods inside the same namespace as the defmulti
sounds interesting, I've done that on Python a lot.. init files with bunch of imports lol
this option is not that good, right now I have three different types that are extending the interface
I'm a fan of https://github.com/ztellman/potemkin/blob/master/README.md#import-vars for that sort of things
do you have more material to recommend about oop in fn-style [idk if I can say this hahaha]?
Otherwise sometimes people just do it manually. So you have this single namespace with all functions and vars you want to expose. And you have them use your impl code under the hood.
I re-read a book I had about CLOS and was very interesting to see the amount of similarities with clojure system
But now that you're using the same name, how do you decide what behavior to choose? Where there are more than one? You do so by inspecting the runtime values
In other words. You could have a talk-dog and talk-cat function. One takes a dog map and another a cat map. No need for polymorphism at all. But if you want to abstract talk from cat/dog. Say because you have another function and it could take a dog or a cat as input, and it wants to make it talk no matter what the animal is
But now if you have many functions who operate on animals. And all your type of animals share a lot of functions, talk, walk, sit, etc.
You'll have a lot of redundancy, where you're going to have case expressions everywhere
What you want is to delegate to the functions themselves, talk, walk, sit, etc. The responsibility of having the case expression and figuring out what to do based on the type of animal
But you could also just move that case inside each of them, though if you do that, they are closed for extension. Adding another animal can only be done if you have access to the source code, since you need to add one more case to case. Multimethod allows open extension, clients can add new cases to it even without access to the source code.
Anyways, my point being, there's nothing here that's OOP. There's no objects, no classes, no inheritance, no grouping of data and operations together
It just so happens that popular OOP languages also support some forms of polymorphism in addition to their OOP support
if you compare to smalltalk (the paradiigmatic OOP language), and the designer's definition of OO, what multimethod does is closer to being OOP than what Java does
I never used smalltalk, but from my understanding its not so much polymorphism, its more about Actors
in smalltalk a method is a message (implemented as a string iirc), which an object dispatches on
conceptually described as an actor, but it's a polymorphic dispatch, just like multemethods
but it is true, in smalltalk the method impl is stored on the object keyed on the method name and in clojure it's stored on the multimethod and keyed on the class of the object (dispatch checks class then superclasses until it finds a match)
Hum... I guess you could see it that way. The object dispatches based on the message. Just like on Java an object dispatches based on the type.
I still don't think that was the point of smalltalk. At least from all the articles about what the real point of smalltalk OOP was. It seems it was more about the message being a string
java objects don't dispatch on type, methods are stored on the object
the connection I'm trying to draw is that regardless of the location of storage, a link is stored between a piece of data and an operation, such that code that is written in terms of the operation can figure out at runtime what to do with that data
and to me that's the interesting part of OO. I'm getting off topic here but I really think C++, Java, and all their descendants severely missed the point
Hum. I'm not actually super familiar with the underlying dispatch mechanism. Doesn't Java walk up the class hierarchy to find a method that matches?
I think I don't totally follow. To me, polymorphism is orthogonal to OOP, even though you can intertwine them. Such as choosing to use the class hierarchy as the driver for the polymorphic dispatch
But you don't need classes. You can decide to dispatch based on value, arity, some configuration, etc.
Even when I said dispatch on type, in Java types and Classes are also intertwined. But types also have nothing to do with OOP. Haskell hapilly has types and dispatches on them without having any classes for example
I don't know enough about smalltalk. And the smalltalk OOP might be a whole different beast and maybe that one is much more related to polymorphism. But that's just semantics. I'm talking about class based OOP, your classic C++/Java/C#/Python OOP
Where I might agree with you, I guess is that if I ask myself what an object is, its really just a set of fields and methods. And so you can think of it as the method to execute is based on the object you are calling the method with
And ignoring the class hierarchy and interfaces, you can think of it as object based polymorphism. But that is only true if by convention you have two objects where you choose to put a function of the same interface and name
I guess I see an object as a self referential collection of mutable fields and methods. Which I'm not sure how to implement in Clojure. But it be something like: `{:name "Shadow" :talk (fn [] (print "Woof, I am " (:name this)))}`
And a language designed around this particular structure as its foundational building block where all data and operations are organized using these objects to be an OOP language
Now you could say if I also created: `{:name "Marble" :talk (fn [] (print "Meow, I am " (:name this)))}`
Now if I do: ((:talk dog))
this will be a polymorphic dispatch based on the kind of object I have and the specific talk fn it contains
So I guess I see them as orthogonal, even though you can leverage objects for polymorphism, it doesn't seem you have too
but the concepts terms have been likely been abused far past the point of being useful at this point
and to go back to @iagwanderson’s point above, it's not accidental that clojure has features that resemble CLOS
I have to reread the multimethod in clojure, but we also have the concepts of before-method, primary method, and after-methods?
no, we don't
hi everyone, did we ever come to consensus on what's the best data structure for something that resembles a ranked list or ranked map?
Not sure if I missed some previous discussion, but I'd think if you care about order you'd want either a list or vector over a map. Use a list of you are going to be re-ordering elements frequently, and don't care about random access. I'm under the impression vectors give you O(1) look ups of elements by index, but need to be pre-allocated so are not ideal if you will frequently be adding/removing/shifting elements around. Lists give you O(n) access, but can more easily modify elements they contain.
CLOS is in fact how polymorphism was introduced to lisp
(where entries move up and or down)
I've been using this dispatch on a project very timidly, but I already got the benefit of changing a implementation more than two times and not changing the interface. I very recently started at clojure and I didn't find many people advocating and showing examples of more intense usage of interfaces. Idk maybe I have missed something along the way. Do you guys think this subject is stressed enough in the community?
Changing an implementation without changing the interface does not necessarily require this kind of dispatch or polymorphism. A simple function will do
There a times where multi-methods are great, I'd say those are when you want users to be able to extend the behavior on their own. Or when you want more than one active implementation of the same interface
And protocols can make sense when you need the same, but over a coherent set of functions.
Its normally most useful for creating pretty generic abstractions. Like fundamental language constructs
For example Manifold is a good example use of protocols to create a very generic abstraction https://github.com/ztellman/manifold/blob/master/src/manifold/deferred.clj#L42
we usually get the benefits implicitly by using clojure's built-in datatypes and using functions that dispatch based on interfaces
most of the time that's enough, and it leads to simple flexible code
90% of my use of polymorphism in java is to stub/mock things out for tests. clojure has with-redefs
so I don't need it for that
anybody got an idea why crux + lmdb runs fine in my repl, but takes forever to finish the process when it's run using lein run (or has never finishes when doing lein uberjar)? I'm just outputting all 4 values and then close the node, is there something I'm missing?
compilation makes sense to me: clojure compiles every form before running it, and runs every form when loading a file
it's possiible to read a file without compiling / running, but uberjar doesn't do that
(and it's only useful fo preprocessing / analysis - you just get lists of symbols and such)
I'm just planning to use it as a db to store some data for a small project, ideally I can just give somebody the .jar, they run it and it starts the node and closes it after it wrote the necessary things to it, should take like 10 seconds max. but I'm guessing this is not the right approach then, other recommendations for what I should be using for this case?
you shouldn't define the db at the top level of the file - you can have a function that makes the db, and run it inside your main method
right, that's a common misconception - side effects inside def run when the file is evaluated, and with standard uberjar with aot, that includes jar packaging time
the reason that lein run
takes so long is another question - one thing to try is adding a call to (shutdown-agents)
at the end of your main right before exit (it closes down clojure's auto-scaling thread pools so the vm can exit, but should only be done at exit, it's a one way switch)
could you also help me to define a function that makes the db, as you mentioned before?
my recommendation:
(defn start-db []
(crux/start-node ...))
(defn -main [& args]
(let [db (start-db)]
(function-that-uses-db db)))
there are libraries like stuartsierra/component and integrant that simplify the above pattern but in early stages that's exactly what you want
thanks! I think I'm confused about the "def ^crux.api.ICruxAPI" part, just copied that from the getting started guide
that's something that works in a simple example - it defines the db as a global, and the ^type
notation tells the compiler what the type contained in the var will be (not really needed here at all)
oh it's a typehint. noisesmith is right. not sure how I forgot that. so metadata on the var that describes the type of the object. and yeah shouldn't be needed while sticking to the clojure api for crux
both descriptions are correct - the compiler uses the metadata
in this case I suspect the type hint is actually there for the human reader who understands how typehints are used (it's telling you the class of the object returned in that call)
after wrapping start-db in another function and adding (shutdown-agents) to the end of -main I still get the same behaviour when trying lein run
you can use Control-\
when running in a terminal to see the stack traces of all running threads - one of them likely exposes what's blocked
the jstack
command does the same, but takes a PID and runs in a different window
for the uberjar question: this is a common pitfall for newcomers, clojure doesn't have a "compile" mode, any code inside def
is run when compiling
You could write union specs? 😛 https://clojuredocs.org/clojure.spec.alpha/or
There’s actually a good example on https://clojure.org/guides/spec
(s/def ::query string?)
(s/def ::request (s/keys :req [::query]))
(s/def ::result (s/coll-of string? :gen-max 3))
(s/def ::error int?)
(s/def ::response (s/or :ok (s/keys :req [::result])
:err (s/keys :req [::error])))
(s/def ::response (s/or :ok (s/keys :req [int?])
:err (s/keys :req [string?])))
(s/conform ::response {:err "toto"})
;; => [:ok {:err "toto"}]
Am I missing something ? because the answer seems wrong from the replreq takes the key names as fully qualified keys which will use the spec of the same key to validate the value of the map for that key
`(s/def ::ok int?) (s/def ::err string?) (s/def ::response (s/or` `:ok (s/keys :req-un [::ok]) :err (s/keys :req-un [::err])) (s/conform ::response {:err "toto"}) [:err {:err "toto"}]`
> Creates and returns a map validating spec. :req and :opt are both > vectors of namespaced-qualified keywords.
You must define a separate spec for the keys you’re checking and validate/conform/explain from there
@dimitri.tavan If you use the specs from the docs, and call this. It will be correct
user=> (s/conform ::response {::error 404})
[:err #:user{:error 404}]