Fork me on GitHub
#beginners
<
2022-03-31
>
leifericf12:03:46

I’m trying to find a talk that I saw on YouTube a year ago where a gentleman and two ladies were demonstrating how they created a mobile app with Clojure. I believe they were using React Native via some Clojure libraries. They showed an app they made for some car manufacturer, where users could unlock and start the car from their app, share access with their friends, etc. During the presentation, they showed a lot of Clojure code and how two separate app instances on different simulated devices were sending and receiving messages from one another. I recall the speaker mentioning something about making it possible for the front-end designers to work on the graphical user interface independently and without understanding much Clojure code. That’s the part of the talk I want to revisit. Does anyone know about this talk? I can’t find it on YouTube anymore for the life of me.

Ben Sless12:03:08

I think that's the one

leifericf12:03:28

Thank you, @UK0810AQ2! I’ve been searching for an hour 😅 Very much appreciate it!

Ben Sless12:03:28

Glad I could help 🙂 Clojurians slack, faster than any search 😉

truestory 2
Ben Sless12:03:02

By sheer luck, I also watched this talk and recognized it immediately from your description

leifericf12:03:05

Indeed! I didn’t want to burden others with doing my Googling, but it sure was fast! Haha.

Ben Sless12:03:08

So this was a single youtube search and was the first search result

😅 2
leifericf12:03:56

My search foo is weak, apparently 😅

Ben Sless12:03:01

I swear I have a superpower when it comes to search engines, I manage to always phrase the search just right

😂 1
Ben Sless12:03:20

david nolen clojurescript mobile

leifericf12:03:57

I think it actually did show up in some of my search results, but the content I was looking for was not recognizable until further into the talk, and the title and description of the video did not give me enough hints. And I didn’t recall which persons gave the talk. I was searching for things like “react native clojure mobile app car” etc.

Ben Sless12:03:03

well, if you remember who gave the talk and which talk it was then it's almost cheating 🙃

😂 1
leifericf12:03:15

The lesson of the day for myself: “Don’t persist with “stubborn dumb-mode searching.” Ask the community first.

Ben Sless12:03:53

I bet this will happen to me at least one time, just like when you remember a song as being awesome, send it to someone only to realize it sort of sucks now, but with a conference talk

💡 1
Ben Sless12:03:34

And the Clojurians community is exceptionally good there

👍 1
leifericf12:03:27

Yeah! It’s like that. When you remember the melody of a song or a fragment of the lyrics but can’t find the music. There’s a song that I’ve had stuck on my mind for nearly two years now. I’ve searched and searched and asked others about it. The fragment of the lyrics is “ooh, calypso, tell me to go…” (that’s all I remember)—And it’s a hoarse and raspy male voice, sort of new-age post-rock-ish music. I only find some songs by John Denver and Mamie Van Doren, but that’s not it. Search engines are not great at searching contents of video and audio yet.

leifericf13:03:39

By the way, here is https://youtu.be/3HxVMGaiZbc?t=1094 (it starts at 18:15). It’s where @U050B88UR talks about https://storybook.js.org: > “Conceptually, what it [Storybook] lets you do, is it lets you build your components in total isolation from your application.” He also mentions an analogous tool called “defparts” or “devparse” or something like that. I can’t make out exactly what David is saying due to insufficient audio quality in the recording. I’m interested in learning more about that concept because I work with customer experience designers and user experience designers who are not very technical. It would be fantastic if they could create the user interface elements in pure data structures or maybe a domain-specific language.

kj13:03:00

> He also mentions an analogous tool called “defparts” or “devparse” or something like that. I can’t make out exactly what David is saying due to insufficient audio quality in the recording. Pretty sure David is referring to “devcards”: https://github.com/bhauman/devcards

👍 1
💜 1
leifericf14:03:09

Thank you, @U01SX4U5LUA and @U031CHTGX1T! Now I have some reading to do. The idea of completely separating the “visual and layout stuff” from the presentation logic and back-end intrigues me, especially if it allows non-technical designers to work on the visual components intuitively, where the artifacts they produced can be versioned alongside any coupled code. Ideally, the visual designers would be able to work in drag-and-drop tools without needing to touch anything resembling code.

