Class-based Tree Shaking - HTTP203

แชร์
ฝัง
  • เผยแพร่เมื่อ 6 ก.ย. 2024
  • Jake Archibald and Surma talk tree shaking in this episode of HTTP203. And show you some code! For the first time. Lovely.
    Subscribe to Chrome Developers here: goo.gl/LLLNvf
    Watch more HTTP203: goo.gl/bTQMrY
    Listen to the HTTP203 podcast for a full hour conversation & more: goo.gl/LR7gNg
    Itunes: goo.gl/cf2yRq

ความคิดเห็น • 109

  • @MrDomSan
    @MrDomSan 4 ปีที่แล้ว +11

    It absoluteley is possible for typescript, here you go:
    type ParamsOf = T extends (...args: infer P) => any
    ? P
    : never
    type ReturnOf = T extends (...args: any[]) => infer R
    ? R
    : never
    class DoClass {
    do(func: T, ...args: ParamsOf): ReturnOf {
    return func.call(this, ...args)
    }
    }
    const x = new DoClass()
    function hallo(a: string, b: number, c: Error) {}
    x.do(hallo, '', 2, new Error())

  • @nhardy96
    @nhardy96 6 ปีที่แล้ว +13

    As of TypeScript 3.0, it is now possible to do what you want using the new and improved tuple features (albeit without JSDoc and autocomplete).

  • @avilde
    @avilde 6 ปีที่แล้ว +27

    Great video! Certainly a great improvement to show code :)

  • @Hector-bj3ls
    @Hector-bj3ls 4 ปีที่แล้ว +7

    You can do that in TypeScript now.
    function do(doFn: (...args: A) => R, ...args: A): R { return doFn(...args); }

    • @amatiasq
      @amatiasq 4 ปีที่แล้ว +1

      works nicely, sadly argument names are missing though
      I'd use it in a pet project anyway

    • @Hector-bj3ls
      @Hector-bj3ls 4 ปีที่แล้ว +1

      @@amatiasq Yes, that's the only real problem with it now. It would be a nice little improvement from the TypeScript team if they were to implement it.

    • @Luxalpa
      @Luxalpa 4 ปีที่แล้ว +4

      @@amatiasq `do any >(func: T, ...args: Parameters): ReturnType` works with argument names as well.

  • @jackninja1
    @jackninja1 6 ปีที่แล้ว +4

    Yes, thanks for listening to the feedback, great video!

  • @ShaneLawrence
    @ShaneLawrence 6 ปีที่แล้ว +4

    Well, yes, the code samples are a great addition, but where can we get the Downasaur and awkward family portrait coasters? 🤔

  • @DanielPopeski
    @DanielPopeski 6 ปีที่แล้ว +15

    That pipe operator is a beautiful beautiful thing in Elm and languages alike. It also goes hand-in-hand with partial application which is kinda covered in JS with `bind`. Trust me fellas, you would very much enjoy it if you practice even a little bit of functional style transformations.

    • @dassurma
      @dassurma 6 ปีที่แล้ว +12

      Oh we do enjoy it, but the problem is that functional programming languages are very much designed to create one-off functions on the fly while in JavaScript those create actual garbage that needs to be collected. Syntactically I _love_ pipeline, but as we outline in the video, it’s really awkward to create functions that return functions and it’s a legit performance concern.

    • @BennyPowers
      @BennyPowers 6 ปีที่แล้ว

      how big of a performance concern?

    • @medikoo
      @medikoo 6 ปีที่แล้ว

      I thought that this video neatly shown how ugly (in JavaScript context) it is ;-)
      Nevertheless let's bring ::bind! it's pure, simple, readable, and very very useful.

  • @mpcref
    @mpcref 2 ปีที่แล้ว

    One of the many reasons why I like Haxe over TypeScript.
    Haxe has had Static Extensions since forever. (the "using" keyword)

  • @returnzero7492
    @returnzero7492 2 ปีที่แล้ว

    Hey guys! Just want to drop an example. It is possible to do the 'do' function in typescript now.
    const kRecords = Symbol('records')
    export type StoreFunction = (...args: any) => any
    export class Store {
    public [kRecords]: Record
    constructor() {
    this[kRecords] = {}
    }
    do(fn: S, ...args: Parameters): ReturnType {
    return fn.apply(this, args)
    }
    }
    export function get(this: Store, key: string) {
    return this[kRecords][key]
    }
    export function set(this: Store, key: string, value: T) {
    this[kRecords][key] = value
    }
    // Meant to be in another file
    const main = () => {
    const store = new Store()
    store.do(set, 'Hello', 'world!')
    store.do(get, 'Hello')
    }
    if (true) {
    main()
    }

  • @feldinho
    @feldinho 6 ปีที่แล้ว +1

    I don't see why can't the set function receive 3 arguments... it's easier to grasp than almost all the alternatives.
    Other than that, partial application is a beautiful thing once you get the hang of it! Right now it's not the more performant way but I'm sure the engines will optimize them in no time!

  • @marcelmundl9012
    @marcelmundl9012 ปีที่แล้ว

    Here is the TS implementation:
    ```ts
    function do(this: TThisType, f: (this: TThisType, ...args: TArgs) => TReturnType, ...args: TArgs) {
    return f.call(this, ...args);
    }
    ```
    You would use it like this:
    ```ts
    function set(this: Store, key: string, value: string) {
    this._innerDb.set(key, value);
    }
    class Store {
    _innerDb = LocalStorage;
    do = do;
    }
    const store = new Store();
    store.do(set, "foo", "bar");
    ```
    The only (but IMHO significant) downside to this is that members of Store cannot be private anymore. You would essentially be stuck with classes which behave like structs in C.

  • @BennyPowers
    @BennyPowers 6 ปีที่แล้ว +2

    @10:40 TypeScript: "It's definitely more complicated than it should be" 🤣

  • @xaviergonz244
    @xaviergonz244 6 ปีที่แล้ว +5

    It can be done in TS 2.8, but you would need to wrap the args inside an array
    type ArgsOf any> =
    T extends (this: any) => any ? never :
    T extends (this: any, a1: infer A1) => any ? [A1] :
    T extends (this: any, a1: infer A1, a2: infer A2) => any ? [A1, A2] :
    T extends (this: any, a1: infer A1, a2: infer A2, a3: infer A3) => any ? [A1, A2, A3] :
    T extends (this: any, a1: infer A1, a2: infer A2, a3: infer A3, a4: infer A4) => any ? [A1, A2, A3, A4] :
    // create overrides up to A10 for example
    any[];
    class Store {
    do any>(func: F): ReturnType; // no args
    do any>(func: F, args: ArgsOf): ReturnType; // args in an array
    do(func: (this: Store, ...args: any[]) => R, ...args: any[]): R { // base generic case
    return func.call(this, ...args);
    }
    }
    function set(this: Store, x: number, y: string) {
    // do something
    }
    function clear(this: Store) {
    // do something
    }
    function anotherClassSet(this: string, x: number, y: string) {
    // do something
    }
    const store = new Store();
    store.do(set, [ 'ho', 'hi' ]); // won't compile, invalid arg type
    store.do(set, [ 5 ]); // won't compile, too few args
    store.do(anotherClassSet, [ 5, 'hi' ]); // won't compile, not a method for the Store class
    store.do(set, [ 5, 'hi', 8 ]); // won't compile, too many args
    store.do(set); // won't compile
    store.do(clear, []); // won't compile
    store.do(set, [ 5, 'hi' ]); // ok!
    store.do(clear); // ok!

    • @xaviergonz244
      @xaviergonz244 6 ปีที่แล้ว

      Assuming TS ever adds support for ...args: [number, string] or some such then it would be possible without the array wrapping

    • @dassurma
      @dassurma 6 ปีที่แล้ว +1

      Thanks for the detailed example!

    • @xaviergonz244
      @xaviergonz244 6 ปีที่แล้ว +1

      Another try, works as intended (the params do not need to be wrapped in an array), but it requires to add the possible methods that can be invoked to the "do" signature
      type FuncSignature =
      T extends (this: any) => infer R ? (fn: T) => R :
      T extends (this: any, a1: infer A1) => infer R ? (fn: T, a1: A1) => R :
      T extends (this: any, a1: infer A1, a2: infer A2) => infer R ? (fn: T, a1: A1, a2: A2) => R :
      T extends (this: any, a1: infer A1, a2: infer A2, a3: infer A3) => infer R ? (fn: T, a1: A1, a2: A2, a3: A3) => R :
      T extends (this: any, a1: infer A1, a2: infer A2, a3: infer A3, a4: infer A4) => infer R ? (fn: T, a1: A1, a2: A2, a3: A3, a4: A4) => R :
      // add more...
      T extends (this: any, ...args: any[]) => infer R ? (fn: T, ...args: any[]) => R :
      never;
      class Store {
      do: FuncSignature & FuncSignature = (func: Function, ...args: any[]) => {
      return func.call(this, ...args);
      };
      }
      function set(this: Store, x: number, y: string) {
      // do something
      }
      function clear(this: Store) {
      // do something
      }
      function anotherClassSet(this: string, x: number, y: string) {
      // do something
      }
      const store = new Store();
      store.do(set, 'ho', 'hi'); // won't compile, invalid arg type
      store.do(set, 5); // won't compile, too few args
      store.do(anotherClassSet, 5, 'hi'); // won't compile, not a method for the Store class
      store.do(set, 5, 'hi', 8); // won't compile, too many args
      store.do(set); // won't compile
      store.do(clear, []); // won't compile
      store.do(set, 5, 'hi'); // ok!
      store.do(clear); // ok!

    • @jakearchibald
      @jakearchibald 6 ปีที่แล้ว

      This gets even more complicated if any of the "methods" have a generic element to them

    • @dassurma
      @dassurma 6 ปีที่แล้ว +2

      Yeah, but who would do _that_? Come on.

  • @clo4
    @clo4 3 ปีที่แล้ว +1

    The Store.do method is now possible in TypeScript!

  • @heratpatel7174
    @heratpatel7174 6 ปีที่แล้ว +2

    yes, it is very understandable than before, please include code example in all sessions.

  • @saadabbasi2063
    @saadabbasi2063 5 ปีที่แล้ว +2

    Thank you sooo much, i have been scratching my head around this for around 3 days now, but honestly Store.do is what i liked the most, unfortunately will have to opt out the types 🙃

  • @victornpb
    @victornpb 6 ปีที่แล้ว +3

    The do() method makes "static" calls into "runtime" calls, it makes call stack unnecessarily polluted, and render autocomplete and jsdocs useless...

    • @wmhilton-old
      @wmhilton-old 6 ปีที่แล้ว +4

      victornpb In the age of Visual Studio Code, saying "can't be defined in TypeScript" is the same as saying "breaks autocomplete".

    • @jakearchibald
      @jakearchibald 6 ปีที่แล้ว +2

      Which is why I want the bind operator.

    • @thearj9248
      @thearj9248 6 ปีที่แล้ว +1

      What if, instead of a prototype.do method, you have a prototype.add method. Ex:
      import { get, Store } from './keyval.js';
      const theStore = new Store();
      theStore.add(get);
      theStore.get('hello');
      // It's like "do it yourself" classes.
      // prototype.add could be:
      add(method) {
      let name = method.name; // functions have a .name property
      this[name] = method;
      }

  • @BertVerhelst
    @BertVerhelst 4 ปีที่แล้ว

    I wonder if we couldn't get the static analyser to detect if any square bracket operators are used on the Store object. If there are, the Store is marked as a dirty object and can't be tree shaken. If there aren't then it can be tree shaken.
    This would probably solve 90% of the tree shake issues. And the remaining 10% can use the more obscure solutions you mentioned in the videos.
    I quess it would still be an issue for certain data type classes. For instance a List class that has a shift method, but also an index operator to get a specific item. Although you could replace the List[index] with List.get(index)

  • @CalvinMannedUp
    @CalvinMannedUp 5 ปีที่แล้ว +2

    Bit late to the party. But how about
    import { get, set, Store } from './idb-keyval.js';
    const store = Object.assign(new Store('my-store'), { get, set });

    • @amatiasq
      @amatiasq 4 ปีที่แล้ว

      well you'll have to do that for each instance and it's up to the devs to include only the methods that they are going to use

  • @FrederikDussault
    @FrederikDussault 6 ปีที่แล้ว

    Thanks for code examples. A real nice addition to your series.

  • @FauzulChowdhury
    @FauzulChowdhury 6 ปีที่แล้ว

    Something Wonderful just started in this series.

  • @nabbydude
    @nabbydude 6 ปีที่แล้ว +1

    There is a proposal for Variadic Kinds on the TypeScript Github (#5453) that would allow rest parameters to be defined as tuples, that (maybe with a little help from the new conditional types in TS 2.8) would make this possible.
    Its theoretically possible right now with an arbitrary amount of overloads, but that would be super ugly.
    Another option would be `store.do(set)("hello", "world")`, as `store.do(set)` would have the exact same signature as `set` itself.

    • @omri9325
      @omri9325 6 ปีที่แล้ว

      It can be achieved with overloads and be ugly, but who cares if it's being imported from a module/library that you as a user don't see it's code?

  • @JoVandenBerghe
    @JoVandenBerghe 6 ปีที่แล้ว

    Great video!
    You could also solve the reversed order of nested arguments: h(g(f())) with a pipe function (not operator) that accepts 'pipeable functions': pipe(f(), g(), h()), like it is done in RxJS or callbags.
    This can be strongly typed without Variadic Types. see following code sample:
    You can copy this to the 'Typescript playground' and change the order of the pipeable functions and you will get errors.
    /** describes a function with one argument */
    type TFn = (arg: TIn) => TOut
    /**
    * Pipe function as alternative of pipe operator: |>
    * Pipe accepts pipeable functions
    * A pipeable function is a function that returns a function that only accepts 1 argument (= the result of the previous function)
    */
    function pipe(startValue: TIn, fn1: TFn): TOut;
    function pipe(startValue: TIn, fn1: TFn, fn2: TFn): TOut;
    function pipe(startValue: TIn, fn1: TFn, fn2: TFn, fn3: TFn): TOut;
    function pipe(startValue: TIn, fn1: TFn, fn2: TFn, fn3: TFn, fn4: TFn): TOut;
    function pipe (startValue: any, ...fs: Function[]) {
    return fs.reduce((prev, current) => current(prev), startValue);
    }
    // Some sample pipeable functions that change types
    const parseNumber = () => (s: string) => parseFloat(s); // string -> number
    const plus = (valueToAdd:number) => (num: number) => num + valueToAdd; // number -> number
    const formatCurrency = (prefix: string, suffix = "") => (valuta: number) => `${prefix}${valuta}${suffix}`; // number -> string
    // A pipe example
    const result = pipe(
    "1", // "1"
    parseNumber(), // 1
    plus(2), // 3
    formatCurrency("€"), // "€3"
    )
    alert(result); // €3

  • @wrburdick
    @wrburdick 6 ปีที่แล้ว +1

    Partial application from the first version of |> is great but when would someone choose to use the second version to write a |> b(?,c) instead of just b(a,c)?

    • @dassurma
      @dassurma 6 ปีที่แล้ว

      What does your last syntax mean? The `?` denotes what parameter the left-hand side should be used as.

    • @wrburdick
      @wrburdick 6 ปีที่แล้ว

      Sorry, I meant the last question mark as an actual question mark, not part of the syntax. I'm asking when someone would use
      a |> b(?, c)
      instead of just
      b(a, c)
      Does the |> operator do anything other than just plug the left hand side into the question mark?

    • @dassurma
      @dassurma 6 ปีที่แล้ว

      No, but it’s much clearer to read if you write pipelines and allows you to re-use existing functions where you don’t necessarily have control over where the argument is. E.g: `canvas |> turnToStream() |> compress(?, 'gzip') |> copyToServer('myServer.com', ?) |> putIntoCache('my-cache', ?)`

    • @wrburdick
      @wrburdick 6 ปีที่แล้ว

      OK, that's what I wasn't seeing. Like Clojure's threading macros

    • @stragerneds
      @stragerneds 4 ปีที่แล้ว

      Yes please! b(a,c) looks like a great solution for the video's example.

  • @InderjitSidhux
    @InderjitSidhux 6 ปีที่แล้ว

    This is much better than before.

  • @roceb5009
    @roceb5009 6 ปีที่แล้ว +3

    Here I thought you were going to talk about how Rangers can take a feat to scare birds out of their nests...

  • @wmhilton-old
    @wmhilton-old 6 ปีที่แล้ว

    I ran into this very problem in isomorphic-git. At one point I had a constructor `let repo = new Git({fs, dir})` and instance methods... but after realizing some of the methods added _serious_ weight (100kb?!) I was determined to allow users to only import the methods they used. Ultimately I ended up with an API where the state management is external to the library. Instead of providing a constructor, I just provide all the operations as functions and make the user pass certain required arguments that I would have stored on "this" every call. `let result = fetch({fs, dir, ...args})`

  • @CardinalHijack
    @CardinalHijack 4 ปีที่แล้ว

    Jake is a legend, love that dude

  • @BennyPowers
    @BennyPowers 6 ปีที่แล้ว +1

    what's ugly about closures and curried functions? @8:15

  • @firstprincipleslearning
    @firstprincipleslearning 6 ปีที่แล้ว +1

    You guys are awesome ! How can we buy those coasters ?

  • @ryanmann9775
    @ryanmann9775 2 ปีที่แล้ว

    Classes in JavaScript aren't real, they're just objects with functions on prototype with their calling context bound to the instance of the object.
    That said, I'd argue not using classes at all, especially if you're using typescript. Instead just use modules and namespaces.

  • @ozone1979
    @ozone1979 5 ปีที่แล้ว

    I wish there was a way to simply import a whole class or object, like Jquery and have it be tree shaken.

  • @odynnxd
    @odynnxd 6 ปีที่แล้ว

    Loved the video, this longs talks are great!

  • @valikhanakhmedov5344
    @valikhanakhmedov5344 5 ปีที่แล้ว

    Great talk guys, did you manage to make .do approach work with typescript though?

  • @emilemil1
    @emilemil1 5 ปีที่แล้ว

    Wouldn't it be easier to just make the methods static and pass in an instance? Something like:
    let store = new Store();
    Store.set(store, "hello", "world");
    And the set method would be:
    static set(instance, key, val) {
    return instance._underlyingDB.setKeyVal(key, val);
    }
    Or, replicating the paradigm for the database:
    static set(instance, key, val) {
    return UnderlyingDB.setKeyVal(instance._underlyingDB, key, val);
    }
    No new syntax needed. Build tools could probably convert most regular code into this style of code automatically, as long as you're not calling methods by strings.

    • @jakearchibald
      @jakearchibald 5 ปีที่แล้ว +1

      Static methods don't tend to tree-shake. What I went for in the end was set(key, val, { store }).

  • @adaliszk
    @adaliszk 6 ปีที่แล้ว +1

    Why not use mixins to extend the Store class with whatever function is needed?

    • @wmhilton-old
      @wmhilton-old 6 ปีที่แล้ว

      AdaLiszk TypeScript supports mixins, so that could work!

    • @adaliszk
      @adaliszk 6 ปีที่แล้ว

      @William Hilton: I know, but in general mixins are part of the ECMAScript and a lot of other tools like Polymer uses it to extend its functionalities and have the tree-shaking work ;)

    • @dassurma
      @dassurma 6 ปีที่แล้ว

      How do you use mixins with instances? Or am I misunderstanding you?

    • @jakearchibald
      @jakearchibald 6 ปีที่แล้ว

      Mixins that only exist in a particular scope would be interesting, but pretty complicated. It feels like bind is just simpler.

    • @adaliszk
      @adaliszk 6 ปีที่แล้ว +1

      @Surma: I would create my own class for my own needs using the building blocks provided the library then after I'm done my application I can remove the mixins one-by-one and ran the test to see which method is used in which cases. For reflecting the instance I can override the inner states to be constants in my own class, that way I can extend that even further in parts of the system where it actially needed.

  • @PhalgunVaddepalli
    @PhalgunVaddepalli 3 ปีที่แล้ว

    Based on a tree story!

  • @hyyanabofakher6229
    @hyyanabofakher6229 6 ปีที่แล้ว +1

    Great video , Keep it up 👍

  • @swanbosc5371
    @swanbosc5371 6 ปีที่แล้ว

    hey... great vid !!!
    Just to know... Can Typescript tree-shake class ?

    • @dassurma
      @dassurma 6 ปีที่แล้ว

      As explained in the video: There’s no fool-proof way to prove that some dynamic code doesn’t invoke a method,

  • @dwighthouse
    @dwighthouse 6 ปีที่แล้ว +1

    If you are going to change your classes to externalize the methods outside the class, and since JS classes can't natively support private data anyway, what's the point of using classes at all? Why not define your program in terms of data structures made of objects, and bare functions that operate on those structures, either purely or with mutations? Is there a way for a class to define what functions the pipeline operator (or the now-dead bind operator) can add, or can literally any code inject a method into a class? If the latter, you don't even have legitimate behavioral separation, so why pretend you do by using classes? Certainly, a `do` method on a class will remove all potential data integrity.

    • @dassurma
      @dassurma 6 ปีที่แล้ว +1

      JavaScript is dynamically typed. Using that against a proposal seems not very useful (you have always been able to pass any object to any function). What’s the point of classes? Grouping data and the functions that operate on that data. You can invoke the method of one class instance on a completely unrelated class instance right now, so that’s not anything new. These 2 proposal just make it more obvious that _that_ is what’s actually happening. It’s not about was is allowed in the language, but about what conveys the intention of the developer most intuitively to another reader.

    • @dwighthouse
      @dwighthouse 6 ปีที่แล้ว +2

      Just as you can pass any object to any function, you can also pass any object to any method on a class, right? I don't see how this is an argument against using plain objects and functions. Classes share the exact same potential vulnerability.
      I understand that classes provide a way to group behavior and data together. But if grouping related data and behavior together is the focus, why not just create a plain object that contains your data and references (via keys) to functions you want related to the data? The only difference, functionally, is that you explicitly pass the object to the function, rather than implicitly relying on the user to use `new` and `this` properly. Both this system and class-based systems provide neither data privacy nor method security, so they are equivalent security-wise. From my perspective, the idea of splitting the method behavior outside of the classes at all (via pipeline or bind) seems to be itself a violation of the intention of classes, because the method is actually just a function that anything could potentially use for themselves, rather than something that is strictly for that class.
      My proposal is to dispense with the subterfuge: functions that operate on objects correctly represent the security available to the API (that is, none) to the end user without wrapping it up in classes which make the code appear to have a strict API when it actually doesn't.

  • @brendanwhiting1235
    @brendanwhiting1235 6 ปีที่แล้ว

    10:40 Why isn't it possible with typescript? Could it just be the "any" type?

    • @jakearchibald
      @jakearchibald 6 ปีที่แล้ว

      The 'any' type is pretty much an opt-out of typescript when used in this way.

  • @talgatsaribayev821
    @talgatsaribayev821 6 ปีที่แล้ว

    Thanks for video and code(finally :)).
    I understand the problems it will resolve. However I would like raise the concerns that Dwight House have. And add that there is proposal for private class methods, however in my opinion it useless. Because by this video you need to treat all methods as a private. So maybe old fashion function and object are more suitable?

  • @tankian8829
    @tankian8829 6 ปีที่แล้ว

    To be honest, I don't feel the `set` function that takes a function and returns a function used with pipe operator is ugly.

    • @dassurma
      @dassurma 6 ปีที่แล้ว

      No, as I said, the ugliness is hidden. But if you have to _develop_ the functions that are supposed to be used with |>, it gets ugly (and produces a bit of memory garbage that :: avoids).

  • @CTimmerman
    @CTimmerman 6 ปีที่แล้ว

    But Google offered its own copy of Jquery on its CDN so it would be cached for all but the first of the sites that use that, and on the Jquery site itself you can make a lib with only the functions you want. Ah, it's about variable scope... and horrible syntax that reminds me of Lisp, Perl, and other languages most people avoid due to lack of readability.

  • @andimclean
    @andimclean 6 ปีที่แล้ว

    @suma Love the coster with you both on. Can I have one? :)

  • @khaled_osman
    @khaled_osman 4 ปีที่แล้ว

    Please do a video about how good and important it is refactor code and keep your dependencies up to date.. I have a hard time convincing my managers everywhere I work, they don't see a "business value", but get anxiety attacks about things breaking everytime I want to change something, refactor some code to follow a best practice, improve performance, or update/migrate to the latest versions of dependencies

  • @jan.melcher
    @jan.melcher 6 ปีที่แล้ว

    This should be possible now in Typescript 3.0 with genreric tuple spread/rest www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html

  • @XiXora
    @XiXora 6 ปีที่แล้ว

    9:10 "…in the pipeline" 😏

  • @GiorgioGamberoni
    @GiorgioGamberoni 6 ปีที่แล้ว

    Thx for the code :)

  • @Khalyomede
    @Khalyomede 5 ปีที่แล้ว

    I do not like .do operator because it means it will privatize it... the pipeline operator is so ugly in the implementation, too complicated, not as straight forward as the bind operator. I do not see any trace of the bind operator in the TC39 github proposal repo. Any clues?

  • @alexeypavlov5340
    @alexeypavlov5340 6 ปีที่แล้ว +4

    Guys, please add subtitles. Auto subtitles very bad and I much more worse in English.

    • @dassurma
      @dassurma 6 ปีที่แล้ว +2

      We always have our videos captioned. It might take a day or two for those to be finished, tho.

  • @DivjotSingh
    @DivjotSingh 6 ปีที่แล้ว +4

    No... What? Pipeline operator is beautiful and opens up a new world of functional programming (partial application, chaining of methods/promises).
    Like your usecase isn't even the idomatic usage of pipeline operator, yet it could help you there. It's more versatile.
    You are more likely to keep a function on RHS than a higher order function, it's just the RxJS community that loves functional programming a lot, and already has its codebase written in a way to support it. You can always rename original set function as `createSetter` and store its result in a `set` variable to make it less convoluted.
    I agree that bind operator is elegant in this use case, coz set method was already using `this`, which can itself be ugly, but if you're going to dissect out all methods of class as functions, you can probably consider functional programming paradigm altogether.
    Also, the other common usecase for bind operator would've been in case of React components. I feel public class fields solve it in a much better way and are already baked-in under a flag, so that kind of makes bind operator less useful. For all other cases (which I feel would be rare), using call/apply wouldn't hurt.
    Sorry for being super opinionated about this.
    Loved the code examples and the topic as whole. `do`approach seems really nice!
    How about creating different classes and extending Store with it? Basically using inheritance to extend features of base class? E.g. BasicStore & EnhancedStore are exported by idb-keyval, user may use either based on appetite.

    • @dassurma
      @dassurma 6 ปีที่แล้ว +1

      That’s what we have been discussing in the spec issue as well. I agree - this tree-shaking problem is not idiomatic for pipeline operator. The problem is that currently _either_ pipeline _or_ bind will happen, not both. This might change, but that’s the premise we used in this video.
      With that in mind, I think the problems `::` solves are currently much more common than the problems `|>` solves. You _can_ shoe-horn the problems space of one operator onto the other, but then weird things happen, as we outline in the video.

    • @zanehannan5306
      @zanehannan5306 6 ปีที่แล้ว

      If only there could be both...
      Also I agree; the this::bind operator is a great thing. Being able to bind without making garbage... sweet.

  • @RafaelCouto
    @RafaelCouto 6 ปีที่แล้ว

    Won't symbols solve this problem?

    • @jakearchibald
      @jakearchibald 6 ปีที่แล้ว +1

      I can't see how

    • @RafaelCouto
      @RafaelCouto 6 ปีที่แล้ว

      Something like this perhaps? It won't clash stores and can be "tree shaken" XD => jsbin.com/verimut/1/edit?js

  •  6 ปีที่แล้ว

    Man, JavaScript is such as mess that developers need to think about things like this.

  • @DoctorReefer
    @DoctorReefer 6 ปีที่แล้ว +2

    How bout we all just stop using classes in our code? Problem solved.

  • @frisoflo
    @frisoflo 6 ปีที่แล้ว

    Great video, but all proposals have different flavors of beeing sh*tty :)
    A Tree Shaker is (should be) able to remove unused methods. I dont see the problem.

    • @jakearchibald
      @jakearchibald 6 ปีที่แล้ว

      For the reasons mentioned in the video: it isn't statically analysable.

    • @frisoflo
      @frisoflo 6 ปีที่แล้ว

      okay. I looked the video again. They mentioned that each method is exported individually. So Rollup & co can use a simple approach to remove unused imports. But a Code analysor knows the callgraph of the whole code and can remove unused methods (and classes). More compilcated, because the code needs to be analyzed but not impossible.

    • @jakearchibald
      @jakearchibald 6 ปีที่แล้ว

      What about the instance[str.split('').reverse().join('')] example in the video?

    • @frisoflo
      @frisoflo 6 ปีที่แล้ว

      Yes, right - no chance for a code analysor. I silently assumed that this kind of "Reflection" stuff (counterpart in Java or C#) will not be used. (I come Java/C#/C++ and would accept that dead code remover would fail in this case)

  • @MartinHodgkins
    @MartinHodgkins 6 ปีที่แล้ว

    Ah, I am not being fooled again. This is an April fools joke right. Nobody would ever believe this, not even funny.