npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

alacarte.js

v0.2.0

Published

Data Types a la carte for JavaScript

Downloads

12

Readme

  • /Data types à la carte/ in JavaScript

It's pretty funny though, this is a simple implementation that port [[http://citeseerx.ist.psu.edu/viewdoc/download;jsessionid=4B1BB52114FB29D3169B1761C3FBFF15?doi=10.1.1.101.4131&rep=rep1&type=pdf][Data Types à la Carte]] (it would be awfully help if you can read this paper first) from Haskell to JavaScript. It will solve the particular problem in [[https://github.com/reactive-react/react-most][react-most]] but you can use this technique with any other flux e.g. redux or DSL(expression + interpreter) ** Install #+BEGIN_SRC sh yarn add alacarte.js #+END_SRC

** Why The problem of react-most of any flux is that when a Action is dispatched, something like =reducer= will have to evaluate it and produce something to change state.

This means, you have to define all your Actions in one place so that any reducer can switch on them. e.g. in react-most [[https://github.com/reactive-react/react-most/blob/master/examples/counter/src/app.jsx#L18][there is a big switch]], you've probably see lot of these in redux as well.

It's global thing, anyone want to add a new Action will have to change it.

[[https://en.wikipedia.org/wiki/Expression_problem][The Expression Problem]] that Data Types à la Carte try to solve is pretty much the same as our problem if we map the concept of =Action= to =Expression=, and =Reducer= to =Interpreter=.

** How With Data Types à la Carte, we now can define Actions anywhere, anytime, further more, it'll let us finally get rid of ugly switch case.

note the difference here

  • before #+BEGIN_SRC js sink$: intent$.map(intent => { switch (intent.type) { case 'inc': return state => ({ count: state.count + 1 }); case 'dec': return state => ({ count: state.count - 1 }); default: return _ => _; } #+END_SRC

  • after #+BEGIN_SRC js let interpreter = interpreterFrom([evalLit, evalAdd, evalOver]) sink$: intent$.filter(supTypeSameAs(injector)) .map(interpretExpr(interpreter)) #+END_SRC

with Data Types à la Carte, you reducer will be "Type" safe and declarative. You'll probably confuse what the hell is =subTypeSameAs= or =injector=, I'll explain this further but now you should able to see the logic is pretty declarative and straightforward here.

#+BEGIN_QUOTE it just filter from all the =Expressions= where they only the same =Type= as =injector=, then interpret these expressions with =interpreter= #+END_QUOTE

** Expression the way writing Action like this is wrong #+BEGIN_SRC js inc: () => ({ type: 'inc' }), dec: () => ({ type: 'dec' }), #+END_SRC

  • it's not type safe, string could be wrong and not uniq
  • describe action at business level is wrong, if your business is complicated, imagine how many Action you'll end up writing.
  • reducer has to do two jobs at this point, interpret action, do business logic.

Let's fix how we define Action with the concept of Expression #+BEGIN_SRC js inc: () => over(lit('count'), add(lit(1))), // you can compose expressions to achieve your bussiness dec: () => over(lit('count'), add(lit(-1))) #+END_SRC here we have 3 dsl, =over=, =add=, =lit=, they're not business code like just inc or dec counter, they are DSLs, you can compose these DSLs to achieve any business that they can represent.

e.g. i can simple write a new action =inc2= with define any new type =over(lit('count'), add(lit(2)))=

but for now, forget about =Over= which should be concept from =Lens=. let's see how to construct a simple Expr that can only increase and decrease the counter #+BEGIN_SRC js inc: () => add(lit(1)), // you can compose expressions to achieve your bussiness dec: () => add(lit(-1)) #+END_SRC

first, create Expr #+BEGIN_SRC js let {Add, Over} = Expr.create({ Add: ['fn'], Over: ['prop', 'fn'] }) #+END_SRC =Add= is the name of the expression and =['fn']= means it contains a value named =fn=. since over need a function so Add should contains a function.

** Interpreter then, create interpreter for each of them #+BEGIN_SRC js // Instances of Interpreters const evalAdd = interpreterFor(Add, function (v) { return x => x + v.fn(x) });

const evalVal = interpreterFor(Val, function (v) { return ()=> v.value });

const evalOver = interpreterFor(Over, function (v) { let newstate = {} let prop = v.prop() return state => (newstate[prop] = v.fn(state[prop]), newstate) }); #+END_SRC

the =Val= Type is built in alacarte.js so you don't need to define the expression type, just simply =import {Val} from 'alacarte.js'= and implement it's interpreter.

compose these interpreters #+BEGIN_SRC js let interpreter = interpreterFrom([evalLit, evalAdd]) #+END_SRC ** Injector create a injector from these functor types #+BEGIN_SRC js let injector = injectorFrom([Val, Add, Over]) #+END_SRC

now inject the injector will generate a list of expression constructor

#+BEGIN_SRC js let [val, add, over] = injector.inject() #+END_SRC

** Add a new Expression Mult after all this, let's see how easy to add a new expression with modify any of the existing expressions and there interpreter

  • a ADT of Mult #+BEGIN_SRC js // a new mult expr is add without modify any of the current code let {Mult} = Expr.create({ Mult: ['fn'], }) const evalMult = interpreterFor(Mult, function (v) { return x => x * v.fn(x) });

let printMult = interpreterFor(Mult, function (v) { return (_ * ${v.fn}) }); #+END_SRC

Nothing has been modify in existing code, a new expression and it's interpreter just works now.

** a new Interpreter say we want another interpreter for the expr, like printer #+BEGIN_SRC js const printAdd = interpreterFor(Add, function (v) { return (_ + ${v.fn}) });

const printVal = interpreterFor(Val, function (v) { return v.value.toString() });

const printOver = interpreterFor(Over, function (v) { return over ${v.prop} do ${v.fn} });

const printMult = interpreterFor(Mult, function (v) { return (_ * ${v.fn}) }); #+END_SRC

interpert the expr will print out the expression #+BEGIN_SRC js interpretExpr(printer)(expr) #+END_SRC will print =count + (count * 2)=