This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-07-21
Channels
- # aws (2)
- # bangalore-clj (14)
- # beginners (20)
- # boot (20)
- # cider (7)
- # cljs-dev (38)
- # cljsrn (13)
- # clojure (487)
- # clojure-argentina (3)
- # clojure-dev (15)
- # clojure-gamedev (4)
- # clojure-italy (3)
- # clojure-poland (1)
- # clojure-russia (1)
- # clojure-spec (25)
- # clojure-uk (47)
- # clojurescript (127)
- # datomic (125)
- # defnpodcast (1)
- # hoplon (27)
- # jobs (4)
- # lein-figwheel (2)
- # leiningen (1)
- # luminus (5)
- # off-topic (3)
- # om (25)
- # onyx (9)
- # parinfer (3)
- # pedestal (20)
- # planck (65)
- # re-frame (43)
- # reagent (4)
- # remote-jobs (1)
- # ring-swagger (2)
- # rum (9)
- # spacemacs (1)
- # unrepl (37)
- # vim (1)
i feel like i’m missing 2 things about protocols;
1) if a record implements a protocol, and that record has fields defined in defrecord MyRecord [foo]
, what’s the difference between using foo
and (:foo this)
in a protocol function’s implementation?
2) how does a protocol call one of the other functions defined in its own protocol interface? I’m getting compiler warnings about my-protocol-func not defined
if attempting to do something like
IMyProtocol
(foo [_ a] ...)
(bar [_ a] (foo _ a)
protocol functions need to be used the same way you would any other function
you look like you might be using the interface generated by the protocol instead of the protocol
they belong to the other namespace
you could also use the method call syntax .foo
without namespacing of course, but it’s better to use the protocol function
ahh.. they are defined in the protocols.cljs
so you have to protocols/foo
in the above example
right
very common beginning mistake with protocols - to think their functions are like methods
(and they are - but not in this aspect)
so the this
context is kind of the method context? “invoke my protocol func on this
”
it’s the object that the method was called on - and don’t call it _
if you are going to call a function on it
I would say the answer to your first question is "there is no difference use whichever you prefer"
_
is a naming convention that means “I plan on ignoring this, on purpose”
although honestly I haven't used a defrecord in 2 years so I may be misremembering something
(this meaning the first argument to something defined inline on a defrecord, deftype, or reify)
I suspect that using the name of the field directly leads to a faster lookup, but if that difference means anything in your code you probably want something more performant than a record anyway
so even if I invoke (protocols/foo this)
, it still doesnt matter if I refer to the record’s field or the (:foo this)
because at that point the context has already changed
the first argument is always going to be the thing you invoked the protocol function
(defrecord MyRecord [field]
IMyProto
(foo [this a] (do-something field)) <- here shouldnt matter if I use field or (:field this)?
(bar [this a] (protocols/foo this a)
I just want to make sure I dont get this very subtle bug by implementing a protocol that’s using a field versus a lookup on this, when self-referencing(?) a protocol
thanks @hiredman @bfabry @noisesmith :thumbsup:
let my_obj = {
lolGoodLuck(args) {
let that = this
function() {
that.call(this, args)
}
}
}
my_obj.lolGoodLuck.apply(youWillNeverGuess)
Got a tough problem if anyone has an answer. Basically I have a large data structure (vector of hashmaps) about 50,000 hashmaps. And I need to pass the data to a Java function, but the hashmap has keyword keywords and those keywords need to be transferred to strings for the java function. Is there a fast way to convert keyword keywords to strings? right now the conversion takes about 2 seconds
You're talking linear time + string manipulation. Probably not a fast operation no matter how you slice it.
name shouldn't be manipulating strings - it should just be getting a field from the keyword
Turning string data into keywords is a bad habit that clojure developers have. Benefit is really only concision, downsides are slow and buggy translation.
(I mean, if you are converting by manipulating strings, use name instead)
@josh_tackett I just generated 50,000 keywords, at 50 random characters each (using c.s.gen), and both str
and name
take about <10ms to convert the whole vector from keywords to strings
(defn trunc [s n]
(subs s 0 (min (count s) n)))
(def kws
(doall
(gen/sample
(gen/fmap
#(keyword (trunc (name %) 50))
(gen/keyword))
50000))
(time (do (doall (map name kws)) nil))
"Elapsed time: 5.684874 msecs"
=> nil
(time (do (doall (map str kws)) nil))
"Elapsed time: 5.781799 msecs"
=> nil
@joshjones ya your test doesn’t compare as I am talking about a series of hashmaps with multiple keys in each one
but @potetm your suggestion is what I landed on, so I’ll just change my json decoding to get string keys
@joshjones correct
I bet the map construction when converting is the more expensive part, not the keyword/string part
but avoiding converting avoids both
@lwhorton re: field
vs (:field this)
-- I asked a while back and Alex said to use field
as it will be faster and it's the "correct" way to deal with records /cc @bfabry @hiredman @noisesmith
I'm using boot for my build system. Is there a library to help with the following task. I have a function TRANSLATE, which does .foo -> .clj. I also have a bunch of .foo files. Whenever any of them changes, I want TRANSLATE to be called to generate the corresponding .clj file. Is there a library to handle this for me?
@solve-calc.com This should probably do the trick https://github.com/wkf/hawk
I see now. The core of my problem is just a "watch + notify." Right, there's a boot task. Boot can do this already for me.
hi everyone, i want ask, how create multiple connection database or host in sqlkorma ?
The arm jvm is not so great last I checked, and startup times go into the multiple minutes if using nrepl and lein
But it can run things once it gets going.
I have two REPL clients connected to the same nrepl and I want them to share a stdout/stderr, is this possible without unix hackery?
nrepl is a network library, it doesn’t share stdio between processes
you’d need some other infrastructure entirely
you could set something up for multicast output to clients, but it would require new middleware on both the client and server sides
hm.. maybe only server side
Thanks! @noisesmith I guess nirvana is not for this world. I'll just do the unix hackery
can anyone explain the difference between event
and trigger
in FSMs? In particular in context of UML state charts specification.
Is it just a java-esque OOP overhead, or is there key semantic difference?
“trigger” is more general, “event” assumes a specific interpretation of how the state machine interacts with an unknown outside world
by unknown I mean “something not specified by the machine itself”
you can of course describe all things as events, but that’s a more specific term, and some semantics prefer to draw a distinction between eg. an event and a start of a timer
since that can be triggered internally by another part of the machine
maybe I did that already as you were typing? if a change in one part of the machine activates another part of the same machine, that’s a trigger, but not an event because it came from inside the machine itself
so let’s say a parser: if your language rules say “you are now in a closure” that’s a trigger but not an event, if you read a semicolon and that means you end the closure, that is an event that is also a trigger
I have a feeling, that having both events and triggers (which are followed with guards (predicates)) - is somewhat excessive, at least in the clojure/edn/library context
they are different levels of abstraction - an event is a special kind of trigger and most users of fsms differentiate them
definitely implement in terms of triggers first, if you do it right you can build events on top if you need them?
but calling a trigger that is totally internal to the state of the machine an event seems weird (unless you also reify the trigger as some output and re-consume it I guess? I’ve done that for coordination)
I get that there are less/more specific events. what I cannot pin point, is why some would be triggers, not events (or vise versa)
so basically: internal=trigger, external=event? (interna/external to machine, not particular state)
no, event is not the general thing, a trigger is the general thing
all things that cause transitions are triggers, period
if you only want one abstraction, it’s a trigger
an event is a special kind of trigger
@misha it could be that what you really want is event sourcing. That works very nicely with clojure / immutable data.
while a state machine is kind of designed for c or assembly, it’s all about when to do what mutation
if so, yeah, everything should be an event, you have a log of events to consume, and you project various views from the events via a projection (aka fold, aka clojure.core/reductions
).
I’m currently building a system around that, and it’s very nice (especially the fact that when I hit a bug I can just dump an excerpt of the log, and replaying that log builds my regression test)
@noisesmith I am trying to reconcile my understanding of the components of: rule based systems, FSMs, and event sourcing, to "solve" single page apps state management for myself. I tried basic event sourcing, but when you have: few audio/video html tags with their own states, some i/o, a bunch of buttons depending on the above, and custom performance-sensitive progress bar – it kind of is not enough, and coordinating all of this is not much easier, than just a bunch of atoms with a page of add-watch
es connecting them. So i went looking for a better way, or just to improve my understanding of the building blocks.
@misha OK - I suggest checking out petri nets btw because they are async and have a formal provable semantics
@misha also the very high level response to that problem is “when event sourcing leads to local state changes, reify the state change by sending it out as a new event and then consuming it”
that way you still have the events as the canonical source of all truth
when you have local state changes that will guide behavior that are not visible in the events, you’ve undermined the event sourcing system because you now have an irreproducible state
it’s actually not event sourced any more
I sort of did that. But having few "a pure state {} depending on side-effectful io/video/audio and vice versa" loops messes things up. So I went for specs to get some vocabulary for "internal/external/pure/io events", etc.
the challenge is: from event sourcing pov: there are like 100 states, but from ui components' pov: 10, and I am looking for a way to manage those 2 "projections"
(basically states vs. https://en.wikipedia.org/wiki/UML_state_machine#Extended_states)
my solution was to base things on a petri net - which have two kinds of things, places and transitions, a transition consumes tokens from 1 or more places and puts tokens into 0 or more places. All canonical state is the content of places (keys in a hash-map, obviously) and transitions are described by predicates on the contents under those keys and an update that removes tokens from N places and places tokens in N places
but yes, I have dealt with that problem you describe in my reagent app
tokens are immutable data
the predicate says “given these tokens that currently exist, do I fire?”
and firing consumes some tokens and maybe produces some in other places
it’s like actors without the data hiding
instead of mailboxes they have some places they watch (and anyone else can watch), and instead of sending messages they remove data from one place and maybe put new immutable data in places
they aren’t ordered
and you might say “I only fire if I can consume 3 from :a plus one from :b” - eg. that is what assigning work to a worker ends up looking like - you have a token for a worker and a token for the job, you consume both and create a new token representing a pending result
no, you don’t even need an atom
I put it all in one hash map, but you could eg. represent the whole thing with reductions
which would mean each state is another item in the lazy-seq that reductions outputs
(free history!)
or reduce on an atom
or whatever
@samcf hehe my event / petri based library is called “ludic” and the primary metaphor is playing chess by mail
but really it’s doing a bunch of data transitions based on a petri net model from an event source 😄
(ludic is a super intellectual word describing playfulness)
so you have a list of "workers", and just (while true) apply them on the same {}? and each of those modify it, if "there is enough of particular tokens"
@misha one thing it does is manage workers and tasks, but that’s just because I’m running a computation cluster
if you are in the browser, you don’t need workers - make places / tokens that model your domain
eg. a place could be a dom node and the tokens represent the data driving it… (maybe?)
or maybe better would be a place being a UI state, tokens describing it, and transitions deciding where it should go next
Hi! I have a set containing vectors, e.g. #{[1 2 3]}
, and I want to get the first element of the first member of the set. Is there a more idiomatic way to do this than (first (first set))
?
There's a ffirst
function.
just a reminder that the first element of a set is not a well defined concept. it may be consistent but this would only be due to implementation details
@U11BV7MTK thanks! I’m aware of that however, and in my case it doesn’t matter. The set should always be either empty or a singleton
thanks 🙂 it could have introduced a nasty bug if I relied on the first element being stable..
@hmaurer sets don’t have “first” anything, they are unordered
if you want an implementation-derived item that will change when you change contents in odd ways, sure, you can call ffirst
@noisesmith right; in my case I don’t care about which element of the set gets picked; hopefully it should be a singleton set, or empty set
but don’t count on any particular item coming out
@misha I almost regret mentioning my lib - it’s still super young and I don’t think the examples are good yet and it’s still changing fast
just FYI
I only brought it up because of the game thing
@noisesmith I just need lots of input and analogies to straighten my thoughts at this point :)
@misha on that account, this book is very well written, has beautiful illustrations and typography https://www.amazon.com/gp/product/3642332773/ref=oh_aui_detailpage_o04_s00?ie=UTF8&psc=1
(it’s a book about petri nets, since the url doesn’t reveal that)
definitely the most beautiful CS book I own
np - and keep in mind you might not need the petri net model - you might just want eg. CSP instead (since core.async is right there) - but do think about how well the foo you connect your events to is something that works nicely in the clojure ecosystem
events -> datalog works great too
the bigger picture I keep in mind, is some kind of format, which can help describe system in concise way, so you could see it all at once, to some level of details
channels are cool, but when all the plumbing is spread across entire project - it becomes challenging to see the "project mini map". And anything I encountered so far – are either one way graph wiz pngs, or completely new visual-first language.
it was pretty straightforward to make a function that consumed my rules and based on the places they consume from and produce to (fields on the rule record) generate a .dot
file for graphviz showing a petri net.
not the precise petri net my code implements (since my code can easily break that abstraction…) but at least a diagram that is helpful
you also might find Milner' Pi Calc useful https://www.amazon.com/Communicating-Mobile-Systems-Pi-Calculus/dp/0521658691
@mobileink oh nice thanks for reminding me of that
Is https://github.com/stuartsierra/component “state of the art” to structure a system in Clojure? Or does anyone have better advice?
I prefer https://github.com/tolitius/mount by far. Simpler, less opinionated, less viral.
could be. I rather don't like component.
@hmaurer check out integrant: https://github.com/weavejester/integrant
😄 well, let me turn my question around then: what would be your preferred way to structure a system to as to make dependency injection / mocking easy?
hmaurer: in that case, build it yourself. once you've got that experience under your belt you'll be able to evaluate mount, component, etc. walk before you run.
@mobileink good point. that has been my general philosophy so far
hmaurer: fwiw all that stuff is interesting and useful, i suppose, but i've not needed to use any of them. mastering clojure is already a tall order, those things are for ninjas (imho). good luck!
@mobileink thanks! any tip on mastering clojure itself?
write code, heh. to me the key is compositionality. it was not obvious to me just what that means when i was starting. i guess i would recommend focussing on the small stuff first - sequuences, laziness, core.async, etc. explore the api - what does juxt do? how to use protocols? etc.
better: forget the app for now, just spend a lot of time in the repl experimenting.
@hmaurer if your app is backed by Datomic, I strongly recommend you use an architecture which reifies the execution context, such as Component or Integrant, which will enable you to implement a 'fork' operation on it. http://vvvvalvalval.github.io/posts/2016-01-03-architecture-datomic-branching-reality.html
@U06GS6P1N I’ve been toying with Integrant since yesterday, and I was considering Component too 🙂
mount 🙂
Integrant 😉
Indeed. Feel free to hit me up with any questions when/if you happen to have any.
@weavejester thanks, will do! Watching your talk on Integrant now
@weavejester quick question on Integrant (I’m half way through the talk): how would I proceed to mock the implementation of one of the services?
For that, I tend to use protocols. In Duct (which builds on Integrant), I call them “boundary protocols”, as they define the I/O boundary of the system.
For example: https://github.com/duct-framework/database.sql/blob/master/src/duct/database/sql.clj
Instead of just returning the database spec, I wrap it in a record.
This allows me to write a protocol against it, and I can mock out the protocol for tests using a tool like Shrubbery.
It’s also useful for having a “local” service that fakes out cloud services or external APIs etc.
Yes that’s exactly the sort of application I’m thinking. Two more projects to look at on my list 😛
Duct builds on top of the ideas of Integrant. It’s the vocabulary to Integrant’s grammar. https://github.com/duct-framework/duct
@weavejester the last web framework I looked into was Pedestal. Do you have any opinions on it?
I haven’t use Pedestal in anger. Some ideas it has are good, but it feels more complex than what I need, and it’s web-focused.
Duct/Integrant are more generic
@weavejester sorry, watching the rest of your talk and reading a bit of Duct’s doc I realise this was a silly question. Duct seems more like an approach to structure an application around Integrant
Duct is a backbone, and all the web/else related logic is handled by other librairies, e.g. Compojure
Right. It provides a template, a bunch of integrant-compatible libraries, and “modules” which are essentially pure functions that transform the Integrant configuration.
Modules provide a way of automating gluing together libraries
@weavejester do you have an example of how a module would work?
An example, or an explanation?
Okay, let me find something quick…
Actually, I wrote a blog post about this, which might be more useful: https://www.booleanknot.com/blog/2017/05/09/advancing-duct.html
@weavejester ah that’s perfect, thank you!
One module is :duct.module/web
. That sets up a handler and middleware, and looks through the configuration for a web server. If it finds one, it connects the handler to the existing web server. If none exist, then it creates a new one.
There’s also: https://www.booleanknot.com/blog/2017/05/29/building-web-services-with-duct.html
The explanation I find myself often using for modules are web sessions that are stored in a SQL database.
how does it “find the web server” in the config? based on the name of popular web servers?
Keyword inheritance. The :duct.server.http/jetty
key is derived from :duct.server/http
.
So to find all the web servers in a Duct config, you just run (ig/find-derived config :duct.server/http)
The idea is to use derive
a little like a dictionary definition or a thesaurus. It allows us to describe what a keyword is, to give it some meaning.
keyword inheritance is just based on the name + namespace of the keywords? e.g. a keyword X inherits from a keyword Y if X’s namespace is Y’s namespace plus its name?
No, it’s its own thing. Any namespaced keyword can derive
from any other.
Namespaced keywords have multiple inheritance in Clojure.
ah, I wasn’t familiar with Clojure’s clojure.core/derive
function. I’ll look into it before bothering you further
It’s not often used, but I feel like it has some important niches
Where does it hold the relationship data between keywords? A global registry? I can’t see it setting metadata on the keywords themselves
A global registry. Derive can be used functionally if you supply a hierarchy, but mostly it’s used globally.
To my mind it’s like vars, or specs.
Oh, I think I get what you meant by a “grammar” then. You use keywords + derive to build a sort of ontology?
Exactly 🙂
Jetty, for instance, is a HTTP server that supports synchronous and asynchronous handlers. So we could write:
(derive :duct.server.http/jetty :duct.server/http)
(derive :duct.server.http/jetty :duct.server.http/async)
(derive :duct.server.http/jetty :duct.server.http/sync)
If Integrant is the “grammar”, Duct is attempting to build a “vocabulary”.
So duct is an ontology for configuration + tools that understand and work with this ontology
Right. That, plus a way of transforming configurations via a preprocessing “prep” stage.
Basically query+transformation
Start with duct/core
Essentially Duct tries to solve the problem of wiring up libraries. Even something seemingly trivial, like storing sessions in a SQL database, requires a lot of wiring.
You need a database migration to create the SQL table to store the sessions
You need a DB connection
Middleware
Session storage protocol
And a worker process to clean up old sessions
Very hard to do with a normal library, but a module could ask questions like “Where do I register a new periodic cleanup job?” “Where do I add new middleware?” “Where can I add a database migration?”
But in the sense that with Duct you establish a language to talk about your configuration, deriving Duct concepts
Right. It borrows a lot from tools like Rails, but whereas Rails used the filesystem as the basis for its convention, Duct uses an immutable data structure.
Instead of following some predefined key structure for your configuration, you just need to follow an ontology/grammar
Exactly 🙂
It’s still early days, but that’s ultimately where Duct is going.
Are there any other projects that you know of which take this approach? Obviously ontologies are quite talked about in the context of the semantic web / knowledge bases / etc, but I think this is the first time I’ve seen it applied to a dev tool
Arachne is another of Duct’s inspirations
It takes a similar approach, but I don’t think it emphasises the semantic ontology angle
And rather than a data structure, it uses a bunch of scripting functions to create an in-memory datomic database, which is then translated into a Component system.
Duct IMO is a little simpler.
@weavejester thanks for taking the time to explain this out! I’ll read up on Duct/Integrant. The theory sounds great; let’s see if it holds up in practice 😛
No problem - let me know what you think
The two blog posts I linked, especially the later API-focused one, should give you a good idea of how to start.
@weavejester I think I am going to have some fun with clojure.core/derive
itself too. Such a neat little function
I wouldn’t say it’s a function that should commonly be used; it’s got niche functionality. But I also think it’s underused. Not many people seem to have heard of it.
But I might be a touch biased
oh, integrant is your project @weavejester
Component is the most used. Integrant is a little new, but YMMV.
yeah, I guess that's the best thing to do: try all 🙂
@weavejester what does integrant do better than mount and component?
component turns your system in to another first class value you can pass around, have multiple copies of, etc
@hiredman we've all done all kinds of stuff, I never liked component. No need to argue with : I like it - I don't.
@hmaurer Compared to Mount, Integrant doesn’t have global state. Compared to Component, Integrant can have dependencies between things that aren’t maps/records.
I feel like Vim and Emacs users can be collectively smug toward users of lesser editors 😉
we know @hiredman doesn't like mount : https://gist.github.com/pandeiro/9a1c8fd431e1b4c78c99
That discussion actually drew my attention to mount, and I like it since. Thanks to @hiredman 🙂
seems bad, like sticking (def state (atom {})) in every namespace would be
isn't this the way I am supposed to do things? still, thanks for pointing me to mount 😉
Here is the problem with global state: you don’t know beforehand how you will want to use your code later. So, inevitably the system grows to the point where your previous “application” is subsumed in some larger structure, and you have to refactor all the global state out of it. Which is a much bigger pain later.
Read the discussion... it's enlightening
https://github.com/hiredman/songs-of-future-past/blob/master/src/com/manigfeald/sofp.clj is an example of something using component, I don't know that it is good
it doesn't have any tests, which is where the lack of global state would really shine
I only say this because I went through this before. Component solves a problem I was having, and has been great ever since.
hmaurer: in that case, build it yourself. once you've got that experience under your belt you'll be able to evaluate mount, component, etc. walk before you run.
I would say it is a classic easy/simple split, component is way simpler (https://gist.github.com/hiredman/075b45eaeb01e4b526ce6f8854685487 implements all the functionality of component in 30 lines, but lacks refinement), but mount is going to be easier because using globals is always easier for a few months
Global state is easy now. You don’t have to learn anything and it doesn’t take work to set up
Going the 'simpler' and 'easier' way, are we?
Right, it may be easier, and if it solves the problem, why make it more difficult? - What is the problem you want to solve? This may be the first question to @hmaurer
Having a viral framework, I never liked that. Component is more a framework - I will be screwed as well
@kurt-o-sys Agreed. @hmaurer If you are trying to learn Clojure, just use a global atom
Have used enough frameworks, they always hit you in the face as well... and hard 🙂
I don’t really want to use a global state atom. While I am new to clojure I’ve been interested in functional programming for quite a while; if there are ways to avoid global state I rather do so
@kurt-o-sys We disagree in general, it is okay
fwiw, there is a yurt for mount https://github.com/tolitius/yurt
Multiple brand new local Yurts with components can be created and passed down to the application / REPL to be used simultaneously in the same Clojure runtime for fun and profit.
Exactly - check what works out for you.
integrant seems ok, I haven't looked at in depth, but it looks like it uses multimethods for dispatch, which I've done with component before, but is annoying if you want to create an anonymous sort of mock of something in a test
generally multi methods are also global, created with defmulti (technically you could create an anonymous one, but I have never actually seen anyone do that, and it would kind of defeat the purpose)
so if you want to create a mock, you have to use defmethod which effects the global multimethod
if you use protocols you can generally reify the protocol inline in the test, and outside of the test there is no effect
each multi method has a dispatch table for behaviors, so say you want stick in a :my-special-mock, well that goes in the table associated with the multimethod, less likely to cause harm than actually replacing the dispatch, but still kind of a drag
(following the thread with @weavejester) it looks like he uses protocols for some stuff, so maybe that is less of a problem
at my last job we used component but had a team member who hated defrecords, so we more or less retro-fitted component to use multimethods instead (which is pretty easy to do) which is where my experience with the pain of mocking that kind of thing comes from
I mean, you could set the global state atom before runing your tests with the mock dependency
@hmaurer the beating heart of mount https://github.com/tolitius/mount/blob/master/src/mount/core.cljc#L12-L16
@spangler right... one prefers frameworks, the other doesn't. Fine with me 🙂.
Global state atom can be bad, of course. That's why mount manages it, so you don't have to.
I would call component more of a pattern. It is not doing much for you, most of it is explicit
Anyway, we'll never agree on that part - that discussion was pretty clear about how it's not that different from component (passing in state everywhere)
@spangler right... it does not do much... I like that 🙂
spangler: the functional way to do things is to have a clear separation between IO and business logic
you should never be passing resources into your business logic code, and if you're not doing that then this whole discussion is moot
yes, that's a dogmatic reason. I prefer pragmatism in many cases.
Is there a good blog/book/resource somewhere on clojure ways of doing things? (i am already familiar with “programming clojure” and “the joy of clojure”)
I've heard good things of http://www.braveclojure.com/
heh, I went to Mount after a couple years of Component straitjacket. In a bigger team I’d use Component though 🙂
clojure does have state management constructs because you don't pass everything in. There's a reason why clojure isn't dogmatic on FP.
https://leanpub.com/elementsofclojure isn’t finished but has some interesting ideas. It’s not a general purpose programming book, though. I think the first chapter on naming is free.
As someone who initially went down a Mount-like path when building a system and has since switched to Component, I'll "vote" for @hiredman's position here that global state is a terrible idea.
It is a terrible idea in general, right. The dogmatic reason. I can understand it.
But having a viral framework has hit me many times, including component.
We're still feeling the pain of the global state approach as we piecemeal migrate away from it (by using Component instead but having its start
/`stop` functions keep the legacy global state in sync until we can replace it).
That's why I prefer mount - pragmatism, and as long as you manage state, it's ok.
@hmaurer as you can tell from this thread, there is always More Than One Way to Do It. Master the basics first, then you'll see just how true that is!
lol true!
Component is not a "viral framework". Functions need to be based stuff as arguments, not reach out to global state for it.
Haha, I am glad it sparked a discussion though. It would have been quite boring if someone linked me a lib and 5 people thumbed up
Once you start using component, it's hard not to use it everywhere in your code base.
So well, I call that viral (inside the code base).
@samcf I wouldn't be surprised if that is the primary split front end / back end on mount / component
You do it 'the component way' or not. There's nothing in between... using component.
I think it is more about providing each part of your system the minimal amount of information it needs to do its job. That way you can decompose and rearrange it later. Which at some point will become very important.
can you do that with component? don’t you pass the whole context around usually? or do you intentionally strip some keys from the context?
You only give each component access to the components it asks for (basically you declare a dependency). Your component only ever sees the state that it asks for
Hate to make an argument by authority, but it's really not insignificant that some of the most veteran clojure developers (@hiredman / @seancorfield ) favor component. Personally I think mount teaches new clojure users bad habits.
ghadi: I'm a pretty experienced Clojure developer myself last I checked, and I'm firmly in favor of mount
personally, I think that most component based code isn't proper FP because resources are often accessed within business logic
Same with mount, honestly. Check that discussion I posted above, it's really all not that different.
@kurt-o-sys any given function just needs to be passed what it needs -- not the whole system -- and that's just functional cleanliness. You initialize all your resources at application startup, put them in a map, pass the relevant bits down the call chain. Component just formalizes that and manages dependencies for you.
Same with mount, really, if you like so.
@ghadi - even though I prefer Mount - I agree 🙂 Mount should only be allowed once you spent a year with Component
Perhaps the pain level with Mount doesn't show up until you have a large enough system? Just like global state only causes you pain after a certain point?
The mount
readme describing component
is disingenuous https://github.com/tolitius/mount/blob/master/doc/differences-from-component.md#differences-from-component
@seancorfield it’s probably true. In my case I’ve avoided building large applications (modules). I just have many of them.
We have about 60,000 lines of Clojure right now...
LOC - so?
anyway, as said before, one prefers dogmatic, other prefer pragmatism. One prefers frameworks, other prefer libs/patterns. One likes pink, the other blue. Whatever 🙂
(some ran away from component, others ran away from mount, so maybe we all should try integrant and decide it's not a silver bullet either 🙂 )
lol - because someone has an opinion? Nice.
Like I say, hard lessons learned building a system the "easy" way with Mount-like managed state, instead of the "simple" way with Component-like managed dependencies.
zzzzz. clojurians having another vigorous debate/ disagreement without calling each other names. wtf is wrong with you people?! 😉
@mobileink I dunno, can't we just hate each other's software without hating each other?
anyway that was a lame attempt at irony - the clojure community is extraordinarily irenic. (look, i made a pun! whee!)
@mobileink Online peace is a noble goal
Interesting discussion. At my work we use mount (mount.lite actually), but I always liked the IoC-pattern in OO, which I miss doing mount. It is nice that you do not have to pass state as parameters everywhere, and if you want to mock a state you can substitute state for a mock implementation. BUT I miss having a composition root responsible for composing modules, and seeing that you have to refactor because a module takes too many constructor parameters.
I find initializing state in one file like below (where a timbre appender is added) quite nice. I don’t know what the equivalent would be for a Component application. Logging is a cross-cutting concern, so perhaps not really something you inject.
(ns blabla.logging
"Logging setup for blabla."
(:require [blabla.config :refer [config]]
[clojurewerkz.elastisch.native :as es]
[mount.lite :refer [defstate]]
[taoensso.timbre :as log]
[blabla.logging.elastic :as log-es]))
(log/handle-uncaught-jvm-exceptions!)
(defstate log-level
:start (let [current-level (:level log/*config*)]
(log/set-level! (:log-level config))
current-level))
(defstate blacklist
:start (when-not (:development config)
(log/swap-config! update :ns-blacklist conj "io.pedestal.http.impl.*"))
:stop (log/swap-config! update :ns-blacklist #(vec (remove #{"io.pedestal.http.impl.*"} %))))
(defstate elastic-client
:start (let [{:keys [elastic-native-pair elastic-cluster-name]} config]
(when elastic-native-pair
(es/connect [elastic-native-pair] {"cluster.name" elastic-cluster-name})))
:stop (when elastic-client
(.close elastic-client)))
(defstate elastic-appender
:start (when elastic-client
(let [options {:base-doc {:app "blabla"
:k8s {:namespace (:kubernetes-namespace config)}}
:mapping log-es/mapping}]
(log-es/add-elastic-appender! elastic-client "hd-logging" options)))
:stop (log-es/remove-elastic-appender!))
if I'm understanding your example correctly, component also wires everything together in one file like you prefer
generally you have a 'system' or whatever ns with a big map that looks like
(defn example-system [config-options]
(let [{:keys [host port]} config-options]
(component/system-map
:db (new-database host port)
:scheduler (new-scheduler)
:app (component/using
(example-component config-options)
{:database :db
:scheduler :scheduler}))))
Yes that is what I am missing from Mount 🙂
Your example-system
is a Composition Root
@hmaurer when you looking at mount, besides the docs, these little example apps may help you to see how it could be used: https://github.com/tolitius/stater
If you want to evaluate mount/component/integrant, I’d recommend starting by just reading them
they’re all in the 300-500 LoC range
I mean there are things you can only learn by using them in a large project for a long time, or talking to someone who has, but I think reading them is a reasonable starting point
Yep that’s what I intend to do now that I’ve seen they’re pretty concise. Thanks for the advice!
Aaand don’t forget the new-new kid on the block: deferst
:
https://github.com/employeerepublic/deferst
@weavejester Out of curiosity, why did you make the choice of using multimethods for Integrant?
hmaurer: A lot of the time stubbing isn’t necessary; only for keys that connect to an external I/O source. Also, polymorphism is more convenient than a lookup table, and it encourages using the same key for the same behaviour.
It’s important for Duct/Integrant’s design around the idea of a vocabulary that keywords have the same meaning.
Also, it’s more useful to stub the I/O source you’re passing around than the multimethod. e.g. you might pass your database to another function as an argument. You need to be able to stub that directly.
Hence records and protocols.
@weavejester I see; I think I am misunderstanding something. Let’s put it into context. In some cases I might want to use a different config during testing, which is very easy to do with Integrant (just load a different edn or merge some keys in the loaded config before calling ig/init
). However, in other cases I might want to stub the implementation. For example, I might be using Redis as a kv store, but during testing I might want to stub it for my own in-memory version.
In that case, I wouldn’t have a :redis
key in my configuration, but likely a :cache
key, or :kv-store
key, which obeys a kv-store protocol
In my default, production implementation, the multimethod would return a Redis connector wrapped inside a record obeying the kv-store protocol
However I am still unclear as to how I would swap that implementation for my own in-memory version during testing
I suspect this is due to my lack of knowledge on Clojure itself, rather than Integrant/Duct…
I’d love to hear more about this as well, I’m going to try out Integrant in a project in the coming weeks and I’ve been wondering the same thing
So there are two ways to do this.
First, if you’re testing a single key, then you can use init-key
directly and pass the stubbed/mocked connections.
For example, say you have a key :foo.handler/user
that takes a database option. You could test it with:
(ig/init-key :foo.handler/user {:db (->StubbedDatabase)})
Because the database isn’t accessed directly, but via protocol methods, we can create a stubbed or mocked version with the same interface. Shrubbery is a test tool that streamlines this process.
If you’re testing the configuration in a wider context, then you can take advantage of keyword inheritance.
For example, say you had a configuration like:
{:duct.database.sql/hikaricp
{:jdbc-url ...}
:foo.handler/user
{:db #ig/ref :duct.database.sql/hikaricp}}
One feature of Integrant is that you can reference derived keys, so you could write the above as:
{:duct.database.sql/hikaricp
{:jdbc-url ...}
:foo.handler/user
{:db #ig/ref :duct.database/sql}}
@weavejester oh, so in that case you wouldn’t swap any implementation, you would simply instruct the system to use a different implementation based on the config (different derived key)
So if you want to stub out the key directly, then change the database key to a fake one that derives from the same base:
(derive :duct.database.sql/fake :duct.database/sql)
Thank you, I’ll try both of those approaches 🙂 I had a follow up question but you answered it. It was going to be: “in your talk you mention how you can instantiate two systems with different configurations, but how can I instantiate two systems with different implementations?”
Right: you could update the configuration to replace the real database with a fake one:
{:duct.database.sql/fake
{:jdbc-url ...}
:foo.handler/user
{:db #ig/ref :duct.database/sql}}
From what I gather the answer to this would be “you only have one implementation per keyword; you just use a different config”
Right. Just take the base config and alter it with assoc
. Or use duct.core/merge-configs
to merge in new options.
It effectively amounts to the same thing.
You could also use with-redefs
to redefine the init-key
multimethod, but since that’s not thread-safe I’d advise avoiding that route.
Thanks for the explanation! So the gist is that I was finding it annoying to swap implementation due to multimethods, but that’s intentional because under your design you should not swap implementations
Right. I mean, in theory it might be good for testing, but in practice I think it makes more sense to substitute keys in the configuration, rather than make the config->implementation bridge dynamic in some fashion
It also makes it explicit where you’re stubbing/mocking.
@weavejester thanks for taking the time to explain this out. Once I understand Integrant/Duct better I’ll try to give back by writing some doc 🙂
Thanks! But if you like, just some feedback once you’ve gotten to use it a little would be useful. The more use-cases I know about, the more useful I can make the library.
Just adding stuff from this conversation to the readme / wiki would be helpful, because as far as I could see the questions are not addressed on Github at the moment
@U3L6TFEJF yep that would be great
Point taken, I’ll put together something for the Duct docs.
@weavejester are you using Duct in production?
The Integrant version? Not yet, or at least it's not running in production. I am working on an app that will be running in production in a couple of months, though.
@weavejester Another question… What would be the preferred way to reference some environment variables in the config? It seems to be like an EDN tag would be neat, e.g. #ig/env "DATABASE_PASSWORD"
. Do you have an existing option? I guess I could merge in the relevant config from env vars
An edn tag would be neat, and in Duct it’s called #duct/env
.
If you put that in your main config.edn
, then you can always override them in dev.edn
or local.edn
.
Oh I hadn’t even realised Duct had this functionality; I am focussing on Integrant’s doc atm. Brilliant
Certain modules also have defaults, e.g. :duct.module/sql
uses JDBC_DATABASE_URL
and DATABASE_URL
automatically, and :duct.module/web
uses PORT
for the port number.
@weavejester on the topic of loading env vars, maybe it would be neat if one was allowed to extended integrant with custom readers?
Take a look at read-config
Yep 🙂
Though in duct someone pointed out that the readers don't persist though includes, so that needs to be fixed.
@weavejester what do you mean by that?
Duct adds a :duct.core/include key that allows a config to pull in other configs from the classpath in the "prep" stage
But prep doesn't know about the custom readers.
I'm also considering better ways of performing an include.
@weavejester another question… Is there a way to “inherit” from a config? For example, let’s say I have a “database” config, and I want to create two services whose configs are slight twist over the general “database” config
except that you replicate the config (port and handle) under both keys, whereas I would like to share a config block and override some bits
Not currently, unless you modify the config with either a module or directly after it’s loaded.
Yes, though depending on your use case, it may or may not be a good idea
It seems to me (as a clojure beginner) that it introduces a form of global that makes stubbing a bit harder
e.g. wouldn’t it be nicer to pass a second argument to ig/init
: a map from keys to implementations?
I am sure you had a good reason for using multimethods and not the approach I’m suggesting, but I would like to understand it
@weavejester is it related to hot-reloading?
and if so, should tooling not assume that there will be a namespace form at the top?
never really know where to go to find the hard requirements on these things so thanks for the info