Cognitect and Nubank are Sponsoring Open Source Developers

2020-12-15 Thread Alex Miller
https://cognitect.com/blog/2020/12/15/sponsoring-open-source-developers

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/clojure/5d7b0904-5d64-409b-a98d-8099961a75e9n%40googlegroups.com.


Re: Cognitect and Nubank are Sponsoring Open Source Developers

2020-12-15 Thread Peter Strömberg
This is fantastic. What an excellent way to show the way!

The Calva team is almost fainting from realizing we are both sponsored by
the very company which creates Clojure. Wow, just wow!

Den tis 15 dec. 2020 kl 18:09 skrev Alex Miller <
alex.mil...@thinkrelevance.com>:

> https://cognitect.com/blog/2020/12/15/sponsoring-open-source-developers
>
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clojure@googlegroups.com
> Note that posts from new members are moderated - please be patient with
> your first post.
> To unsubscribe from this group, send email to
> clojure+unsubscr...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
> ---
> You received this message because you are subscribed to the Google Groups
> "Clojure" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to clojure+unsubscr...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/clojure/5d7b0904-5d64-409b-a98d-8099961a75e9n%40googlegroups.com
> 
> .
>

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/clojure/CAMX9Yz8sZU5JfSosDfUVaf9LetXdTLv-QJWjnUEEAwvJ9LKbJA%40mail.gmail.com.


Re: Idiomatic program for someone new to Clojure

2020-12-15 Thread James Lorenzen
Thanks for the suggestions aditya. I definitely like where you are headed. 
I have a few questions. This syntax in `pipeline-build-count` method looks 
confusing to me:
> [{:keys [body] :as response}] ;; destructuring for convenience and 
function API documentation

Would you mind breaking that down a little more?

Also, your program works great though it only returns a list of the latest 
build numbers without the project name. I'm trying to think of how I could 
include that but I'm not seeing it. The fetch pipeline response does 
include the project name so I could just return it and the counter; so I 
could return a tuple? or just two values (I think clojure can support 
that). I'd rather just thread the project-name through though.

Thanks for all the help! I'm learning a ton.
On Monday, December 14, 2020 at 11:35:15 PM UTC-6 aditya@gmail.com 
wrote:

