Fork me on GitHub
#clojurescript
<
2022-03-20
>
Ty01:03:48

Does anyone happen to know if the js/ts ecosystem has any analog for sequences?

valtteri12:03:28

Iterator protocol perhaps..?

Ty18:03:51

Hmmm you're probably right.

rayat20:03:02

There are libs that AFAICR approximate that, I think, assuming you mean generally, lazy iterators ( operations like map etc done lazily or aggregated unlike with regular js arrays ). However, I cannot remember šŸ˜ž as it's been a while. That being said I am interested in this somewhat, and if you're still looking it might be fun to try to help you find what you're looking for,

Ty21:03:22

Before I even asked here I made my own simple wrapper around an array. It's just a class that takes in an array and does a super naive index increment with a check to wrap back around to zero.

Ty21:03:14

There are some easy improvements over my current version just by using a better base structure like map/set. AFAIK internally js/ts probably just use arrays for the same, may an object as a hash map for quick access. To actually get some niceness in terms of operations and use, doing something based on iterators does seem viable. There's also generator functions, which are pretty under used in the js world but are probably perfect for this sort of thing.

Ty21:03:35

Here's the current version I'm using:

export class Sequence<T> {
    private index = 0;

    // TODO: Consider cloning array for safety
    constructor(public elements: Array<T>) {}

    take(n: Number): Array<T> {
        if (n < 0) return [];

        const nArray = [];

        for (let i = 0; i < n; i++) {
            nArray.push(this.consume());
        }

        return nArray;
    }

    public current(): T {
        return this.elements[this.index];
    }

    public consume(): T {
        const element = this.elements[this.index++];

        // Naieve Cycling
        if (this.index >= this.elements.length) {
            this.index = 0;
        }

        return element;
    }

    // Weirdly defaults to moving to 0 if element not found
    public moveTo(e: T) {
        const index = this.elements.indexOf(e);
        // Ternary to scrub default to 0
        this.index = index === -1 ? 0 : index;
        return this; // Hack for creation
    }

    public moveToIndex(i: number) {
        this.index = i;
        return this; // Hack for creation
    }

    public moveForward(n: number) {
        for (let i = 0; i < n; i++) {
            this.consume();
        }
    }
}

pinkfrog13:03:07

I have a shadowcljs browser project and connecting to the browser in a REPL. Say I type (println ā€œhiā€) in the repl, at where will the cljs content be transpiled into js code? On the browser side or on the NREPL server?

p-himik13:03:04

On the server.

pinkfrog14:03:40

If on the server, then why canā€™t we use macro inside a repl?

p-himik14:03:55

No clue about nREPL, but I was able to use a macro just fine with a regular REPL.

p-himik14:03:15

And there are no GitHub issues in the nREPL repo about not being able to use macros, so my guess is that there's something wrong in your setup or macro usage.

p-himik14:03:25

Ah, well - initially you wrote about "using a macro", not "using defmacro". Those two are completely different.

pinkfrog14:03:15

Yeah. I am not sure about this part. Whatā€™s the difference?

p-himik14:03:06

"Using defmacro" is writing code like this: (defmacro my-macro [form] (do-stuff-to form)). "Using a macro" is writing code like this, assuming the above is already written somewhere: (my-macro [1 2 3]).

p-himik14:03:07

I'm 70% certain that you can use CLJ REPL to create a macro NS and define a macro there, then start a CLJS REPL from within that CLJ REPL (so it shares the same process) and use that macro in there.

thheller17:03:47

yes, macros are written in CLJ. you cannot write them in a CLJS REPL

thheller17:03:55

a CLJ REPL is fine though

pinkfrog14:03:25

I primarily donā€™t understand why a cljs repl cannot write defmacro given that the underlying COMPILATION envrionment is on JVM, only the execution of code happens on browser or node.

thheller17:03:54

thats just how it works. macros run in the CLJ runtime so thats where they are defined.

ikitommi18:03:19

defs are still not subject to DCE? Is there an idiomatic way to write zero-arity`defn`s where the contents would be memoized? I have a lot of code in malli, which look like:

