Fork me on GitHub

Does anyone here use proto repl with atom? I can't figure out how to turn it on. (derp)


@derpocious yes, how are you trying to turn it on?


@derpocious Did you follow Jason Gilman's "opinionated setup"? That's a really good place to start (although I recommend disabling the three auto-refresh options that it says enable -- at least until you're building everything with Stuart Sierra's Component 🙂 )


I go to package -> proto repl -> start repl


but the repl is always empty


RE: Naming Conventions After reading through Elements of Clojure's chapter on naming and several other blog posts, I am working on a small app that I am hoping will clarify how names could work. The meat of this is a little longer, so I am going to start a thread with the code examples. Any input would be amazing 🙂


(defn event
  "Return a data type of `event`."
  [name start-time end-time]
    :name name
    :time (event-time start-time end-time)})

(defn- event-time
  "Return a data type of `event-time`.
  x is the `start-time` and `y` is the `end-time`.  Both of these are `time` units
  which must come in this format:  hour + .25 increments.  For example, 9 9.25
  9.50 9.75 are all valid `time` units."
  [x y]
  [start-time end-time])

(defn get-event-name
  "Return `name` from the `event` provided.
  Must only be passed a data type of `event`."
  (get event :name))

(defn get-event-time
  "Return `time` from an `event`.
  `time` is a vector which includes `start-time` and the `end-time`."
  (get event :time))

(defn get-start-time
  "Return `start-time` from an `event`."
  ((get-time event) 0))

(defn get-start-time
  "Return `end-time` from an `event`."
  ((get-time event) 1))
The questions I am left asking are: 1. should get-event-time even be there. The name is really just describing the implementation. Do I even need this helper? 2. Maybe question could be solved if I don't use the ver get e.g. rename it to event-name - problem here is that clashes with the function event-name. 3. I could change event-name to something like start-time+end-time and than I could have a getter function function called event-time Clarify point: I would expect people to use the this module like this: event/event, event/get-event-name etc. I could shorten these to event/name - which is intersting because if I am looking at event/name it looks like it is creating a data tpye and pulling something from another data type. As you can see this is a very simple example, and I want to use this as a template for sensible defaults so I can spend less time, when possible thinking on these things.


generally we shouldn't have getters in clojure


you can make exceptions with things that describe mutable or side effecting stuff, but I don't think that's the case here


That will clear this up quite a bit then. So the recommended approach would be to just do ((get-time event) 0)) whenever it is needed and not make a function responsible for it


or store the event time as a hash-map instead of a vector, so the key would be something intelligible


What would the reasoning be for avoiding getters - as a default - because I imagine there are scenarios where they could be useful - not this one of course


getters add complexity by tying code to the implementation and domain


you can't use get-in or update-in with getters


Shoot, thats really true. Never thought of it like that.

Lucas Barbosa18:11:03

but @noisesmith, isn’t creating constructors, selectors and domain-operations something good?

Lucas Barbosa18:11:16

to establish an abstraction barrier


idiomatic clojure avoids these things in most cases. the book tkjone is talking about, "elements of clojure" addresses these sorts of design trade offs in later chapters


it's the lisp way to prefer 1 data type and 100 functions, instead of 10 data types and 10 functions for each

Lucas Barbosa18:11:17

so should I deliberately access any piece of data in a map created somewhere every time I need it?

Lucas Barbosa18:11:38

like (:key map)


that's how most clojure is written, and how most clojure libraries are meant to be used


there are places where abstraction boundaries help a lot!

Lucas Barbosa18:11:22

I see… it is hurting me to thing about the time when the implementation of that data structure changes


but you probably want that to be inside the implementation, and still return vanilla clojure data


@U6UNHRZ99 if you return it to a client in clojure, it's not an implemention detail, it's the domain range of your function


you can change how you generate it, or add keys that were not there previously


but once you return it, they own it

Lucas Barbosa18:11:58

Didn’t think about it that way

Lucas Barbosa18:11:08

makes sense :thinking_face:


zach tellman is more articulate about these design considerations than I am, I recommend finding his recent talks on youtube where he presents the material from the later chapters of his book

Lucas Barbosa18:11:59

I will watch some now


RE: Defrecords I was reading through B&T and want to confirm that if I have code like this:

(defn event
  "Return a data type of `event`."
  [name start-time end-time]
    :name name
    :time { :start-time end-time
            :end-time start-time}})
Where an event is a data type in the domain I am working on that using a defrecord is the better choice as opposed to just building a map like I am doing above. CB&T would suggest this to be the case.


why would you use a record? just because it's a domain type isn't enough


Thats pretty much the only reason I can think of right now for the above scenario - really, trying to understand when they are used


typically a record is used when you need an immutable map and you also need to implement a protocol


(or interface, or more than one)


> when you need an immutable map I understand the protocol part, but what do you mean by the above? Isn't most everything immutable?


hash maps don't implement your protocols / interfaces


if by "everything" you mean all instances of () {} #{} {} [] - sure


it's not many types actually, we just re-use them a lot


if we didn't have defrecord, and you needed to make something implementing someone else's interface or protocol, and also wanted to use clojure's immutable data functions, you would have to do a lot of work to implement something that fit all those requirements


I'm stumped trying to get the same result from (pr (str g)) that I get from (pr g), where g is a record. From the docs, I'd have expected (str g) to expand to the contents of the record, but I get "" instead. And I'm having a heck of a time trying to find the source for pr and friends, to figure out what they invoke to get those contents. Any pointers?


(It would be nice if e.g. actually explained how stringizing works....)


The only real reason I want str here is to avoid spaces in between arguments. I could use printf, but that has the same issue -- %s expands to the obscure object id, not the contents, of the object.


