This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-07-10
Channels
- # announcements (3)
- # architecture (54)
- # babashka (11)
- # beginners (12)
- # calva (5)
- # clj-on-windows (1)
- # cljdoc (2)
- # cljs-dev (1)
- # cljsrn (6)
- # clojure (130)
- # clojure-europe (8)
- # clojurescript (21)
- # conjure (23)
- # core-async (4)
- # datomic (7)
- # depstar (77)
- # events (1)
- # fulcro (27)
- # lsp (88)
- # malli (5)
- # meander (1)
- # off-topic (4)
- # pathom (43)
- # polylith (39)
- # re-frame (9)
- # shadow-cljs (14)
- # timbre (3)
- # tools-deps (53)
Hello, I am struggling with firing up named tools described in: https://clojure.org/reference/deps_and_cli#_using_named_tools
$ clj -Ttool list
-T is no longer supported, use -A with repl, -M for main, or -X for exec
For me this looks like a version problem. I am using
$ clojure -version
Clojure CLI version 1.10.3.855
that is the same version from the https://clojure.org/guides/getting_started. Not sure about the repair version, but according to github tags, 1.10.3 is the latest stable, but there is a 1.11.0-alpha1. Is named tools available in alpha only? If so, how can I get the proper download link? For
listing is not enabled, so I could not figure it out on my own.
Can someone help?Try the download link for 1.10.3.905 - that's the version mentioned in the original announcement.
It's also worth noting that 1.10.3 and 1.11.0-alpha1 are the versions of Clojure (the language) itself, not the CLI. The CLI has it's own versions -- http://clojure.org/releases/tools -- where the prefix indicates which version of Clojure it will use by default if you don't override it. But you can use any version of Clojure (from 1.0 all the way up to the latest 1.11 alpha) with the CLI.
Assuming I've got a code like the following:
(def ch (chan))
(go-loop []
(let [val (<! ch)]
(prn val))
(recur))
what is a good graceful way to kill both the channel & the go-loop?
that's it? wow! Thanks! I thought the go-loop part will be more complicated than that!! Thanks again!
@abhishek.mazy make sure to always check the value you take out of the channel
If it's nil
then don't recur
cool!! Will do!
Also, depending on what you send, make sure to distinguish between when-let
and when-some
because the former's assumptions might be violated if you take false
out of the channel
I think this is the origin of the concept: https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/
@ben.sless by wait on go-loop you mean <!! on the go-loop return... right?
hey btw, would this work?
(let handler (go-loop...))
(<!! handler)
yes, everything in Clojure returns a value. Go blocks return a channel which will contains the value of the expression which runs within them
I mean if the above 2 lines are in 2 completely different locations in the code, it is still going to wait for the handler to die... right?
err.... I must have missed something... assuming the handler is available in the scope... still?
(let [handler (go-loop ,,,)] ,,, (<!! handler))
Or do you mean calling handler outside of the let's scope?oh yes this works as well... but in general if instead of let, if handler was a def , even then within the same ns you should be able to <!! handler)
no?
Yes, but please don't def
things with a state which might change over time, it's a recipe for trouble
I know 😄 I'll keep that in mind. Thanks for all your help!! Take care
And yeah, since Clojure (but not ClojureScript) has blocking ops, you can avoid coloring.
Then you either need to immerse your code in async-ness, or not. Giving it two distinct colors
What if you were waiting for more than one block to exit?
What if you wanted to perform different actions based on which block exited?
calling <!!
on a go-block "breaks out" of async world to synchronous world
but if you're working with more than one channel and you don't want to block a thread, you have to wrap it all in a go-block
The only way to "synchronize" but not block is using go-blocks, which means I had to keep my code in go-blocks, which means it has color
Possible. You just got up, I'm about to go to sleep, too many opportunities for communication error
Thanks, we'll pick it up later. Take a look at the functions color post, it lays it out well
I think Ben just meant, if you want to work with non thread based concurrency you're stuck using go everywhere
You can use <!!
, but now you're doing regular old OS thread concurrency, which defeats the point of using go
in the first place.
If you program with the async versions of map, merge, filter, etc, you're programming with their async versions
I agree with that there is still color, but I don't know if having it is as bad as in JS, because channels are continuous streams, it seems like the way CSP works, the normal programming style just doesn't apply. Where as with promise it makes sense, because functions normally return a single result, so it be great to just have something automatically sync the async function call with the thing that waits for its single result. But processes are meant to run more like services, so a channel could return many things, so having them behave like collections where you can use transducers over them, but also understanding their async nature, I don't think you can retrofit this into a normal programming flow, like its just a different style of programming.
Which is why I prefer building "workers" with core.async and not use puts and takes directly
> calling `<!!` on a go-block "breaks out" of async world to synchronous world
That's exactly why coloring is not a problem in Clojure.
Within a single go
block you can treat <!
as blocking as well - the whole block is sequential in nature.
In e.g. JS you just can't block. You simply have no way of waiting for an async op to continue your sync flow.
That's where coloring exists.
It isn't a strong color but it still has a distinct hue 🙂
By which I mean it leaks. You feel it when you call <!!
in a go block
To be clear, I'm talking about calling <!!
outside of a go block. In a purely sync context.
its not a problem at the abstraction level. It is still a problem at the performance level
i.e. you can always turn Chan[T]
, Promise[T]
, or Future[T]
into T
- which is different from JS
but you don't get the full performance benefits of being "async" without keeping your T
wrapped
that being said, if we pretend we are go and have VM threads instead of OS threads (which is the future, if not immediately), then chan
still makes sense as an abstraction for running a concurrent process with a timeout or with alternatives or whatever
Yeah, this is coming soon with https://github.com/teknql/tapestry
I've never seen this function naming style but is it common to use arrows in names? E.g.
defn csv-data->maps [csv-data]
(map zipmap
(->> (first csv-data) ;; First row is the header
(map keyword) ;; Drop if you want string keys instead
repeat)
(rest csv-data)))
@U01F1TM2FD5What do you mean by using it as a "map"? DO you mean the returned value is a map or the arg itself? @U11BV7MTK speaks about type signatures out of the thread.
It's a common naming convention for functions that convert one thing into another, where it goes:
(defn x->z
"Converts an x into a z"
[x]
...)
Seems like you @-ed a wrong person. :)
I mean this: (def id->entity {1 entity-1, 2 entity-2})
Oh whoops. 🙂
@U0K064KQV very succinct explanation, thanks.
Other languages normally use to
for the same, so when you see
x->z
Think of it as:
xToz
I like some of the symbols in the function names, prevents verbose names.
You'll also see it sometime for constructor functions (sometimes called factory functions in some languages) where it'll have ->x
which means to x
(defn ->person
"Construct a person map with values as provided from name and address"
[name address]
{:name name
:address address})
And again in other languages normally they'd use to
instead, like toPerson(name, address)
Ya, so when you create a record, Clojure will auto-create two such functions:
(defrecord Person [name address])
(->Person "John" "1111 street")
(map->Person {:name "John", :address "1111 street"})
Also, generally Clojure distinguishes an uppercase to indicate a record or type like Person with uppercase P because it is a record. While it uses lowercase to indicate the entity is a common data-structure like person would generally imply it's a map or a vector
when a function like this, often is basically a type signature f: csv-data -> maps
and just keep the type signature. When a var, often for the same or a map with keys of the left side of the arrow and values the right side of the arrow. This gets a bit hazy too as maps are invocable as functions so you can treat it as the same case or not
Sorry I lost you at "When a var, often for the same or a map with keys of the left side of the arrow and values the right side of the arrow." I get the idea of expressing the function type signature in the name though.
(defn x->y [x] <computes y from x>)
is a var named x->y
(let [id->people {1 person-1 2 person-2}] ...)
here the local of id->people is a map of id mapped to people. it is a map. but it's also a function since maps are invocable. so its kinda different, kinda the same
Honest follow up question: I used to name conversion functions as a->b
s as well but I've recently started to doubt it...
If you have a function that "creates" a person, why not just name it "person"? Why does the type of input and the information that the function converts something to something else have to be part of its name?
I can actually think of two reasons:
• If the function that converts a to b is called b, what name would you bind the actual result of the function to? and
• having multiple types of input (a->b, c->b etc) - which one would the function named b
be?
But even so I'm generally content with shorter names :man-shrugging:
I don't think there is a generic answer to any of those questions - it all depends on the context and on personal preferences, and even those fluctuate within the same person.
If you haven't already, I'd highly recommend reading Zach Tellman's "Elements of Clojure" which talks in depth about naming.
Yeah that's true @U2FRKM4TW ! I guess what I was trying to convey is that, from personal experience, I've found that trying to avoid the extra symbols has resulted in more readable code for the most part. It goes without saying that ymmv 🙂 There's actually a free sample online that includes the naming section and it's quite insightful, thanks @U04V70XH6
Not quite the same, but related: I definitely think person
is all-around better than fetch-person
or get-person
.
I do less of the a->b
stuff lately as well. I find it does work when the operation describes an actual conversion. For example, if you are transferring products from one system to another: product-a->product-b
or (and this one’s odd) a->b:product
> if your function name is just a noun, what does the naming tell you about what the function does?
>
Well, take keys
and vals
for instance. Would ->keys
or map->vals
be better names for them?
I don't see verbs as a requirement in function names but yeah sometimes they do give some context, e.g. in a namespace called email, fetch could be a nice name for a function (a similar example is actually in Elements of Clojure as well 😄 ).
I do think its a Lisp-1 problem. You will for sure want to do:
(def person (->person "John"))
And you can't do:
(def person (person "John"))
(def other-person (person "Bob"))
> if your function name is just a noun, what does the naming tell you about what the function does?
Ah. My current take: I read (person "John")
as “it returns a person named John.” I don’t care what it does to get there.
and as for naming:
(def john (person "John"))
(def bob (person "Bob"))
(defn foo [name]
(let [p (person name)] ...))
Ya, like it's okay that m
is map, which is necessary because map is a function, but then I find using p
becomes cryptic, because p
is not generally understood as a person, maybe you also have a provider entity in your app, and now things get weird, etc.
@U0K064KQV Notice I used it for a locally-scoped thing.
So if there’s a question, “what’s p
?” you look up at the definition, “oh, p
is a person”
I usually still pass that stuff in. It allows you to do things like rate limiting and circuit breaking.
I would probably agree with others, functions perform computation, seem logical that they tend to be verbs, and variables define things, seems logical they tend to be nouns.
The "but my function is pure so it can be treated as a value" I don't really buy into, because it still needs to be parameterized, even if all it does is lookup the right output for the given input, that feels like an action to me
What gets weird are getter like "keys", but to me that makes sense a noun, because its really like a kind of field access
@U8QBZBHGD that’s basically what I do. I make a namespace for a particular service. Everything in there is expected to probably do some i/o.
Like as a Clojure wrapper for Zulip? Or as like where you put your business logic for things that need stuff from Zulip?
As a zulip wrapper I do the same thing. And normally use get
for lookup queries, and fetch
for search
So get-message
is lookup, so would take like an id, where as fetch-message
would be a search so maybe say it takes a regex to find all messages of match in their content
So like on some API if I need to lookup a message somewhere I'd probably have a get-message
function for it in the API namespace. If another API also needs to do the same then I'll just move the function to a shared helper namespace
Right, so this is my apps get-message
for some API. The fact it uses Zulip would be like an implementation details. So inside get-message
maybe I use Zulip, or something else, and maybe that has its own get-message. That's what I meant like this is different then if you created a ZulipClient namespace which was like a Clojure based client for Zulip