> I'd try to separate the "I/O or side-effecting" parts from the "purely 
> data processing" parts. This makes the program much easier to test --- the 
> "purer" the code, the better it is. This also helps tease apart 
> domain-agnostic parts from domain-specialised parts, which is useful, 
> because domain-agnostic parts tend to be generalisable and thus more 
> reusable.
>
> I'd also avoid mixing lazy functions like `map` with effectful things like 
> `GET` requests, because : 
> https://stuartsierra.com/2015/08/25/clojure-donts-lazy-effects
>
> I've taken the liberty to rearrange the code, and rename functions to 
> illustrate what I mean.
>
> (defn pipeline-api-endpoint ;; lifted out of `fetch-pipeline` 
>   [project-name]
>   ;; knows how to translate, or perhaps more generally, map, a project 
> name to a project URL format
>   (str "https://example.com/go/api/pipelines/"; project-name "/history"))
>
> (defn http-get-basic-auth ;; instead of `fetch-pipeline', because now 
> this operation doesn't care about a specific type of URL  
>   [well-formed-url] ;; it can simply assume someone gives it a well-formed 
> URL.
>   (client/get well-formed-url
>   {:basic-auth "username:password"})) ;; we'll see about this 
> hard-coding later
>
> (defn pipeline-build-count ;; now this only cares about looking up build 
> count, so no "GET" semantics
>   ;; assumes it gets a well-formed response
>   [{:keys [body] :as response}] ;; destructuring for convenience and 
> function API documentation
>   (-> body
>   (parse-string true)
>   :pipelines
>   first
>   :counter))
>
> (defn fetch-pipeline-counts! ;; ties all the pieces together
>   [project-names]
>   (reduce (fn [builds project-name] 
>;; uses reduce, not map, because: 
> https://stuartsierra.com/2015/08/25/clojure-donts-lazy-effects
> (conj builds
>   (-> project-name
>   pipeline-api-endpoint
>   http-get-basic-auth
>   pipeline-build-count)))
>   []
>   project-names))
>
>
> Now... It turns out that fetch-pipeline-counts! is a giant effectful 
> process, tied directly to http-get-basic-auth. We could try to lift out the 
> effectful part, and try to make it a pure function.
>
> (defn http-get-basic-auth
>   [well-formed-url username password]
>   (client/get well-formed-url
>   {:basic-auth (str username ":" password)}))
>
> (defn http-basic-auth-getter
>   "Given basic auth credentials, return a function that takes an HTTP 
> endpoint, and GETs data from there."
>   [username password]
>   (fn [well-formed-url]
> (http-get-basic-auth well-formed-url
>  username
>  password)))
>
> (defn fetch-pipeline-counts-alt
>   [pipeline-fetcher project-names]
>   ;; Easier to unit test. We can pass a mock fetcher that doesn't call 
> over the network.
>   ;; In fact, we can now use any kind of "fetcher", even read from a DB or 
> file where we may have dumped raw GET results.
>   (reduce (fn [builds project-name]
> (conj builds
>   (-> project-name
>   pipeline-api-endpoint
>   pipeline-fetcher
>   pipeline-build-count)))
>   []
>   project-names))
>
> (comment
>   (fetch-pipeline-counts-alt (http-basic-auth-getter "username" "password")
>  ["projectA"
>   "projectB"
>   "projectC"
>   "projectD"])
>  )
>
> A closer look might suggest that we're now describing processes that could 
> be much more general than fetching pipeline counts from an HTTP endpoint...
>
> Enjoy Clojuring! :)
>
> On Monday, December 14, 2020 at 10:51:52 PM UTC+5:30 jamesl...@gmail.com 
> wrote:
>
>> Very cool everyone. This is exactly the kind of feedback I was hoping 
>> for. I'm going through Clojure for the Brave and I hadn't made it to the 
>> macros chapter yet. That single threading macro is pretty sweet!
>>
>> Thanks ever

Re: Idiomatic program for someone new to Clojure

2020-12-15 Thread James Lorenzen
So I think I can answer my first question. That particular line in 
`pipeline-build-count` is doing associative destructing 
. And 
you didn't have to do the `as response` but like you said in your comment, 
you are just doing that to sort of document the method. That sound about 
right?

On Tuesday, December 15, 2020 at 2:33:08 PM UTC-6 James Lorenzen wrote:

> Thanks for the suggestions aditya. I definitely like where you are headed. 
> I have a few questions. This syntax in `pipeline-build-count` method looks 
> confusing to me:
> > [{:keys [body] :as response}] ;; destructuring for convenience and 
> function API documentation
>
> Would you mind breaking that down a little more?
>
> Also, your program works great though it only returns a list of the latest 
> build numbers without the project name. I'm trying to think of how I could 
> include that but I'm not seeing it. The fetch pipeline response does 
> include the project name so I could just return it and the counter; so I 
> could return a tuple? or just two values (I think clojure can support 
> that). I'd rather just thread the project-name through though.
>
> Thanks for all the help! I'm learning a ton.
> On Monday, December 14, 2020 at 11:35:15 PM UTC-6 aditya@gmail.com 
> wrote:
>
>> I'd try to separate the "I/O or side-effecting" parts from the "purely 
>> data processing" parts. This makes the program much easier to test --- the 
>> "purer" the code, the better it is. This also helps tease apart 
>> domain-agnostic parts from domain-specialised parts, which is useful, 
>> because domain-agnostic parts tend to be generalisable and thus more 
>> reusable.
>>
>> I'd also avoid mixing lazy functions like `map` with effectful things 
>> like `GET` requests, because : 
>> https://stuartsierra.com/2015/08/25/clojure-donts-lazy-effects
>>
>> I've taken the liberty to rearrange the code, and rename functions to 
>> illustrate what I mean.
>>
>> (defn pipeline-api-endpoint ;; lifted out of `fetch-pipeline` 
>>   [project-name]
>>   ;; knows how to translate, or perhaps more generally, map, a project 
>> name to a project URL format
>>   (str "https://example.com/go/api/pipelines/"; project-name "/history"))
>>
>> (defn http-get-basic-auth ;; instead of `fetch-pipeline', because now 
>> this operation doesn't care about a specific type of URL  
>>   [well-formed-url] ;; it can simply assume someone gives it a 
>> well-formed URL.
>>   (client/get well-formed-url
>>   {:basic-auth "username:password"})) ;; we'll see about this 
>> hard-coding later
>>
>> (defn pipeline-build-count ;; now this only cares about looking up build 
>> count, so no "GET" semantics
>>   ;; assumes it gets a well-formed response
>>   [{:keys [body] :as response}] ;; destructuring for convenience and 
>> function API documentation
>>   (-> body
>>   (parse-string true)
>>   :pipelines
>>   first
>>   :counter))
>>
>> (defn fetch-pipeline-counts! ;; ties all the pieces together
>>   [project-names]
>>   (reduce (fn [builds project-name] 
>>;; uses reduce, not map, because: 
>> https://stuartsierra.com/2015/08/25/clojure-donts-lazy-effects
>> (conj builds
>>   (-> project-name
>>   pipeline-api-endpoint
>>   http-get-basic-auth
>>   pipeline-build-count)))
>>   []
>>   project-names))
>>
>>
>> Now... It turns out that fetch-pipeline-counts! is a giant effectful 
>> process, tied directly to http-get-basic-auth. We could try to lift out the 
>> effectful part, and try to make it a pure function.
>>
>> (defn http-get-basic-auth
>>   [well-formed-url username password]
>>   (client/get well-formed-url
>>   {:basic-auth (str username ":" password)}))
>>
>> (defn http-basic-auth-getter
>>   "Given basic auth credentials, return a function that takes an HTTP 
>> endpoint, and GETs data from there."
>>   [username password]
>>   (fn [well-formed-url]
>> (http-get-basic-auth well-formed-url
>>  username
>>  password)))
>>
>> (defn fetch-pipeline-counts-alt
>>   [pipeline-fetcher project-names]
>>   ;; Easier to unit test. We can pass a mock fetcher that doesn't call 
>> over the network.
>>   ;; In fact, we can now use any kind of "fetcher", even read from a DB 
>> or file where we may have dumped raw GET results.
>>   (reduce (fn [builds project-name]
>> (conj builds
>>   (-> project-name
>>   pipeline-api-endpoint
>>   pipeline-fetcher
>>   pipeline-build-count)))
>>   []
>>   project-names))
>>
>> (comment
>>   (fetch-pipeline-counts-alt (http-basic-auth-getter "username" 
>> "password")
>>  ["projectA"
>>   "projectB"
>>   "projec

Re: Idiomatic program for someone new to Clojure

2020-12-15 Thread Peter Strömberg
Seconding aditya's advice here. I can truly recommend watching this talk
about solving problems the Clojure way:

https://youtu.be/vK1DazRK_a0

Interestingly, he applies it on a JavaScript code example. Which makes it
all that much more powerful.

On Tue, 15 Dec 2020 at 22:42, James Lorenzen 
wrote:

> So I think I can answer my first question. That particular line in
> `pipeline-build-count` is doing associative destructing
> .
> And you didn't have to do the `as response` but like you said in your
> comment, you are just doing that to sort of document the method. That sound
> about right?
>
> On Tuesday, December 15, 2020 at 2:33:08 PM UTC-6 James Lorenzen wrote:
>
>> Thanks for the suggestions aditya. I definitely like where you are
>> headed. I have a few questions. This syntax in `pipeline-build-count`
>> method looks confusing to me:
>> > [{:keys [body] :as response}] ;; destructuring for convenience and
>> function API documentation
>>
>> Would you mind breaking that down a little more?
>>
>> Also, your program works great though it only returns a list of the
>> latest build numbers without the project name. I'm trying to think of how I
>> could include that but I'm not seeing it. The fetch pipeline response does
>> include the project name so I could just return it and the counter; so I
>> could return a tuple? or just two values (I think clojure can support
>> that). I'd rather just thread the project-name through though.
>>
>> Thanks for all the help! I'm learning a ton.
>> On Monday, December 14, 2020 at 11:35:15 PM UTC-6 aditya@gmail.com
>> wrote:
>>
>>> I'd try to separate the "I/O or side-effecting" parts from the "purely
>>> data processing" parts. This makes the program much easier to test --- the
>>> "purer" the code, the better it is. This also helps tease apart
>>> domain-agnostic parts from domain-specialised parts, which is useful,
>>> because domain-agnostic parts tend to be generalisable and thus more
>>> reusable.
>>>
>>> I'd also avoid mixing lazy functions like `map` with effectful things
>>> like `GET` requests, because :
>>> https://stuartsierra.com/2015/08/25/clojure-donts-lazy-effects
>>>
>>> I've taken the liberty to rearrange the code, and rename functions to
>>> illustrate what I mean.
>>>
>>> (defn pipeline-api-endpoint ;; lifted out of `fetch-pipeline`
>>>   [project-name]
>>>   ;; knows how to translate, or perhaps more generally, map, a project
>>> name to a project URL format
>>>   (str "https://example.com/go/api/pipelines/"; project-name "/history"))
>>>
>>> (defn http-get-basic-auth ;; instead of `fetch-pipeline', because now
>>> this operation doesn't care about a specific type of URL
>>>   [well-formed-url] ;; it can simply assume someone gives it a
>>> well-formed URL.
>>>   (client/get well-formed-url
>>>   {:basic-auth "username:password"})) ;; we'll see about
>>> this hard-coding later
>>>
>>> (defn pipeline-build-count ;; now this only cares about looking up
>>> build count, so no "GET" semantics
>>>   ;; assumes it gets a well-formed response
>>>   [{:keys [body] :as response}] ;; destructuring for convenience and
>>> function API documentation
>>>   (-> body
>>>   (parse-string true)
>>>   :pipelines
>>>   first
>>>   :counter))
>>>
>>> (defn fetch-pipeline-counts! ;; ties all the pieces together
>>>   [project-names]
>>>   (reduce (fn [builds project-name]
>>>;; uses reduce, not map, because:
>>> https://stuartsierra.com/2015/08/25/clojure-donts-lazy-effects
>>> (conj builds
>>>   (-> project-name
>>>   pipeline-api-endpoint
>>>   http-get-basic-auth
>>>   pipeline-build-count)))
>>>   []
>>>   project-names))
>>>
>>>
>>> Now... It turns out that fetch-pipeline-counts! is a giant effectful
>>> process, tied directly to http-get-basic-auth. We could try to lift out the
>>> effectful part, and try to make it a pure function.
>>>
>>> (defn http-get-basic-auth
>>>   [well-formed-url username password]
>>>   (client/get well-formed-url
>>>   {:basic-auth (str username ":" password)}))
>>>
>>> (defn http-basic-auth-getter
>>>   "Given basic auth credentials, return a function that takes an HTTP
>>> endpoint, and GETs data from there."
>>>   [username password]
>>>   (fn [well-formed-url]
>>> (http-get-basic-auth well-formed-url
>>>  username
>>>  password)))
>>>
>>> (defn fetch-pipeline-counts-alt
>>>   [pipeline-fetcher project-names]
>>>   ;; Easier to unit test. We can pass a mock fetcher that doesn't call
>>> over the network.
>>>   ;; In fact, we can now use any kind of "fetcher", even read from a DB
>>> or file where we may have dumped raw GET results.
>>>   (reduce (fn [builds project-name]
>>> (conj builds
>>>   (-> project-name
>>>   pipe

Re: Idiomatic program for someone new to Clojure

2020-12-15 Thread aditya....@gmail.com
On Wednesday, December 16, 2020 at 3:12:22 AM UTC+5:30 jamesl...@gmail.com 
wrote:

> So I think I can answer my first question. That particular line in 
> `pipeline-build-count` is doing associative destructing 
> . 
> And you didn't have to do the `as response` but like you said in your 
> comment, you are just doing that to sort of document the method. That sound 
> about right?
>

Yes it's "associative destructuring". The main purpose of destructuring is 
to conveniently bind a name to a value found somewhere inside a data 
structure. The additional benefit is that it also serves to document the 
function API, which IDE tooling can surface.

The term "destructuring" sounded heavy when I first tried to grok it. It 
got easier when I started thinking of it like visually matching shapes (of 
name bindings) to shapes (of data). Sort of like a fancy mask or a cookie 
cutter pattern. To illustrate:

;; If I see this:

(let [[a b c & others]
  [1 2 3 4 5 6]]
  ;; do something
  )

;; I visualise the effect of destructuring as:

1  2  34 5 6
|  |  |   ( )
v  v  v  v
[a  b  c & others]
 

> On Tuesday, December 15, 2020 at 2:33:08 PM UTC-6 James Lorenzen wrote:
>
>> Also, your program works great though it only returns a list of the 
>> latest build numbers without the project name. I'm trying to think of how I 
>> could include that but I'm not seeing it. The fetch pipeline response does 
>> include the project name so I could just return it and the counter; so I 
>> could return a tuple? or just two values (I think clojure can support 
>> that). I'd rather just thread the project-name through though.
>>
>
There are at least two ways to do this. In both cases, I'd favour returning 
a hash-map, instead of tuples. 

One way is to modify `pipeline-build-count` like this:

(defn pipeline-build-count
  [{:keys [body] :as response}]
  {:project-name (:the-key-for-project-name body)
   :latest-build-count (-> body
   (parse-string true)
   :pipelines
   first
   :counter)})

The other way is in the fetch-pipeline-counts function. Something like this:

(defn fetch-pipeline-counts-alt
  [pipeline-fetcher project-names]
  (reduce (fn [builds project-name]
(conj builds
  {:project-name project-name
   :latest-build-count (-> project-name
   pipeline-api-endpoint
   pipeline-fetcher
   pipeline-build-count)}))
  []
  project-names))

If the name is the only relevant detail that matters, then either way is 
fine. However, I'd modifying the pipeline-build-count function, because it 
has access to all the information about a pipeline, and we may want to 
process and/or pass through more of it later.

 

> Thanks for all the help! I'm learning a ton.
>>
>
Cheers :)

On Monday, December 14, 2020 at 11:35:15 PM UTC-6 aditya@gmail.com 
>> wrote:
>>
>>> I'd try to separate the "I/O or side-effecting" parts from the "purely 
>>> data processing" parts. This makes the program much easier to test --- the 
>>> "purer" the code, the better it is. This also helps tease apart 
>>> domain-agnostic parts from domain-specialised parts, which is useful, 
>>> because domain-agnostic parts tend to be generalisable and thus more 
>>> reusable.
>>>
>>> I'd also avoid mixing lazy functions like `map` with effectful things 
>>> like `GET` requests, because : 
>>> https://stuartsierra.com/2015/08/25/clojure-donts-lazy-effects
>>>
>>> I've taken the liberty to rearrange the code, and rename functions to 
>>> illustrate what I mean.
>>>
>>> (defn pipeline-api-endpoint ;; lifted out of `fetch-pipeline` 
>>>   [project-name]
>>>   ;; knows how to translate, or perhaps more generally, map, a project 
>>> name to a project URL format
>>>   (str "https://example.com/go/api/pipelines/"; project-name "/history"))
>>>
>>> (defn http-get-basic-auth ;; instead of `fetch-pipeline', because now 
>>> this operation doesn't care about a specific type of URL  
>>>   [well-formed-url] ;; it can simply assume someone gives it a 
>>> well-formed URL.
>>>   (client/get well-formed-url
>>>   {:basic-auth "username:password"})) ;; we'll see about 
>>> this hard-coding later
>>>
>>> (defn pipeline-build-count ;; now this only cares about looking up 
>>> build count, so no "GET" semantics
>>>   ;; assumes it gets a well-formed response
>>>   [{:keys [body] :as response}] ;; destructuring for convenience and 
>>> function API documentation
>>>   (-> body
>>>   (parse-string true)
>>>   :pipelines
>>>   first
>>>   :counter))
>>>
>>> (defn fetch-pipeline-counts! ;; ties all the pieces together
>>>   [project-names]
>>>   (reduce (fn [builds project-name] 
>>>;; uses reduce, not map, b