This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-11-18
Channels
- # beginners (84)
- # boot (21)
- # cider (12)
- # cljs-dev (13)
- # cljsrn (3)
- # clojure (169)
- # clojure-dusseldorf (2)
- # clojure-gamedev (6)
- # clojure-germany (1)
- # clojure-russia (15)
- # clojure-serbia (4)
- # clojure-spec (16)
- # clojure-uk (4)
- # clojurescript (51)
- # core-async (1)
- # datomic (23)
- # emacs (16)
- # figwheel (1)
- # fulcro (60)
- # hoplon (8)
- # lein-figwheel (3)
- # leiningen (1)
- # luminus (4)
- # lumo (43)
- # off-topic (4)
- # re-frame (17)
- # rum (37)
- # shadow-cljs (21)
- # test-check (13)
- # vim (14)
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`."
[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`."
[event]
(get event :time))
(defn get-start-time
"Return `start-time` from an `event`."
[event]
((get-time event) 0))
(defn get-start-time
"Return `end-time` from an `event`."
[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.
but @noisesmith, isn’t creating constructors, selectors and domain-operations something good?
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
so should I deliberately access any piece of data in a map created somewhere every time I need it?
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!
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
think*
@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
Didn’t think about it that way
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
I will watch some now
thanks a lot @noisesmith
RE: Defrecords I was reading through B&T https://www.braveclojure.com/multimethods-records-protocols/ 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
Thanks!
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 "tic_tac_toe.game.Game@54627eec"
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. https://clojuredocs.org/search?q=pr 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"))
"{}\"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"))
"{}hello"
+user=> (format "%s%s" {} "hello")
"{}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?
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
yes, the fn macro
which is a doozy
(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))
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)
body (if pre
(concat (map (fn* [c] `(assert ~c)) pre)
body)
body)]
(maybe-destructured params body)))
new-sigs (map psig sigs)]
(with-meta
(if name
(list* 'fn* name new-sigs)
(cons 'fn* new-sigs))
(meta &form))))
nil
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[java.net.URL 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)