fadrian13:03:39

I understand why a set is not an ISeqable - basically, as it has no order, seq makes no sense when applied to the set. However, sorted-sets are also non-ISeqable. This makes less sense to me, as a sorted-set has an order implied by its comparator function. Can someone explain why a sorted-set is also non-ISeqable?

Ferdinand Beyer13:03:05

Hm?

user=> (seqable? #{})
true

fadrian13:03:24

That's odd, I'm getting a non-ISeqable error thrown in my code when I'm using a sorted-set. It must be some other issue.

ghadi13:03:50

you might be confusing ISeq and Seqable

ghadi13:03:03

sets are not seqs, but they can produce a seq

fadrian13:03:12

Possibly. I'll look at my code a bit more.

ghadi13:03:13

just like vectors

genmeblog13:03:22

maybe you think about sequential?

genmeblog13:03:22

(seqable? (sorted-set)) ;; => true
(seqable? (hash-set)) ;; => true
(sequential? (sorted-set)) ;; => false
(sequential? (hash-set)) ;; => false
(seq? (sorted-set)) ;; => false
(seq? (hash-set)) ;; => false

fadrian13:03:10

I found it - the ISequable error was being thrown because of an incorrect variable reference which was returning a nil rather than the sequence I thought I was using.

fadrian13:03:29

Sorry for the confusion.

Muhammad Hamza Chippa14:03:30

how can I parse xml in clojurescript I have tried to use https://github.com/funcool/tubax in my shadow-cljs project but it appears to be broken

sheluchin14:03:33

I'm trying to understand transducers.

(transduce (comp (mapcat (fn [[a b]]       
                           {:a a :b b})))  
           (completing                     
            (fn [_ new-num]                
              (prn new-num)))              
           []                              
           (vec (take 3 (cycle [[1 2]])))) 
Result:
[:a 1]
[:b 2]
[:a 1]
[:b 2]
[:a 1]
[:b 2]
Why the result a bunch of map entries being printed, rather than the map (`{:a a :b b}`) created in the transducer? Is the reducing function receiving the result of the transformation?

Ferdinand Beyer14:03:18

Because of mapcat, maybe you want map instead? Your mapcat function produces maps, but mapcat will map+cat, treating the map as a sequence of items and return the concatenation of that.

dpsutton14:03:32

(mapcat (fn [[a b]] {:a a :b b}) [[1 2] [1 2] [1 2]])
([:a 1] [:b 2] [:a 1] [:b 2] [:a 1] [:b 2])

dpsutton14:03:12

which is “concat {:a 1 b 2} {a 1 b 2} …” so it concats the key value entries

sheluchin15:03:10

Ah, okay. So mapcat applies the map, which produces a hashmap, but then concat calls seq on the result, which turns each hashmap into a seq of entries, and then those are concat'ed into a vector.

dpsutton15:03:31

concated into a sequence

sheluchin15:03:12

Right, right.. makes sense. Thank you.

sheluchin15:03:03

My 10k foot view is that you provide transduce with some lazy sequence, the transform is a set of pure transformations on the sequence, and then the reducing function receives the result and can perform some IO. Does that sum it up well?

dpsutton15:03:30

I think its better to think about transducers being a function that takes a reducing function and returns a new reducing function. Reducing functions do not have to be pure but can do io, etc

dpsutton15:03:53

lazy sequence is not required and is just one collection type you can transduce over

sheluchin15:03:28

If it's not lazy, you still get the benefit of not having to retain the entire set of transformations in memory, yeah? That's basically the point?

Ferdinand Beyer15:03:59

Even though it is tempting, I would not start with transduce to understand transducers. into for example might be simpler:

(into [] (filter even?) (range 10))
[0 2 4 6 8]
So (filter even?) returns a transducer, that when applied to a sequence will only keep even items.

Ferdinand Beyer15:03:16

The idea behind transducers is actually that you don’t care for the sequence itself. Lazy, not lazy, async channels --- it does not matter to the transducer

sheluchin15:03:50

@U031CHTGX1T I saw that example from the docs but I don't understand how that's supposed to make understanding it any easier. Seems like it just introduces an another abstraction layer into->transducer to explain the transducer.

dpsutton15:03:51

https://clojure.org/reference/transducers is a good reference. As is (source transduce)

👍 1
Ferdinand Beyer15:03:17

Your mileage might vary. For me, this is helpful be cause the key of a transducer is that it transforms items that “pass through it”. With transduce, you also have to worry about the f parameter 🤷

Ferdinand Beyer15:03:30

If you haven’t seen it already, go watch Rich’s talk! https://www.youtube.com/watch?v=6mTbuzafcII

sheluchin15:03:18

I did watch that one a couple months ago. I should refresh.

sheluchin15:03:30

Haven't seen "Inside Transducers" yet.

sheluchin15:03:05

> I think its better to think about transducers being a function that takes a reducing function and returns a new reducing function. So transducers return a new reducing function, which is just the given reducing function with the given transforms applied to its input before it's evaluated? Sound better?

Ferdinand Beyer15:03:32

I think this is worse unfortunately. See, a transducer wraps a reducing function. If it calls the wrapped one or not is up to the transducer.

Ferdinand Beyer15:03:03

(map inc) will call the passed reducing function for every item after transforming it

dpsutton15:03:06

yeah. look at the source of map.

([f]
    (fn [rf]
      (fn
        ([] (rf))
        ([result] (rf result))
        ([result input]
           (rf result (f input)))
        ([result input & inputs]
           (rf result (apply f input inputs))))))
So it takes a reducing function and calls (rf result (f input)).

gratitude-thank-you 1
Ferdinand Beyer15:03:22

(filter even?) will not call the reducing function for odd items

Ferdinand Beyer15:03:35

(remove any?) will never call its reducing function

Ferdinand Beyer15:03:09

(filter pred) will also not transform the items themselves

sheluchin15:03:25

> I think this is worse unfortunately. See, a transducer wraps a reducing function. If it calls the wrapped one or not is up to the transducer. But doesn't this align with my description? You've provided some examples where the transducer transforms the input by excluding some of the members. So the transducer is not "deciding" whether the wrapped function is called, but it is changing the size of the the original input and what ultimately gets to the wrapped function. Is that different from how I described it as: > the given reducing function with the given transforms applied to its input before it's evaluated

Ferdinand Beyer15:03:45

The input to a reducing function is each item individually, not the whole sequence, right?

Ferdinand Beyer15:03:15

I read your definition as “Transforms values before passing them to the wrapped reducing function”. This describes the map transducer, but not filter

gratitude-thank-you 1
sheluchin16:03:14

> The input to a reducing function is each item individually, not the whole sequence, right? Hmm :thinking_face: So the transformation receives the entire sequence and hands off individual items to the reducing function. But I guess that's not quite right either. I remember from Rich's luggage handler analogy, the input can be filtered, transformed, and rebundled. So if there's a partition step in there somewhere, the input to the reducing function could be a bundle of the individual items. It's starting to come together for me, I think..

Pablo20:03:32

Hello, What is the best way to dissoc a value if it’s get empty?

(? {:foo {:a ("A" "B")} :bar {:x 1}} [:foo :a] "A")
;; => {:foo {:a ("B")} :bar {:x 1}}
(? {:foo {:a ("B")} :bar {:x 1}} [:foo :a] "B")
;; => {:bar {:x 1}}

hiredman20:03:07

There is no built in function that does that, there have been proposals to add something like that, sometimes called dissoc-in, but I think that ticket got nixed

hiredman20:03:54

You might be able to find something like that in some utility library

Pablo21:03:43

I tried medley/dissoc-in, but I got :

Execution error (ClassCastException) at medley.core/dissoc-in (core.cljc:31).
class clojure.lang.PersistentVector$ChunkedSeq cannot be cast to class clojure.lang.IPersistentMap (clojure.lang.PersistentVector$ChunkedSeq and clojure.lang.IPersistentMap are in unnamed module of loader 'app')
:thinking_face:

hiredman21:03:31

Seqs are not associative

hiredman21:03:54

So you cannot dissoc from them, nested or otherwise

🆗 1
teodorlu21:03:28

You can remove from them, though!

(remove #{"A"} (list 1 "A" "B" "C" "A"))
;; => (1 "B" "C")

hiredman20:03:19

The times I have needed it, I think I just did an update-in at a level above where the dissoc is, and did the dissoc and checked the result and it was empty also dissoced that

hiredman20:03:47

But that isn't a general solution

teodorlu21:03:29

I gave it a start:

(defn dissoc-in-seq [m path value]
  (let [without-value (remove #{value}
                              (get-in m path))]
    (if (empty? without-value)
      ;; Update m to remove value and the last key from the map
      ;; Messy.
      :todo
      ;; else - just assoc in the new value
      (assoc-in m path without-value))))

(dissoc-in-seq {:foo {:a (list "A" "B")} :bar {:x 1}} [:foo :a] "A")
;; => {:foo {:a ("B")}, :bar {:x 1}}
but ... I'm getting a feeling that there's a better way to organize the data than "arbitrary nested maps with sequences in them that need cleaning up".

teodorlu21:03:42

This feels like a bad idea 😄

;; remove empty sequences from map
(defn remove-empty-sequences [m]
  (into {}
        (remove (fn [[_ v]]
                  (empty? v)))
        m))

(remove-empty-sequences {:x [1]
                         :y [2 3]
                         :z []})
;; => {:x [1], :y [2 3]}


;; recursively remove empty sequences from map
(clojure.walk/prewalk (fn [value]
                        (if (map? value)
                          (remove-empty-sequences value)
                          value))
                      {:a {:x [1]
                           :y [2 3]
                           :z []}
                       :b {:w [:a :b]
                           :y '()}})
;; => {:a {:x [1], :y [2 3]}, :b {:w [:a :b]}}

1
Pablo21:03:28

Maybe

(defn dissoc-in-val
  [m path value]
  (let [map-value (get-in m path)]
    (if (coll? map-value)
      (let [without-value (remove #{value} map-value)]
        (if (empty? without-value)
          (medley/dissoc-in m path)
          (assoc-in m path without-value)))
      (medley/dissoc-in m path))))

👍 1
Muhammad Hamza Chippa21:03:51

how can I parse xml in clojurescript I have tried to use https://github.com/funcool/tubax in my shadow-cljs project but it appears to be broken and giving me very strange error "The required namespace "tubax.core" is not available, it was required by "cljs/user.cljs".

Michael Agres22:03:04

When I see the REPL display 'nil' after executing some code, is that similar to 'return 0' that one sometimes sees in the OO realm with methods that don't return anything?

emccue22:03:02

i'm not sure what OO Language you are referring to

>>> def a():
...     print("...")
...
>>> a()
...
>>> b = a()
...
>>> b
>>> print(b)
None
>>> type(b)
<class 'NoneType'>
>>>

emccue22:03:09

for dynamic languages "no return is implicitly null return" is pretty common

ahungry22:03:58

similar yes - in lisps (clojure included) the last form evaluation result is implicitly the return value of the function being called if that evals to nil, that's like 'false' or 'undefined' in most other langs, although clojure has false and nil (moved from non-thread to thread, my bad)

Michael Agres22:03:10

I think the last time I saw something like that was with Objective-C

quoll13:04:00

The answer here is “no”. Some languages have “void” as a return type for a side-effecting operation. That's not possible in Clojure, so you get the result of the last statements. io operations like println will return nil which may be where this question came from? But that can't be relied on. There are also legitimate reasons to return nil: it can be returned from a function that would typically return something else, and can indicate a missing value, an empty structure, a falsey value, and so on