Fork me on GitHub
#squint
<
2022-08-26
>
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?

borkdude09:08:20

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

borkdude09:08:14

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

๐Ÿ‘ 1
borkdude09:08:57

I don't want to make a big deal out of this, but here are some notes on partial from a recent conversation: https://gist.github.com/borkdude/b16118aa12af1dac3e487612a7936aad

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.

borkdude14:08:46

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

borkdude14:08:06

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

borkdude14:08:31

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

borkdude14:08:02

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

borkdude14:08:05

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

borkdude14:08:13

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

borkdude14:08:24

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?

borkdude14:08:26

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

borkdude14:08:23

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

borkdude14:08:03

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?

borkdude14:08:48

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

borkdude14:08:18

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

borkdude14:08:19

let's wait for lilac though :)

Cora (she/her)14:08:47

that sounds like the simplest solution to me

borkdude14:08:12

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

borkdude14:08:03

composing iterators likely also results in less garbage

lilactown17:08:32

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

lilactown17:08:54

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

lilactown17:08:18

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

borkdude17:08:18

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

borkdude17:08:27

and of course do the transducer thing too

borkdude17:08:49

clojure 1.12 will also have partitionv

lilactown17:08:11

what's changed since we made that decision?

lilactown17:08:38

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?

borkdude18:08:10

@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.

borkdude18:08:43

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

borkdude18:08:31

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.

borkdude18:08:01

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

borkdude18:08:44

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

borkdude18:08:07

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

borkdude18:08:57

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

lilactown19:08:45

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

lilactown19:08:58

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

borkdude19:08:28

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

borkdude19:08:48

yes, we need to consider the pros and cons carefully

borkdude19:08:45

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

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

lilactown19:08:23

I know that React handles iterables pretty well

borkdude19:08:00

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

lilactown19:08:01

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

borkdude19:08:11

that's cool then

lilactown19:08:57

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

lilactown20:08:49

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

๐Ÿ‘ 1
borkdude20:08:51

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

borkdude20:08:57

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

lilactown20:08:39

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 ,,,)

borkdude20:08:40

we could do that for the clava.transducers counterparts

lilactown20:08:26

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

lilactown20:08:01

esp. if we have support for lazy seqs

borkdude20:08:28

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

borkdude20:08:42

I get what you're saying about the different namespace

borkdude20:08:11

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

lilactown20:08:14

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
lilactown20:08:10

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

lilactown20:08:56

it is a decent amount of code for each operation tbh

borkdude20:08:18

operation = map, filter, etc?

borkdude14:08:05

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.

borkdude14:08:19

Let's continue in thread ๐Ÿงต

borkdude15:08:30

I think I could use eslint

borkdude15:08:49

or just load react and render something

borkdude21:08:20

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

borkdude21:08:25

and sequence

lilactown21:08:43

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

lilactown21:08:53

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

borkdude21:08:02

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

borkdude21:08:22

You can reset to a5316d48bac64f2ad1a8bdb14aeb2445adb8e505

borkdude21:08:51

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

lilactown21:08:24

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

borkdude21:08:44

I usually only push from the command line

borkdude21:08:46

I think now you accidentally removed commits from main

lilactown21:08:56

I missed comp

lilactown21:08:01

should be right, now

borkdude21:08:33

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.

lilactown21:08:23

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

borkdude21:08:35

because you're an admin

lilactown21:08:45

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

borkdude21:08:46

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?

borkdude21:08:02

@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?

lilactown22:08:10

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

lilactown22:08:23

my goal is to maintain interop with things like ramda

lilactown22:08:54

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

borkdude22:08:26

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

borkdude22:08:23

also, still interested in the answer to this: https://clojurians.slack.com/archives/C03U8L2NXNC/p1661550542608869?thread_ts=1661550226.814589&amp;cid=C03U8L2NXNC - wouldn't this already get you most of the benefits?

lilactown23:08:38

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

lilactown23:08:51

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

lilactown21:08:12

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

borkdude21:08:53

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