Fork me on GitHub

What's the clj equivalent of Ruby's [h].pack('H*') and ('h*') ?


@bradford what do those Ruby functions do?


Asking so no one has to look up Ruby to try and answer...


pack() looks like a formatting method on an array/string...


H/`h` is for hex


I'm assuming you want to take an array of bytes (`[B` in Java-speak) and display it as a Hex value @bradford ?


Hi! Question: is it considered “good practice” to use protocols with a 1-1 correspondence with records. By that I mean the following: I am familiar with protocols as a way to abstract behaviour that various data structures may implement. e.g. Seq, etc. However, I have been tempted to use protocols as a way to define “class-like” things which have state + methods


For example, I have been tempted to define a DatabaseBoundary protocol together with a record which holds a database connection. The protocol would have a couple of methods to interact with the DB (e.g. select, write, etc)


It seems a bit like an anti-pattern though


if you only ever have 1 implementation of the protocol, then you could just make functions that operate on the record?


mmh fair enough. Follow-up question: when would you prefer a record over a plain old map if the record does not need to implement any protocol @tatut ?


my first impulse is to just go with maps


basically, my use-case is that I want to define some functions to operate on a database. These functions all need to take some configuration data which defines, amongst other things, authorisation filters


can't seem to find the threads now, but this has been asked before - and the consensus seemed to be 'always try maps first, then use records if/when that breaks down'


a common context


I think both maps and records would be suitable


are there any real time / fps games scriptable in clojure ? (I'm looking to setup a clojure botting contest, and need to find a game)


ok, thanks! Last question: if my “database managment” code is a library, and some bits (e.g. authorisation filters) are the same for all users and throughout the lifetime of the applications, it could be a bit annoyiing/error-prone to pass it as argument to every function of the library. Is it acceptable in this case to store the config in an atom (managed by the lib) and expose a function like my-lib/configure! config-map to set it?


I would try to avoid it, if possible


I haven’t checked the code of timbre but it looks like they have timbre/merge-config!, which I assume works this way


Often always passing the same parameters can be tedious, but it pays off in better testability and ability to reason about what the code does


Ah, timbre has a dynamic var


having an atom you read, couples the code to some other point in time where you set it


and if you have a global atom, then you can only have one configuration at any given point in time


passing parameters is better


so I guess a dynamic var is a better solution than a global atom, but probably worse than passing down a param?


but, there is a middle ground where you have 2 arities, one passing in the context and one that reads the atom


users of the lib could always partial apply functions if they don't want to pass the config all the time


yeah, like (def do-some-lib-thing (partial lib/so-some-lib-thing context)) and act like it isn’t there


but, like always, it depends… sometimes there really is naturally a single global context and the convenience is worth it


yeah, imagine having to pass System/out and a map of formatting options to every println call, that would be very, very tedious


I think clj-fakes has a cool solution. It has two namespaces with the same functions: one where there is an implicit context, and one where you pass the context explicitly:


why two namespaces? why not arities?


(defn foo ([x] (foo @current-context x)) ([context x] ...the code...))) approach is something I’ve used


so you can pass the context if you want, otherwise it will read the global atom


some would consider that too implicit, i imagine


and for things like a database connection, always pass that in and be explicit


@hmaurer I wouldn't take timbre as an example in this case. Most logging libraries I've used have some sort of global state


the idea being you very rarely want to log to two places at once in the same code.


DBs on the other hand..Almost every project I've worked on in the past 5 years has had at least one DB. most have 2-3.


So I normalyl recommend putting "state" in a map, and when a function only needs one bit of the state, pass just the state it needs.


(get-user db user-ud) (start-server {:db db :port 4424})


@tatut "arities" approach is cumbersome when optional args are needed:

user=> (defn foo
  #_=>   ([x & y])
  #_=>   ([context x & y]))

java.lang.RuntimeException: Can't have more than 1 variadic overload


that’s a good point


@tbaldridge thanks! I wasn’t thinking of storing the db connection in that global state though. Roughly speaking I am writing a library to do some things on top of Datomic (data validation, etc), a bit like


The global state would be configuration of the application schema (a sort of extension of the Datomic schema)


I'd highly recommend keeping that in a function parameter then. Especially considering that almost all of Datomic can change at run-time and yet the old state of the DB will still exist.


Having things in dynamic vars or in some global atom makes it really hard to have multiple versions of something. And with Datomic it's very common to have a few dozen de-reffed dbs lying around.


@tbaldridge one of my worries is that one of the configuration option is a set of “access polices” which define what can and cannot be done on the data. If I pass this around as a function parameter on every call it seems a bit harder to ensure that the same access policies are passed everywhere


In my use-case I am only planning to use my data-access library on the latest state of the db; I don’t expect it to work on old dbs/schemas


if that makes any sense


@hmaurer but all DBs are "old" there is no such thing as a "current" database.


But be that as it may, I don't think I've ever passed this stuff by param and regretted it. Passing via dynamic var or global config has been a pain more times than I can count.


@tbaldridge right; I should have said “latest schema”


Ok, I’ll give the param version a go then. Thank you!


also, the param version is the flexible one - if you code for that, you can adapt to all the others (global mutables, dynamic vars, etc.) trivially if it's actually needed, but the others are not nearly as simple to adapt


@hmaurer I'll second @tbaldridge on this: when we started building our Clojure libraries at work back in 2011, we used global atoms, delays, and dynamic vars because it seem "so much more convenient" than passing all that stuff through parameters. We are still deeply regretting that poor life choice, six years later. Save yourself the pain! Trust me!