(defn string-transformer []
  (transformer
   {:name :string
    :decoders (-string-decoders)
    :encoders (-string-encoders)}))
ā€¦ which is subject to DCE, but all calls re-create the identically behaving instance. I guess I could write my own wrapper that stores those into custom atom:
(defn string-transformer []
  (memoizing ;; check first from cache 
    'string-transformer ;; .. with this key
    (transformer
     {:name :string
      :decoders (-string-decoders)
      :encoders (-string-encoders)})))
but, is there a better way?

p-himik18:03:39

delay might be more suitable.

ikitommi18:03:30

delay would not be subject to DCE?

p-himik18:03:51

DCE is apparently finicky enough, according to a discussion yesterday or the day before, so I can't say. And it probably depends more on what the functions you mentioned do rather than delay itself.

thheller06:03:38

yes, delay is eliminated if never used

thheller06:03:09

if you however put the even underef'd delay into a map or so that might not remove it anymore

thheller06:03:35

(def string-transformer (delay (transformater ...))) will be removed if string-transformer is never referenced anywhere

ikitommi10:03:40

oh, thatā€™s perfect, didnā€™t know that and itā€™s really easy to incorporate. Thank You! šŸ™‡

Richard Bowen18:03:26

Hey, I've come across a convention material-ui where a prop starts with a uppercase letter as opposed to the regular lowercase; it looks like this:

<StepLabel StepIconComponent={QontoStepIcon}>{label}</StepLabel>
It requires that the component be converted to a string. How do I achieve this with ClojureScript?

p-himik18:03:20

> It requires that the component be converted to a string What do you mean? QontoStepIcon is never converted to a string, which, I assume, is what you mean by "the component".

p-himik18:03:51

Also, the question would probably be better suited for #reagent, assuming that's what you're using.

Richard Bowen18:03:32

This is an example from the material-ui 4 docs. I'll clean up the question and move it to #reagent.

p-himik18:03:23

And if you just want to be able to supply the StepIconComponent property, you need two things: ā€¢ Passing "StepIconComponent" instead of :StepIconComponent - assuming that the latter doesn't work (I don't recall) ā€¢ Wrapping your own version of QontoStepIcon in reagent.core/reactify-component.

Richard Bowen19:03:33

In my case I'm just passing a function that defines an svg-icon component. I want to pass that icon to the StepLabel component to replace the default.

p-himik19:03:13

Well, my previous message is still applicable.

Richard Bowen19:03:00

That worked. Thanks. So what reagent.core/reactify-component does is turn a function/input into a react component.

Richard Bowen19:03:24

So could I have done (defn stepper-icon:f []) which would have the same effect?

p-himik19:03:42

I have no clue what you mean by that.

Richard Bowen19:03:43

I meant (defn stepper-icon:f []).

p-himik19:03:45

StepLabel expects a proper React component. Your stepper-icon:f is just a function, nothing more - why do you think it would work?

Richard Bowen19:03:15

the :f , doesn't it convert a function into a React component?

p-himik19:03:49

First time I hear about it. And Reagent source code doesn't seem to have anything that would work that way. But it does have :f> however. But that's for the reverse - for using functional components in Reagent hiccup vectors.

rayat21:03:14

Hey folks. I love typescript, and I love clj so far. I have a broader question/discussion I'm new to: (opt-in) Typescript-provided type hints/hover/static analysis/autocomplete in cljs code that uses npm+TS modules? I missed a lot this weekend working on a TS project (#calva in fact) from the clj world. However, one thing I did not miss, and in fact desperately wish I could address, was the nightmare of wide refactor work without static type analysis in clj/s. In this TS project, I just had to change a single type definition in a single location, and simply pressed F8 (Go To Next Problem in vscode), fix how the symbol is referenced, and repeat until the project built and ran without issue. Pure bliss. However, in my expansive work codebase with tons of existing references to symbols and heavy reliance on TS type-definition-providing npm libraries, I cannot take advantage of said static analysis. While working in clj is in so many ways so very elegant and poetic almost, I felt like I spent 70% of some recent refactor work (where a react component dependency of ours changed types/props) not actually updating logic, but simply finding references to outdated symbols (and amusingly, misspellings thereof), and ascertaining how we utilized them, eg what entries were passed in maps that were simply spread into arguments or props, etc. This is the kind of thing that I'd done hundreds of times in in other languages, and why TS became my new gospel over the past couple years. I can always opt-into TS definitions, and get as strict or defined as I wanted, which allowed me to iterate and experiment quickly, and yet harden the logic and APIs for collab, future-proofing, self-documenting and basic bug catching etc as I would get more confident with my experiments. This gave me the idiot-proof refactoring experience I need, and the fluid move-fast-break-things I enjoy. Is it possible to hack on clj-lsp or calva or something, to get such features from TS type defs of npm packages? Maybe even just hover to start with?

