Fork me on GitHub
Chris McCormick09:08:19

One small data point: I use partial quite a lot. Maybe this is a bad habit. Do most people just use anonymous function shorthand instead?


I can't speak for most people but there are pros and cons of not using it. I think the pros of avoiding it outweigh the cons but some other people think the reverse


I think we should just support it in the same way that CLJS does it.

๐Ÿ‘ 1

I don't want to make a big deal out of this, but here are some notes on partial from a recent conversation:

Chris McCormick09:08:41

This is super useful thanks!

Prabhjot Singh14:08:27

general rule in clava is that functions take iterables and return array. now there is a situation with the following code (take 2 (partition 2 (repeat 1))) The partition functions try to put everything in an array and fails. Please provide your feedback as to what is the prefered approach here.

Prabhjot Singh14:08:56

should the partition function be lazy just like repeat. Or should it be lazy if the input is not finite.


Great question. Maybe it would be more consistent if all of these returned iterables


One potential footfun could be that one tries to re-use the iterator more than once


but when one wanted a concrete array, one could do (into []) probably which is then safe to use more than once


Let's continue in this thread. @corasaurus-hex @lilactown

Cora (she/her)14:08:34

using iterables all the way through and then relying on the user to call vec or do (into [] does seem like a viable solution


and it would not be breaking if we decide later on that take etc will return a LazySeq-like thing

Cora (she/her)14:08:13

that or getting rid of infinite sequences


yeah, I think it's convenient to have infinite sequences for things like:

(map (fn [i e] ..) (range) coll)

Cora (she/her)14:08:18

I wonder if we should make something like a cloneable iterable, so that in iterable-consuming functions we could clone the iterable to preserve the original


or (interleave (repeat :foo) coll)

Cora (she/her)14:08:39

I'm not sure how to make iterables cloneable but yes

Cora (she/her)14:08:56

I do love infinite sequences, they're so useful

Cora (she/her)14:08:55

or maybe we require chains of iterables to be called with some containing function similar to comp?

Cora (she/her)14:08:24

I'm interested to hear @lilactown's thoughts on this

Cora (she/her)14:08:48

or make iterable-returning versions of these functions and users need to be aware of which they're using?


clava.iterable and clava.transducers ?

Cora (she/her)14:08:44

like repeat can always return an iterable but we could have an iter-partition that could return an iterable instead of an array


but repeat can also return a finite array: (repeat 10 :foo)


it would be damn convenient if you didn't have to import another namespace for this, since it's so much used in CLJS

Cora (she/her)14:08:05

maybe have iter-repeat with arity of 1 and have repeat have an arity of 2?


this would be a bit annoying with tools like clj-kondo which already know the types of repeat with 1 argument

Cora (she/her)14:08:49

or make repeat 's arities return different types (iterable for 1, array for 2)

๐Ÿ‘ 1
Cora (she/her)14:08:00

yeeeaaaaahhhhh that's true

Cora (she/her)14:08:33

even with an arity of 2 you might not want all of repeat's available values


I think for now it would make a lot of sense to return iterables and concretions should be done using into or vec


let's wait for lilac though :)

Cora (she/her)14:08:47

that sounds like the simplest solution to me


I think we could implement custom data structures that cache their results, but there's also a trade-off: using iterators directly is likely faster and the 99% pattern in CLJS is probably to use composed lazy-seqs once... we could try this for a while and when it doesn't turn out to be good enough, implement some custom data structure stuff


composing iterators likely also results in less garbage


i think we should push people to use transducers, if we have the choice


there was an explicit decision (i thought) early on that we would make core functions like map, filter, etc. eager


and tell people to use transducers for composing over potentially infinite sequences


well, we could reconsider making e.g. map iterable and mapv return an array


and of course do the transducer thing too


clojure 1.12 will also have partitionv


what's changed since we made that decision?


is it that people are trying to write code, and we don't have a solution for it yet because we haven't implemented transducers?


@lilactown I think the whole seq ~ iterator resemblence came later. The usage of iterator-returning functions like (range) came later. We have an oppurtunity to have a difference between map and mapv. Even with transducers, the input can still be a "lazy" iterator.


Having some seq functions be eager and others return an iterator could also be a little confusing.


Just because we made a decision early should not be a reason to stick with it, I'm learning new things as we go every day. Although we should document these choices to remember why we made them.


And this project doesn't promise any stability as of now, I suggest we take at least a year to figure out how to best to things


Also I wasn't that aware of JS iterators, coming from CLJS mostly, I've never really been exposed to them :-)


But there is a serious trade-off of course, having map be a function* with yield probably has performance overhead


