Fork me on GitHub
#clojurescript
<
2020-03-03
>
deep-symmetry13:03:30

I just realized I should probably announce that as part of building the byte field diagram generator I’m creating to port a big LaTeX document to Asciidoctor/Antora, I have ported Analemma (https://github.com/liebke/analemma) to .cljc so it works in ClojureScript as well as Clojure, and (with David Liebke’s permission) posted a canonical release to Clojars.

p-himik14:03:24

That may be worth posting at #announcements

deep-symmetry15:03:19

Ah, I wasn’t even aware of that channel, I see #admin-announcements but hadn’t yet joined the other one, thanks!

folcon19:03:47

Any equivalent of extends? in cljs? Or at least better than:

#(satisfies? protocol (new %))
I’ve been digging around and I can’t seem to find anything, other than perhaps sticking the above in a macro.

lilactown19:03:07

what are you trying to do?

lilactown19:03:59

AFAIK in general, you can’t know that a type or record implements a protocol without constructing it, because under the hood a type / record is just a constructor function which returns an object

lilactown19:03:54

this is different than the JVM, where a class is an actual concrete concept in the runtime

lilactown19:03:44

I think it’s usually best practice to pass around instances of types/records rather than the type/record itself

folcon19:03:45

I’ve got something that’s really similar to an ECS style system, so I’m using protocols to group together related functionality. I don’t want a situation where adding a new type of record (which is just a map after all) requires me to change a bunch of calls sites when what I’m interested in is stuff like, now call all the physics stuff, now call all the render stuff…

folcon19:03:50

Let me know if I’ve been unclear =)…

lilactown19:03:16

protocols should solve that, yes

lilactown19:03:50

it’s unclear to me why you would pass the Record constructor in rather than an instance of the record into a call site

lilactown19:03:37

> a new type of record (which is just a map after all) a “type of record” is not a map though, it’s a function. an instance of a record is the one that implements all of the same protocols as a map

lilactown19:03:51

(defprotocol IRenderable
  (-render [this]))

(defrecord Player []
  IRenderable
  (-render [this]
    ,,,))

(defn render [renderable]
  {:pre [(satisfies? IRenderable renderable)]}
  (-render renderable))

(render (->Player))

folcon19:03:22

So I have :entities which is a collection of different records that all implement the IEntity protocol, however certain functions, like ai, will only want to loop through stuff with IMonster. Now finding all the records which implement a protocol by looping through the entities list is expensive, so I have a :entities-with-component, which is a map of types of entities to a collection of entity id’s of that type, then I can quickly get by the type I’m interested in and then do a get to turn the entity-ids into entities. Now I’m going a step beyond that, by using extends I can say give me all the entities that implement this protocol. But I can’t do that in cljs without doing #(satisfies? protocol (new %)).

folcon19:03:18

I mean if I could take a record and get the protocols it implements that might work too? But it feels a little bit more messy…

bfabry19:03:26

"Now finding all the records which implement a protocol by looping through the entities list is expensive" is it? if that's really true then I'd suggest creating a second map of protocol->entities (there's no real reason to use id's with persistent data structures). I'm ever so slightly skeptical of this performance issue though 🙂

lilactown19:03:01

it sounds like you’re trying to create a mapping on top of the component->entity mapping, behavior->component->entity . but why not behavior->entity?

bfabry19:03:31

oh it's a game and your entities probably have some sort of state associated with them, that explains the id thing

lilactown19:03:01

you’re losing out on all the goodness of protocols by trying to map behavior to concrete types

folcon20:03:54

@lilactown As in for every behaviour create a reference to the entity? Not thought it about it. Will give it a try.

colinkahn21:03:18

Are there any safe ways to redef functions when using core.async go blocks in ClojureScript? I ran into the same issue found here https://clojure.atlassian.net/browse/CLJS-884 which doesn’t provide any alternatives. I ended up making a test fixture that resets the values after each test but wondering how other people are doing this.

dnolen21:03:41

I do it with fixtures as well

lilactown21:03:55

depending on what level of abstraction you are operating at, you can create a macro or function that re-binds the values at each async barrier.

(let [dynamic-var *dynamic-var*]
  (go
    (binding [*dynamic-var* dynamic-var]
      ,,,))
I’ve used this to similar effect when creating a data fetching framework that worked with server-side rendering, isolating the cache between requests

lilactown21:03:16

this doesn’t work as a general way to redef/rebind things you don’t control that use core.async

colinkahn21:03:49

@lilactown right, I can introduce some code specific things, I was considering just making a version of the function that got passed all deps so I could just provide them.

colinkahn21:03:29

the fixtures method definitely works, gives me some confidence if you’re doing the same @dnolen 😄