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

setem

v0.6.1

Published

Set'em: Elm record setter generator

Downloads

168

Readme

🔸 setem 🔸

sanity_check npm License: MIT Twitter: gada_twt

Set'em: Elm record setter generator

What is this?

"Getters just get, setters just set!"

setem generates all possible record setters from your Elm source files into a single module.

Prerequisites

  • Reasonably new nodejs

Install

npm install --save-dev setem
# OR
# yarn add --dev setem

Usage

# Generates for an Elm project (including dependencies)
npm run setem --output src/                                 # For Elm project cwd. "elm.json" file must exist
npm run setem --output src/ --elm-json sub_project/elm.json # For non-cwd Elm project, targeted by "elm.json" file path

# Only generates from specific files (NOT including dependencies)
npm run setem --output src/ src/Main.elm                    # From a single source file
npm run setem --output src/ src/Main.elm src/Data/User.elm  # From multiple source files
npm run setem --output src/ src/**/*.elm                    # From multiple source files resolved by glob in your shell
npm run setem --output src/ src/                            # From multiple source files in a specific directory, expanded recursively

(npx or global install also work)

It generates src/RecordSetter.elm file like this:

-- This module is generated by `setem` command. DO NOT edit manually!


module RecordSetter exposing (..)


s_f1 : a -> { b | f1 : a } -> { b | f1 : a }
s_f1 value__ record__ =
    { record__ | f1 = value__ }


s_f2 : a -> { b | f2 : a } -> { b | f2 : a }
s_f2 value__ record__ =
    { record__ | f2 = value__ }

...

All s_<field name> setter function works for any records with matching field names!

But Why?

As you all Elm developers have probably known, Elm innately provides getters for any record fields:

$ elm repl
---- Elm 0.19.1 ----------------------------------------------------------------
Say :help for help and :exit to exit! More at <https://elm-lang.org/0.19.1/repl>
--------------------------------------------------------------------------------
> .name
<function> : { b | name : a } -> a

These getters are concise, pipeline-friendly (i.e. you can x |> doSomething |> .name |> doElse), therefore composition-friendly (there are packages with "lift"-ing functions that work in tandem with getters, such as elm-form-decoder or elm-monocle).

On the other hand, it does not provide setters of the same characteristics. Standard record updating syntax is:

{ record | name = "new name" }

...which is,

  • Not pipeline-friendly. You have to combine it with anonymous function like:

    x |> doSomething |> (\v -> { record | name = v }) |> doElse
  • Not nest-friendly. You have to combine either let in or pattern matches:

    let
        innerRecord =
            record.inner
    in
    { record | inner = { innerRecord | name = doSomething innerRecord.name } }

Now, as discussed many many times in the community, it is somewhat deliberate choice in the language design, to NOT provide "record setter/updater" syntax.

  • It encourages to create nicely named top-level functions rather than relying on verbose anonymous functions.
  • Also it encourages to design flatter, simpler data structures (and possibly using custom types) to more precisely illustrate our requirements.
  • For unavoidable situations where setters are in strong demand, we can create "data" module with necessary setters exposed, which actually leads us to think about proper boundary of data and concerns. Never a bad thing!

But. A big BUT.

In our everyday programming we yearn for setters time to time.

  • When we work with foreign record data structures from, say, packages
  • When we have tons of records to work with, from code generation facilities such as elm-graphql
  • When it is more natural to work with nested records as-is. For example when we use external/existing JSON APIs.
  • Simply when we do not have much time.
    • Requirement of writing many setters in order to leverage composition-centric logics, is tedious.
    • It is a discouragement for us before writing proper data modules, when we forsee many boilerplate works. Even if it is one time thing.

For that, setem is born.

What you get

As the slogan says, "setters just set!"

  • setem just generates setters, and setters only
    • It is up to you how you utilize those setters. For instance, use setem-generated setters as building blocks for Monocle definitions!
  • It does not provide "updaters" in the sense of (a -> a) -> { b | name : a } -> { b | name : a }
    • It might prove useful, though my current intuition says it sees less usages than setters.
  • A single importable module. Just import RecordSetter exposing(..) in your code and that's it! All setters are always available.

With generated setters it is possible to:

  • Pipeline-d set:

    x |> doSomething |> s_name v |> s_anotherField (updater x.anotherField) |> doElse
  • Nested set:

    { record
        | inner =
            record.inner
                |> s_name (doSomething record.inner.name)
                |> s_anotherField v
        , anotherInner =
            record.anotherInner
                |> s_number 1
                |> s_moreNest
                    (record.anotherInner.moreNest
                        |> s_yetAnotherField vv
                        |> s_howFarCanWeGet [ 2, 3, 4 ]
                    )
    }

Setters are ordinary functions. Pass them to any high-order functions as needed!

Tips

Q. Should we add setem-generated file to git?

A. Up to you. Personally I do. If you are irritated by cluttered diffs every time you generated setem, create a .gitattributes file with a following entry:

src/RecordSetter.elm linguist-generated=true

Paths set as linguist-generated=true are collapsed by default on GitHub pull request diffs, reducing visual clutter. See https://github.com/github/linguist/blob/master/docs/overrides.md#generated-code

Implementation note

It is based on tree-sitter-elm. Quite fast!

The generator looks for both record type definitions and record data expressions from your source files. It generates setters of not yet used fields (or even, ones you are not going to use at all.) Unused ones are expected to be sorted out by Dead-Code Elimination feature of elm make --optimize.

If you do not give explicit paths as command line arguments, setem reads your elm.json file and generates setters not only from your "source-directories" but from your dependencies as well. In this scenario, tokens from your dependencies are cached in your elm-stuff/setem/ directory.

Development

Install reasonably new Node.js. If you are using mise,

git clone [email protected]:ymtszw/setem.git
cd setem/
git submodule update --init --recursive
mise install
npm install
npm run test
npm run test:cli

In GitHub Actions, sanity checks are performed against recent LTS Node.js versions (14,16,18)

Author & License

MIT License (c) Yu Matsuzawa

Show your support

Give a ⭐️ if this project helped you!


This README was generated with ❤️ by readme-md-generator