Then again, you can use mapv for the array-based one


i'm not opposed to changing, just want to have a clear why


I think that the iterable & generator stuff wasn't on my radar as much either


one concern would be interop - does react etc. play well with iterator results rather than array results?


yes, we need to consider the pros and cons carefully


one pro would be more CLJS-like code, e.g. what @UGFGYK4GM posted:

(take 2 (partition 2 (repeat 1)))


I know that React handles iterables pretty well


yes, you could do this using transducers: (into [] (comp (partition 2) (take 2)) (repeat 1)) but it's more verbose and using transducers isn't always that important, until you need the performance


e.g. in CLJS you can return a lazyseq to React and since it's iterable, React will iterate it like any array


that's cool then


I remember Rich saying that if he could go back, he would have designed the core lib to focus on transducers over lazy seqs


there's a tension between supporting existing Clojure patterns vs. iterating on Clojure's original design

๐Ÿ‘ 1

He probably said that right after he introduced transducers, does he still say that today? And this doesn't change the goal of clava to re-use existing CLJS idioms in JS


I think we have some wiggle room to experiment with iterators for the non-v functions though


an alternative design i've thought about is taking over the 2-arity version of seq ops to compose transducers instead

(into [] (take 2 (partition 2)) (repeat 1))
the clunkiest part of reading & writing transducers to me is the (comp ,,,)


we could do that for the clava.transducers counterparts


I'm wondering about separating transducers... if we do that, I'm betting people will use them even less.


esp. if we have support for lazy seqs


I'm not so much for changing the semantics of core functions to behave totally different. clj-kondo will be very confused about this for example


I get what you're saying about the different namespace


perhaps the extra transducer code isn't that much of a deal to have it inside core.js


yeah i'm feeling pulled towards not changing anything about the core seq ops. 1-arity returns transducer, 2-arity does a lazy seq

โž• 1

I have a WIP of the transducer protocol. I'll open up a draft PR either today or tomorrow


it is a decent amount of code for each operation tbh


operation = map, filter, etc?


I'm making some improvements to JSX, there was one bug when you had a map as an attribute. Currently there are no tests for JSX, but I'm doing this:

$ ./node_cli.js --show --no-run -e '#jsx [:div {:dangerouslySetInnerHTML {:_html "<i>Hello</i>"}}]'
<div dangerouslySetInnerHTML={({ "_html": "<i>Hello</i>" })}></div>;
I wonder how I could make unit tests for this to verify the JSX is valid.


Let's continue in thread ๐Ÿงต


I think I could use eslint


or just load react and render something


Already loving it. Does transducer-js also support eduction ?


and sequence


I just fucked up main again. I thought I protected it...


you'd think I wasn't a professional software developer


I wonder how you get into such a situation. I usually start with a branch instead of committing on main locally


You can reset to a5316d48bac64f2ad1a8bdb14aeb2445adb8e505


If you want I can revoke your commit rights so you're forced to go through PRs ;)


I'm playing with some different magit config on my personal laptop


I usually only push from the command line


I think now you accidentally removed commits from main


I missed comp


should be right, now


I think you should enable this maybe?

Do not allow bypassing the above settings
The above settings will apply to administrators and custom roles with the "bypass branch protections" permission.


I guess so? IDK why it would allow me to bypass it


because you're an admin


it also prevented me from force pushing with whatever the default is, but didn't prevent me from pushing lol


btw, transducers-js seems a lot of code... are you going to implement all of that inside of clavascript - or should we recommend people who want to use transducers to actually use that library?


@lilactown when map would return an iterator, and one would do (vec (map inc (map inc (range 1000)))) - what would be the benefit of the transducer approach compared to this?


I think I would implement all the core transducers in clavascript using JS


my goal is to maintain interop with things like ramda


I assume you're talking about including the cognitext/transducers-js lib?


No, I mean, if people want to have the transducers functionality, they could also just use rambda or transducers-js directly


also, still interested in the answer to this:;cid=C03U8L2NXNC - wouldn't this already get you most of the benefits?


all the same reasons you would weigh transducers vs lazy seqs in clojure


maybe we should benchmark it. it's possible that generators are more performant than lazy seqs


the issue seems to happen when: 1. I have two remotes: origin which points at my fork lilactown/clavascript, and upstream which points at clavascript/clavascript 2. I fetch upstream/main and create a new branch 3. the default "merge" remote is set to upstream/main 4. I naively press P u like I've built my muscle memory to


I've now enabled "do not allow bypassing", maybe that'll help. No problem btw, these things can happen ;)