p-himik21:03:45

> Is it possible to hack on clj-lsp or calva or something Probably, but sounds like it would be better asked in #lsp or #calva since what you describe is very tool-specific and not language-specific.

p-himik21:03:17

Also, just as an extra bit of info - I doubt there will be significant support for this feature. Interop with JS is somewhat niche, and it's currently non-existing with TS simply because CLJS compiler can't consume TS code at all. And CLJ community itself is also somewhat type-averse, understandably so.

rayat22:03:34

Thanks @U2FRKM4TW for the pointers, will x-post! Was imagining though that #clojurescript members have had more interop exposure than #lsp or #calva. FWIW my post has 2 agendas: 1. To find others who a. agree with my aims suggested above i. and have tips for scratching that itch b. or pointers to existing works that have started to 2. And/Or to pick the brains of people who have: a. both i. written robust projects from scratch ii. and have made large, wide-ranging contributions to very large existing codebases, b. in both i. clojure/script ii. and Typescript 1. (as opposed to Java/C#/really type-intensive langs where type-aversion feels less controversial in comparison) c. So that I can gain perspective, tips, advice, tooling, etc or simply their thoughts in general. Sorry for the verbosity, anyways, thanks for the response!

mruzekw00:03:16

Maybe look into any projects that take *.d.ts files and convert them to https://clojure.org/guides/spec or https://github.com/metosin/malli definitions. Both have a way to spec functions. If there isn't one, there's an opportunity to contribute. Then existing tools like clojure-lsp could pick up the clojure.spec definitions and provide some of the hints you're looking for.

mruzekw00:03:27

As for in-project constraint/contract checking, look into using clojure.spec or malli with "instrumentation". This would run the specs in your dev version of the app, admittedly at runtime, but you could write and run a test suite with instrumentation and have a static-like type experience that way.

mruzekw00:03:35

If you REALLY want to go the typed route, there's https://typedclojure.org/, but I have no idea of the status of the project.

mruzekw00:03:50

I would encourage you though to investigate why Rich Hickey and the Clojure community have opted for specs over static typing. Check out the videos: https://www.youtube.com/watch?v=YR5WdGrpoug https://www.youtube.com/watch?v=2V1FtfBDsLU

mruzekw00:03:49

That way you have a deeper understanding of the trade-offs Clojurists are thinking about when it comes to choosing Clojure over a statically typed alternative.

mruzekw00:03:07

My stance, at least currently, is that we can get pretty close to and even farther than a statically typed lang like TS. Consider https://practical.li/clojure/clojure-spec/ and specs including checks beyond just types. Not to mention that we can rarely guarantee that our types are 100% correct 100% of the time for outside data coming into the system, making runtime checks much more valuable.

mruzekw01:03:26

That of course comes with some perf trade offs in dev and testing. But the beauty of specs are you can pick and choose where to use them. Unfortunately I can't speak to all this in practice in the large scale. So someone else will need to chime in there.

mruzekw02:03:28

Also, have a look at this link for a static typing like experience https://github.com/jeaye/orchestra

teodorlu06:03:17

With Clojure-LSP you get some refactorings for free. Rename symbol is one. I bet more can be added. I see your point about wanting to address the larger community - but I still think you should make those who follow #lsp and #calva aware of what you want.