Fork me on GitHub
#clojure
<
2017-08-26
>
bradford03:08:54

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

the2bears04:08:10

@bradford what do those Ruby functions do?

the2bears04:08:42

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

seancorfield04:08:58

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

seancorfield04:08:14

H/`h` is for hex

seancorfield04:08:08

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

hmaurer12:08:39

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

hmaurer13:08:32

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)

hmaurer13:08:51

It seems a bit like an anti-pattern though

tatut13:08:13

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

hmaurer13:08:59

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 ?

tatut13:08:25

my first impulse is to just go with maps

hmaurer13:08:26

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

sundarj13:08:58

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'

tatut13:08:33

a common context

tatut13:08:54

I think both maps and records would be suitable

qqq13:08:59

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)

hmaurer13:08:24

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?

tatut13:08:12

I would try to avoid it, if possible

hmaurer13:08:18

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

tatut13:08:43

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

hmaurer13:08:58

Ah, timbre has a dynamic var

tatut13:08:01

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

tatut13:08:53

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

tatut13:08:14

passing parameters is better

hmaurer13:08:39

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

tatut13:08:04

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

sundarj13:08:51

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

tatut13:08:31

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

tatut13:08:43

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

schmee13:08:58

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

schmee13:08:49

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: http://metametadata.github.io/clj-fakes/user-guide/

tatut13:08:44

why two namespaces? why not arities?

tatut13:08:17

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

tatut13:08:38

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

sundarj13:08:43

some would consider that too implicit, i imagine

tatut13:08:07

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

tbaldridge13:08:55

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

tbaldridge13:08:13

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

tbaldridge13:08:39

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.

tbaldridge13:08:59

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.

tbaldridge13:08:31

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

metametadata14:08:07

@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

tatut14:08:34

that’s a good point

hmaurer14:08:54

@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 https://github.com/zcaudate/adi

hmaurer14:08:21

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

tbaldridge14:08:23

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.

tbaldridge14:08:10

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.

hmaurer14:08:35

@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

hmaurer14:08:25

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

hmaurer14:08:38

if that makes any sense

tbaldridge15:08:18

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

tbaldridge15:08:50

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.

hmaurer15:08:54

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

hmaurer15:08:43

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

noisesmith15:08:51

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

seancorfield18:08:39

@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!