DEV Community

Cover image for From classes to plain objects and pure functions

From classes to plain objects and pure functions

Dominik Lubański on January 10, 2019

This is the first in a series of posts about core concepts of hybrids - a library for creating Web Components with simple and functional API. ES20...
Collapse
 
clozach profile image
Chris Lozac'h

Well done, exploring a more functional approach!

There may be an issue with your use of the word "pure," both in the title of this article and in the hybrid docs. Either it's unclear how hybrids uses pure functions, or the term "pure" isn't accurate and is best left out. As it is, the term left me confused, and I'll attempt to explain why.

Per Wikipedia and others, a function must meet two criteria to be pure: idempotency (which you have) and 0 side effects.

Here's the first function used as an example in the Hybrid docs:

export function increaseCount(host) {
  host.count += 1;
}

This function isn't pure because it mutates host. Here's one way to get similar functionality from a pure function:

export function increaseCount(oldCount) {
  return oldCount + 1;
}

Or, perhaps more usefully…

function increaseCount(oldHost) {
  return Object.assign(
    { count: oldHost.count + 1 },
    oldHost
  )
}

In your place, I'd either remove the term "pure", or update the docs to explicitly demonstrate how hybrids takes advantage of functional purity.

None of this should be taken as feedback on the system itself. The approach you offer is intriguing. Bravo!

Collapse
 
smalluban profile image
Dominik Lubański • Edited

Thanks for the comment! In hybrids pure function term relates mainly to the property descriptor methods (get and set), where you don't mutate host element - it is only used to get dependencies.

Obviously, connect method is not pure - it is created especially for side effects, like adding event listeners. However, for the user of the library, the definition can be a simple object with values and pure functions - and usually, it is, when you use built-in factories or created by yourself.

The increaseCount from the example is a side effect in some way (not technically) - it is a callback attached to the button - it is not an integral part of the definition of the custom element.

Hybrids is a UI library for creating Web Components, which favors plain objects and pure functions over class and this syntax.

This is the first sentence of the docs. As you can see, it means, that library favors pure functions, not require to use them. Also, there is no statement, that all of the functions should be pure :)

Collapse
 
clozach profile image
Chris Lozac'h

Ah, that last point is subtle. Some of my confusion came from having skimmed over the code samples in the docs trying to find an example that showed pure functions in use, and didn't find any.

Not a complaint, mind you. Just sharing a perspective in case you feel it makes sense to more explicitly demonstrate the ways in which hybrids promotes the use of pure functions. The other part — about how it demotes class and this syntax — is clear from the docs as-is…and that's something I really like about what you've done!

Thanks for your thoughtful reply. :)

Collapse
 
basickarl profile image
Karl Morrison • Edited
    fullName: ({ firstName, lastName }) => `${firstName} ${lastName}`
                 ^

TypeError: Cannot destructure property `firstName` of 'undefined' or 'null'.

Your last example.

Collapse
 
smalluban profile image
Dominik Lubański • Edited

I think you just put my example in the file and run it ;) Then of course fullName will not work - it is just a function that requires a host as an argument. The library is doing that when it defines properties - this example uses translation feature, so the final definition (if you pass this to define('my-element', MyElement)) will be:

const MyElement = {
  firstName: 'Dominik',
  lastName: 'Lubański',
  fullName: {
    get: ({ firstName, lastName }) => `${firstName} ${lastName}`,
  },
};

define('my-element', MyElement);

Then your custom element will have element.fullName property, which works and returns concatenated first and last name.

Collapse
 
basickarl profile image
Karl Morrison

I appreciate the answering, however it's still failing!

define('my-element', MyElement);
^

ReferenceError: define is not defined
    at Object.<anonymous> (/home/karl/dev/javascript/sandbox.js:10:1)
    at Module._compile (internal/modules/cjs/loader.js:959:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:995:10)
    at Module.load (internal/modules/cjs/loader.js:815:32)
    at Function.Module._load (internal/modules/cjs/loader.js:727:14)
    at Function.Module.runMain (internal/modules/cjs/loader.js:1047:10)
    at internal/main/run_main_module.js:17:11

I'm trying to run this in Node v12

From the looks of it you are using RequireJS? How would a pure JavaScript implementation look like?

Thread Thread
 
smalluban profile image
Dominik Lubański

My code is not fully working example ;) It's a most important part of the file. You still need to import the library. If you use some kind of bundler you can use this:

import { define } from 'hybrids';
...

If you want "pure" JS solution, you can use ES modules:

<script type="module">
  import { define } from 'https://unpkg.com/hybrids@4.0.3/src';

  const MyElement = {
    firstName: 'Dominik',
    lastName: 'Lubański',
    fullName: {
      get: ({ firstName, lastName }) => `${firstName} ${lastName}`,
    },
  };

  define('my-element', MyElement);
</script>

Or for older browsers support:

<script src="https://unpkg.com/hybrids@4.0.3/dist/hybrids.js"></script>
<script>
  var define = window.hybrids.define;

  ...
</script>

Read more in Getting Started section of the library documentation.

Collapse
 
paulen_8 profile image
🌹

Awesome, love it. Hope to see this project go far!

Collapse
 
smalluban profile image
Dominik Lubański

Thanks! I have a lot of cool ideas, so stay tuned for the updates!

Collapse
 
pociej profile image
Grzegorz Pociejewski

Hmm to be honest i dont understand step 4, to get rid of "this", there must be something more that allows using current instance as first argument of setter, the glue code seems to be missing.

Collapse
 
smalluban profile image
Dominik Lubański

The change is simple. You can think of this keyword as a different type of argument of the function. Instead of using it, we can expand the arguments list by passing the context (in actual getter/setter property definition) as the first argument (shifting rest of them).

If you want to know more, I recommend to see the implementation in the library. For example here: github.com/hybridsjs/hybrids/blob/...