Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/lue-bird/elm-review-missing-record-field-lens
elm-review: helper generation
https://github.com/lue-bird/elm-review-missing-record-field-lens
accessors elm elm-review lens nested prism record-field variants
Last synced: 14 days ago
JSON representation
elm-review: helper generation
- Host: GitHub
- URL: https://github.com/lue-bird/elm-review-missing-record-field-lens
- Owner: lue-bird
- License: mit
- Created: 2021-09-09T08:09:52.000Z (over 3 years ago)
- Default Branch: master
- Last Pushed: 2022-09-10T15:21:14.000Z (over 2 years ago)
- Last Synced: 2024-12-09T18:02:07.487Z (18 days ago)
- Topics: accessors, elm, elm-review, lens, nested, prism, record-field, variants
- Language: Elm
- Homepage: https://package.elm-lang.org/packages/lue-bird/elm-review-missing-record-field-lens/latest/
- Size: 362 KB
- Stars: 2
- Watchers: 1
- Forks: 1
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- Changelog: changes.md
- Contributing: contributing.md
- License: LICENSE
Awesome Lists containing this project
README
Despite what the name suggests,
this package contains _multiple_ [`elm-review`](https://package.elm-lang.org/packages/jfmengels/elm-review/latest/) rules to help with automatic code generation based on use:- [`RecordFieldHelper.GenerateUsed`](RecordFieldHelper-GenerateUsed)
- [`VariantHelper.GenerateUsed`](VariantHelper-GenerateUsed)When [`lue-bird/generate-elm`](https://github.com/lue-bird/generate-elm) – a framework for making code generation easy and safe –
is finished, every functionality will be ported over.-----
You find yourself writing code like ↓ ?
```elm
... path newInput =
\state ->
{ state
| projects =
state.projects
|> Scroll.focusMap
(Fillable.map
(\project ->
{ project
| calls =
project.calls
|> List.map
(Tree.elementAlter
( path, Tree.childPrepend newInput )
)
}
)
)
}
````Field.nameAlter` helpers will help remove some verbosity:
```elm
import Field... path newInput =
Field.projectsAlter
(Scroll.focusMap
(Fillable.fillMap
(Field.callsAlter
(List.map
(Tree.elementAlter
( path, Tree.childPrepend newInput )
)
)
)
)
)
```
with
```elm
module Field exposing (callsAlter, projectsAlter)callsAlter : (calls -> calls) -> { record | calls : calls } -> { record | calls : calls }
callsAlter alter =
\record -> { record | calls = record.calls |> alter }
...
```We can reduce the number of helpers by _combining the possible operations (access, replace, alter, name, ...)_ into a "lens":
```elm
import Field
import Hand.On
import Accessors exposing (over)
import Accessors.Library exposing (onEach)... path newInput =
over Field.projects --← a "lens" for the field .projects
(over Scroll.focus
(over Hand.On.filled --← a "lens" for the variant `Hand.Filled`
(over Field.calls --← a "lens" for the field .calls
(over onEach
(over (Tree.elementAt path)
(Tree.childPrepend newInput)
)
)
)
)
)
```
Seeing a pattern? You can, to put the cherry on the cake, _compose_ those "lenses":```elm
import Field
import Emptiable.On
import Accessors exposing (over)
import Accessors.Library exposing (onEach)... path newInput =
over --
(Field.projects -- { _ | projects : }
<< Scroll.focus
<< Emptiable.On.filled -- type Emptiable fill = Filled | ...
<< Field.calls -- { _ | projects : }
<< onEach -- List ()
<< Tree.elementAt path
)
(Tree.childPrepend newInput)
```Methods like this make your code more **readable**. Compare with the first example.
→ [`RecordFieldHelper.GenerateUsed`](RecordFieldHelper.GenerateUsed) automatically generates _record field_ lenses you use.
In the last examples
- `projects`, `calls` lenses will be generated in `module Field`
- `Hand.On.filled` prism will be generated by [`VariantHelper.GenerateUsed`](VariantHelper-GenerateUsed)## `RecordFieldHelper.GenerateUsed`
### try without installing
```bash
elm-review --template lue-bird/elm-review-missing-record-field-lens/example/field-accessors
```### configure
```elm
module ReviewConfig exposing (config)import RecordFieldHelper.GenerateUsed
import Review.Rule exposing (Rule)config : List Rule
config =
[ RecordFieldHelper.GenerateUsed.rule
{ generator = RecordFieldHelper.GenerateUsed.accessors
, generateIn = ( "Field", [] )
}
]
```
See [`Config`](RecordFieldHelper.GenerateUsed#Config)### lenses that work out of the box
- [`erlandsona/elm-accessors`](https://package.elm-lang.org/packages/erlandsona/elm-accessors/latest/)
- [`sjorn3/elm-fields`](https://package.elm-lang.org/packages/sjorn3/elm-fields/latest/)
- [`arturopala/elm-monocle`](https://package.elm-lang.org/packages/arturopala/elm-monocle/latest)
- [`zh5/zipper`](https://package.elm-lang.org/packages/z5h/zipper/latest/)
- [`bChiquet/elm-accessors`](https://package.elm-lang.org/packages/bChiquet/elm-accessors/latest)It's also possible to generate custom helpers or to customize the generation of existing ones.
## `VariantHelper.GenerateUsed`
Helpers for the values of one variant.
With the [`Config`](VariantHelper-GenerateUsed#Config) below,
calling `YourVariantType.onOneOfThree`,
the rule will automatically
- `import YourVariantType.On`
- generate non-existent prisms/lenses `YourVariantType.On.variantName`### try without installing
```bash
elm-review --template lue-bird/elm-review-missing-record-field-lens/example/variant-accessors
```### configure
```elm
module ReviewConfig exposing (config)import Review.Rule as Rule exposing (Rule)
import VariantHelper.GenerateUsedconfig : List Rule
config =
[ VariantHelper.GenerateUsed.rule
{ build =
VariantHelper.GenerateUsed.accessors
{ valuesCombined = VariantHelper.GenerateUsed.valuesRecord }
, nameInModuleInternal = VariantHelper.GenerateUsed.variantAfter "on"
, nameInModuleExternal = VariantHelper.GenerateUsed.variant
, generationModuleIsVariantModuleDotSuffix = "On"
}
]
```
**Check out [`Config`](VariantHelper-GenerateUsed#Config)!**### out of the box
- [erlandsona/elm-accessors](https://package.elm-lang.org/packages/erlandsona/elm-accessors/latest)
- [bChiquet/elm-accessors](https://package.elm-lang.org/packages/bChiquet/elm-accessors/latest)It's also possible to generate custom helpers or to customize the generation of existing ones.
## pitfalls
Don't let this pattern warp you into overusing nesting.
Structuring a model like
```elm
{ player : { position : ..., speed : ... }
, scene : { trees : ..., rocks : ... }
}
```
makes it unnecessarily hard to update inner fields.organizing in blocks
```elm
type alias Model =
{ column : Column
, textPage : TextPage
}
```often doesn't make sense in practice
where small pieces interact with one another:
[from "Make Data Structures" by Richard Feldman – blocks → multiple sources of truth](https://youtu.be/x1FU3e0sT1I?t=1039)```elm
{ playerPosition : ...
, playerSpeed : ...
, sceneTrees : ...
, sceneRocks : ...
}
```
Doesn't ↑ make ui harder?
Yes, but the extra explicitness is worth it.
`player` could have things that are irrelevant to the ui like `configuredControls` etc.
It's best to keep state structure and ui requirements separate.Similarly, leaning towards a more limited, domain tailored API of types, packages, ... with strong boundaries
will lead to easier code with stronger guarantees.
[↑ example from "Make Data Structures" by Richard Feldman: `Doc.id` should be read-only](https://youtu.be/x1FU3e0sT1I?t=2745)Don't try to design your API around lenses etc.
Only if the API interaction happens to mirror that behavior, Dōzo### when is nesting acceptable?
When parts are logically connected like an `Address` or a [`Camera`](https://package.elm-lang.org/packages/ianmackenzie/elm-3d-camera/latest).
Make sure to make types, packages, ... out of these.
Don't [obsessively employ primitives](https://elm-radio.com/episode/primitive-obsession/).## suggestions?
→ [contributing](https://github.com/lue-bird/elm-review-missing-record-field-lens/blob/master/contributing.md)