pr tries to print in a form that is readable


so if you give it a string, it prints something that will read as a string


if you want to combine without spaces, you can use pr-str with format


+user=> (format "%s%s" (pr-str {}) (pr-str "hello"))
or use str instead of pr-str to get the normal stringify (or equivalently let it implicitly use toString which is what str inokes)
+user=> (format "%s%s" (str {}) (str "hello"))
+user=> (format "%s%s" {} "hello")


@noisesmith -- ah, now I understand that pr-str is what I want, not str. They do different things for an instance of a record; pr-str prints the contents, str does not. Thanks!


Next q: is there a canonical way to handle frequently used pre/post-conditions so a) I don't have to keep writing out (say) four postconditions, and b) I still get the helpful message, upon violation, as to exactly which postcondition failed?


(I tried the macro approach, but of course that doesn't work, since a macro invocation can return only a single Sexpr....)


you can fix that with a data structure, the bigger problem is that macros only see data that is directly in the source, it can't know anything about the runtime data


@noisesmith -- do you have an example? I don't understand....


macros don't know anything about your data if it isn't literally in the source code (or put there by another macro first)


they run while compiling


Still not clear. What I'm looking for is a way to convert something like this, that occurs multiple times in my source code: :post [(cond1 ...) (cond2 ...) (cond3 ...) (cond4 ...)] to: :post [(common-code ...)] Now, if common-code is a function that returns the (and ...)of cond1 through cond2, it should work. But if the postcondition fails, the diagnostic will then only tell me that common-code returned false -- whereas, before, it'd tell me which of condN returned false. Does that make any sense?


(Typo: meant "...returns the (and ...) of cond1 through cond4.)


I know Lisp macros (well, at least Clojure macros) can only return a single sexpr -- so I'm just wondering whether there's any better way to tackle this (rather minor) issue.


OK - to make that work as a macro, you need to either use do (which gives you the same problem the common-code function had) or make a macro that generates an fn or defn form


(Maybe I'll get around to studying the Clojure source code for things like :post and come up with something myself?)


because you are generating input to a macro


Right, I assume some macro is getting :post and processing it somehow.


yes, the fn macro


which is a doozy


I can imagine!!


(defmacro fn
  "params => positional-params* , or positional-params* & next-param
  positional-param => binding-form
  next-param => binding-form
  name => symbol

  Defines a function"
  {:added "1.0", :special-form true,
   :forms '[(fn name? [params* ] exprs*) (fn name? ([params* ] exprs*)+)]}
  [& sigs]
    (let [name (if (symbol? (first sigs)) (first sigs) nil)
          sigs (if name (next sigs) sigs)
          sigs (if (vector? (first sigs)) 
                 (list sigs) 
                 (if (seq? (first sigs))
                   ;; Assume single arity syntax
                   (throw (IllegalArgumentException. 
                            (if (seq sigs)
                              (str "Parameter declaration " 
                                   (first sigs)
                                   " should be a vector")
                              (str "Parameter declaration missing"))))))
          psig (fn* [sig]
                 ;; Ensure correct type before destructuring sig
                 (when (not (seq? sig))
                   (throw (IllegalArgumentException.
                            (str "Invalid signature " sig
                                 " should be a list"))))
                 (let [[params & body] sig
                       _ (when (not (vector? params))
                           (throw (IllegalArgumentException. 
                                    (if (seq? (first sigs))
                                      (str "Parameter declaration " params
                                           " should be a vector")
                                      (str "Invalid signature " sig
                                           " should be a list")))))
                       conds (when (and (next body) (map? (first body))) 
                                           (first body))
                       body (if conds (next body) body)
                       conds (or conds (meta params))
                       pre (:pre conds)
                       post (:post conds)                       
                       body (if post
                              `((let [~'% ~(if (< 1 (count body)) 
                                            `(do ~@body) 
                                            (first body))]
                                 ~@(map (fn* [c] `(assert ~c)) post)
                       body (if pre
                              (concat (map (fn* [c] `(assert ~c)) pre) 
                   (maybe-destructured params body)))
          new-sigs (map psig sigs)]
        (if name
          (list* 'fn* name new-sigs)
          (cons 'fn* new-sigs))
        (meta &form))))


So it's "the fn macro" in more than one sense of "fn". 😉


Oh, well, that looks straightforward enough! 😉


Where does the code for that live, BTW? Now that I'm on my Windows 7 machine, instead of my Mac, I can't easily go to the source code (my CIDER environment tries to invoke unzip, which isn't installed ATM).


you can use clojure.repl/source to see it, and you can look at (System/getProperty "java.class.path") to see the jars you are using - the clojure source is in one of them


a jar is a zip file with a specific set of contents, which is why cider wants to use unzip


a more helpful trick:

+user=> ( "clojure/core.clj")
#object[ 0x47a5b70d "jar:file:/home/justin/bin/bench!/clojure/core.clj"]
the resource prints the path to the jar plus the path inside the jar, if it can find the resource


(I make an uberjar called "bench" for raw benchmarking containing clojure.jar plus my most commonly used libraries)


Okay, thanks! I had actually made a note about (clojure.repl/source map) awhile back, while reading "The Joy of Closure", and forgot I'd done that....


unless you interfere, clojure will refer clojure.repl in your starting namespace, so (source foo) will work


Okay. BTW, I can list the source for pr, but that source code calls pr-on, and this doesn't seem to work:

> (clojure.repl/source pr-on)
Source not found
;; => nil


it's private, it can't be seen unqualified from your ns


try (source clojure.core/pr-on)


Ah, of course, thanks again!