Fork me on GitHub
#beginners
<
2023-10-07
>
Matt LeVeck06:10:32

I'm wondering how, when calling Java methods, to deal with the fact that Java dispatches on argument type, and it seems like Clojure maybe only dispatches on arity? Example to follow in the thread

Matt LeVeck06:10:54

(import '(java.nio.file Paths Files))
(let [path "src/foo.clj"]
(Files/readAllBytes (Paths/get path)))
fails with: java.lang.ClassCastException: java.lang.String cannot be cast to java.net.URI even though Paths.get takes both strings and URI objs

Matt LeVeck06:10:06

I then tried something like:

(import '(java.nio.file Paths))

(let [path #^String "src/foo.clj"]
  (Paths/get path))

Matt LeVeck06:10:43

but that fails with: Metadata can not be applied to "src/foo.clj". Metadata can only be applied to IMetas.

Matt LeVeck06:10:04

(let [path ^String "src/foo.clj"]
  (Paths/get path))
gives ; (err) : java.lang.String cannot be cast to clojure.lang.IObj foo

Matt LeVeck06:10:23

oh... ok thanks

Matt LeVeck06:10:05

I mean, I know the signature. But since in Java Paths.get("mypath") works, I didn't think that I needed a vector arg the second array of String arg.

seancorfield06:10:00

Varargs in Java is tricky from other languages because of the implicit array argument. Some of the changes coming in Clojure 1.12 may improve that in some cases, and more work on varargs is planned for 1.13.

Matt LeVeck06:10:47

Thanks so much for the help both of you.

sarna09:10:06

hi! I have a couple programs I'd like to implement in clojure, but they all currently have a hot loop in which the "state of the world" is updated. it's the bottleneck of the whole program - it takes the world state and updates it on an every tick. imagine an emulator that has to take the user input & the state, update it, and render a frame. is there any good way of doing that in clojure, without creating a ton of garbage while updating the big "state of the world" map in a tight loop?

daveliepmann10:10:31

there are volatiles (scroll down a bit on https://github.com/clojure/clojure/blob/master/changes.md#21-transducers or see https://clojuredocs.org/clojure.core/volatile!) but i would recommend first trying to implement it without them to see if the jvm can handle the garbage produced from normal swap! with e.g. assoc

daveliepmann10:10:00

sorry, I'm a bit zonked — https://clojure.org/reference/transients would be another option between those two

sarna11:10:25

volatiles look interesting, thanks! I've never actually used transients, but to me it seems like you need to "freeze" the value after constructing it, is that correct? I need to continuously modify the same value and observe it between modifications, I'm not sure if transients support that

phill11:10:34

There are transducers, which emit less garbage than sequences. And a Clojure program may use Java arrays, arraylists, maps, and sets if you feel they're faster than immutable persistent collections. But keep in mind that short-lived garbage doesn't cost much in the JVM, and "Premature optimization is the root of all evil" (Donald Knuth).

2
sarna11:10:32

oh, transients aren't write-only even before they're made persistent - cool! and yeah, I agree - I should stop overthinking and just write the code & profile. if I can't make it fast enough I'll pester you guys again 😉 thank you!

James Amberger13:10:10

;; a.clj
(ns A)

(def animal "whale")

;; b.clj
(ns B)

(println A/animal)
I was expecting failure when I eval’d the last statement in B at the repl because I haven’t required A , but it worked. I am perplexed!

Alex Miller (Clojure team)13:10:45

Once a namespace has been loaded/defined, it’s symbols can be resolved

Noah Bogart15:10:57

I have a function that thread-firsts 9 update calls, each updating a different key and none of the updates rely on the work of the previous calls. Some of the calls are expensive, but they’re all pure functions. Is there any benefit to parallelizing the calls? Is there an existing method of performing such parallel updates? Reducers seemed focused on sequences

Coby Tamayo15:10:30

you'd need to benchmark it to see the benefit, but if all you're doing is setting independent keys, you can mutate your map in-place as a https://clojure.org/reference/transients, distributing the work across a (managed) thread pool using parallel future:

(defn expensive-op1 [m a]
    (assoc! m :a a))

  (defn expensive-op2 [m k v]
    (conj! m [k v]))

  (let [m (transient {})
        updates [[expensive-op1 "A"]
                 [expensive-op2 :b "B"]]
        updated (doall (map deref (for [[f & args] updates]
                                    (future
                                      (apply f m args)))))]
    (persistent! m))
Each (future ...) call returns immediately while work is performed concurrently on a different thread. The map deref causes it to block until all (future ...) calls have finished, and the doall forces eagerness since map is lazy by default. Based on: https://levelup.gitconnected.com/back-to-the-future-in-clojure-934b85a3d08e

Noah Bogart15:10:21

Is that safe? Transients still need their results to be used.

Coby Tamayo15:10:53

hmmm I guess not > Transients require thread isolation

Ben16:10:41

I think this is a silly question but I don't understand why this doesn't work as I hope it to.

(map #(int (.charAt % 0)) '("A" "B")) => (65 66)

(let [A "A" B "B"] (map #(int (.charAt % 0)) '(A B)))
=> Error printing return value (IllegalArgumentException) at clojure.lang.Reflector/invokeMatchingMethod (Reflector.java:127). No matching method charAt found taking 1 args for class clojure.lang.Symbol
It looks like it's taking A and B as symbols instead of evaluating them, but I can't switch '(A B) to (A B) because then it tries to evaluate the list. Is there some other syntax I'm missing here?

1
seancorfield16:10:24

(list A B)

‼️ 1
seancorfield16:10:56

'(A B) means (quote (A B)) so there's no evaluation and you get a literal list with two symbols.

seancorfield16:10:36

In the same way that '(+ 1 2) produces a list of the symbol '+ and the literals 1 and 2 -- again with no evaluation.

seancorfield16:10:27

Folks normally use a vector for this purpose tho':

(let [a "A" b "B"] (map .. [a b]))
since map calls seq on its argument.

Ben17:10:43

Thank you, I misunderstood how quote worked. I was just thinking of it as a way to let me write a list literal and not treat the first element as a function but which would have its elements evaluated, expecting '(1 (+ 1 2)) => (1 3) but I see it's more serious than that.

seancorfield17:10:40

Yeah, quote means "Here's a literal symbolic form as a data structure!"

seancorfield17:10:50

You can think of '(...) as "distributing" quote over all the elements so '(+ 1 2) is the same as (list '+ '1 '2) -- and so '(A B) is the same as (list 'A 'B).

jpmonettas10:10:48

just for another option for building lists you can also do :

(let [A "A" B "B"] (map #(int (.charAt % 0)) `(~A ~B))) 
(65 66)

wow 1
roelof20:10:48

I have to iterate a loop till I see a particular entry. How can I do that the best in clojure ?

Bob B20:10:36

possibly filter , sort of depends on more context

Bob B20:10:25

or some:

(some #{5} [0 10 5 10 5]) 
;; => 5

Bob B20:10:13

for that, I'd probably use take-while

roelof20:10:47

oke, thanks , Then time to experiment with that

kennytilton20:10:29

some will stop at the first match.

Returns the first logical true value of (pred x) for any x in coll,
else nil.
filter would keep going. loop itself might be overkill: https://clojuredocs.org/clojure.core/loop

Bob B20:10:59

for example...

(take-while (complement #{"stop"}) ["a" "b" "stop" "c" "d"])
;; => ("a" "b")

roelof20:10:52

again thanks

👍 1
kennytilton21:10:21

take-while! I never saw that before. 🙏

Jason Bullers23:10:53

Let's say I need a classic OOP-style observer/observable type of thing to notify of property change. For example, in Java, I might have a Person on which I could addListener and register a callback that would be invoked when the person's name changes. In Clojure, I assume I would represent the person entity as a map, like {:name "Jason"}. Now I can update the name with assoc or update, but where do I put the callbacks? If I put my person in an atom, I can add a watch, but say I don't want an atom. Do I just add the callbacks to the entity map too, under their own key? That makes the map more than "data" since it's got functions in it (can't send that around as edn for example)

Jason Bullers23:10:17

Or as another example, think dynamic validation that can be added à la carte to an entity and has the chance to veto an update. Where do I register these validators for a specific entity?

phronmophobic23:10:06

> but say I don't want an atom why not? > Where do I register these validators for a specific entity? You can look at https://clojure.org/guides/spec and https://github.com/metosin/malli for inspiration. The key idea is that whether or not some information is valid depends on the context and use case. The simplest version of that is have a function that lives "near" (eg. same namespace) that returns whether some info constitutes a valid person (for a specific use case).

phronmophobic23:10:21

You don't want to couple validation to an entity, but instead, add validation as part of a process.

phronmophobic00:10:32

Maybe some context has expectations about what it thinks a user should be, but it's an architectural mistake to try and restrict all contexts from even describing or representing a Person in any particular way.

Jason Bullers00:10:13

From what I understand, an atom could be my application state, but I wouldn't have one for each individual entity. Another issue with watches is they don't allow vetoing. A common type of listener in GUI components is something that can see if what's been entered in the field is valid, and if not, the change isn't persisted and any listeners that want to know of the change won't be called. I'm kind of curious how this kind of thing works in Clojure.

phronmophobic00:10:15

Right, you would have some process that updates the atom and that process would be responsible for validating that the updates to the atom have valid data.

dgb2300:10:52

You can think of your GUI state as an in-memory database. Do you control the rendering or is it a web gui?

phronmophobic00:10:18

I know that doesn't sound like a straight answer, but there are lots of options that make sense depending on the context. One way to do it would be to have a validate function checks the data before it is committed (either to an atom via swap! or a local db or otherwise).

Bob B00:10:25

atoms can also have validators (to the vetoing thing)

Jason Bullers00:10:17

Hmm I see. I don't have any concrete examples, so I'm just using GUI as an analogy because they're a pretty OO model (even the browser is all about registering event handlers to elements that have properties). I'm trying to build up a mental model for how problems I'm used to tackling in Java land translate over. Having and object that emits events to zero or more interested parties is a pretty common pattern, so I'm just trying to get a sense of what kinds of analogs are available.

dgb2300:10:27

Have you ever looked at core.async?

Jason Bullers00:10:29

@U013JFLRFS8 yeah, but my understanding is that they either accept or throw, not veto which causes the update to be rolled back

dgb2300:10:37

you can model what you describe with channels for example

dgb2300:10:05

channels decouple process with communication

Jason Bullers00:10:22

Ah, right. So a channel is a sort of listener port, and anything connected to the other end is a listener

Jason Bullers00:10:50

That makes sense. Queues are a common enough pattern too. And that's basically what channels are I think

dgb2300:10:05

a pubsub style implementation would have unidirectional channels for listeners. The publisher pushes messages onto them

Jason Bullers00:10:29

So would my person map hold on to a channel?

dgb2300:10:35

interestingly its "i don't care" from both sides instead of just one side

Jason Bullers00:10:48

If different persons need different registered listeners?

dgb2300:10:15

if that's your model, yes

dgb2300:10:27

you can also filter over messages though

dgb2300:10:40

depends on more context I would say

Jason Bullers00:10:26

Right, cool. That's been one of the trickier parts learning: a lot of patterns and designs I'm familiar with kind of get turned inside out

Jason Bullers00:10:58

Thanks 👍:skin-tone-2:

dgb2300:10:05

have fun! 🙂

phronmophobic00:10:28

> So would my person map hold on to a channel? It would not. The person map does do anything. It just describes things. Separately, you have a process that uses information to make decisions (think of a flow diagram).

☝️ 1
dgb2300:10:50

I agree with this take

dgb2300:10:05

the communication mechanism can be thought of as a completely separate/generic thing

phronmophobic00:10:08

Just to go back to the GUI example. I actually think Model, View, Update (which was popularized by Elm) does a good job of describing the architecture of what a functional UI program looks like: As you can imagine, the update function can end up doing a lot, but what makes it tractable is that the "large" update function is composed of separate, smaller update functions. It can also be composed with validation.

phronmophobic00:10:07

Based on how complicated UI programming tends to be, it might seem like this process diagram is too small or leaving things out, but you can actually build "sophisticated" UIs with this simple process model.

dgb2300:10:42

The mechanics of the arrows can differ quite drastically though

Jason Bullers01:10:52

Even with Elm though, isn't there registered callbacks? Like the view has say two buttons: one button registers an onClick to fire a Decrement message and the other button registers an onClick to fire an Increment message. They're both just "button" entities, but they have different event listeners to send different messages. I do like the separation though. It seems quite clean.

phronmophobic01:10:59

Even with Elm though, isn't there registered callbacks?That's not Elm's fault. That's the price of building on top of HTML.

Jason Bullers01:10:45

I guess I'm just having a hard time imagining how you'd represent a button without it being an "object"

Jason Bullers01:10:11

Like if HTML wasn't a constraint, how would you handle onClick in a more functional way? Does that even make sense as a question?

phronmophobic01:10:51

It's a great question.

phronmophobic01:10:11

I wish more people would ask it.

phronmophobic01:10:36

I've tried my best to give an explanation on my blog, https://blog.phronemophobic.com/what-is-a-user-interface.html, but I still think the explanation could be improved.

phronmophobic01:10:05

I'll try to give a short answer. Basically, a button is just some data that describes a button. For example:

(ui/button "Click me" 
   (fn [] 
     ;; return some data about what should happen
     [[::click]]))

phronmophobic01:10:47

Most of the time, the data is just a title and a function that returns the user's intents for when the button is clicked.

phronmophobic01:10:32

I also recently gave a talk trying to outline how UIs could be more functional and solve some of the complexities from building on top of OO paradigms, https://www.youtube.com/watch?v=Mjn92fODdaA

👀 2
Jason Bullers02:10:59

Interesting talk. So, let me try and say it back to you. You're suggesting that a GUI in a more FP style would deal with callbacks by separating the action itself from some declarative representation of an action you'd like to have happen. So a button could say "when I get clicked, I'd like to declare that a to-do should be added", but there's no callback function that actually goes and adds a to-do anywhere. Separate from this, you could have functions that represent user interaction. Given a component and any relevant arguments (like mouse position), this function would obtain from the component it's declaration of what to do. And then presumably, somewhere else takes that declaration and maps it to actually doing something? (Not shown in your talk) But the key idea is that you defer that as long as possible so that you're dealing with as much pure function stuff as possible. In the Elm example, the onClick is "declarative" in the sense of just being a message to send, not a function that does stuff. But in Elm, that automatically implies an invocation of the registered update function, while you're suggesting that it could potentially be deferred further. Is that right?

phronmophobic03:10:49

Yes, I think that's a pretty good summary. > But the key idea is that you defer that as long as possible so that you're dealing with as much pure function stuff as possible. The main goal is to try and break User Interfaces down into simpler pieces to make them easier to reuse, test, compose, and reason about. It's also about trying to get to the essence of what user interfaces are actually about. A button 1) has a visual depiction and 2) maps user events like mouse clicks to user intents. That's it. Buttons should not be firing missiles. Another clarification I would add is that this is only about the mental model. There's a bunch of interesting optimizations I haven't tried (because I haven't needed to) that would allow you to analyze a given UI description and spit out an optimized version that speeds things up by directly connecting all the pieces.

👍 1
didibus19:10:12

If you don't have an atom, you are not updating the values, so there is no change to register a callback for. Without an atom, you're just copying the value, leaving the original as-is.

Jason Bullers19:10:11

How so? Say I want to run a reduction across some set of values and notify some interested parties each step of the way. And at the end, I take the result and write it to a database. No atom required, but notification should happen.

Jason Bullers19:10:02

I've seen some examples in the past of how you can start with an initial state and reduce through all the states of the application (or at least for a particular set of things). Then, separately, you can walk those transformations and do your side effects. No atoms involved. So I suppose the argument may be that doing this doesn't really require registration of listeners? You get kind of the same by just dispatching the states after the fact via some pool of functions.

Jason Bullers19:10:28

You still need a way to register that pool of functions though, and define what they care to listen to. Fwks like reframe (I'm very new to it, so forgive me if I'm missing something here) seem to cover this idea with central registration points of functions for messages. I guess that's basically "listeners", just global rather than per object

didibus20:10:32

Well, then the change happened only once you wrote to the DB. So you would register for a change callback on the DB itself.

didibus20:10:48

Assuming you care about application state changes. You'd want the callback to register when the app state is changed. That's either in your DB, an atom, or a shadowed loop/recur var of some sort.