lue-bird/elm-review-missing-record-field-lens/1.1.2

NOTE: the latest version is 2.1.0

NoMissingRecordFieldLens

rule : Config -> Rule

Automatically generates used record field helpers that don't exist yet.

Examples are

config =
    [ NoMissingRecordFieldLens.rule
        { generator = NoMissingRecordFieldLens.accessors
        , generateIn = ( "Accessors", [ "Library", "Fields" ] )
        }
    ]

Example

module SomeModule exposing (scoreAPoint)


import Accessors.Library.Fields as Field


scoreAPoint =
    Accessors.over Field.score (\score -> score + 1)

Fail

module Accessors.Library.Fields exposing (name)


...

Success

module Accessors.Library.Fields exposing (score)


...
type alias Config =
{ generator : FieldLensGenerator
, generateIn : ( String, List String )
}

The rule's configuration.

lens generators

working out of the box

accessors : FieldLensGenerator

FieldLensGenerator for named erlandsona/elm-accessors in the form

import Accessors exposing (Lens, makeOneToOne_)


score : Lens { record | score : score } transformed score wrap
score =
    makeOneToOne_ ".score" .score (\alter record -> { record | score = record.score |> alter })
monocle : FieldLensGenerator

FieldLensGenerator for arturopala's elm-monocle in the form

import Monocle.Lens exposing (Lens)


score : Lens { record | score : score } score
score =
    { get = .score, set = \replacement record -> { record | score = replacement } }
fields : FieldLensGenerator

FieldLensGenerator for sjorn3's elm-fields in the form

score :
    { get : { record0 | score : score } -> score
    , set : score -> { record1 | score : score } -> { record1 | score : score }
    }
score =
    { get = .score, set = \replacement record -> { record | score = replacement } }
zipper : FieldLensGenerator

FieldLensGenerator for z5h's zipper in the form

import Zipper exposing (Zipper, into)


intoScore : Zipper { record | score : score } root -> Zipper score root
intoScore =
    into .score (\replacement record -> { record | score = replacement })
accessorsBChiquet : FieldLensGenerator

FieldLensGenerator for bChiquet/elm-accessors in the form

import Accessors exposing (Relation, makeOneToOne)


score : Relation score transformed wrap -> Relation { record | score : score } transformed wrap
score =
    makeOneToOne .score (\alter record -> { record | score = record.score |> alter })

accessors generates for erlandsona/elm-accessors which adds names.

custom

type alias FieldLensGenerator =
{ imports : List Import
, declaration : { fieldName : String } -> FieldLensDeclaration
}

How to generate a FieldLensDeclaration plus the necessary imports.

Out of the box there are lenses for

You can also create a custom one with the help of the-sett's elm-syntax-dsl:

customLens : FieldLensGenerator
customLens =
    { imports =
        [ importStmt [ "CustomLens" ]
            Nothing
            (exposeExplicit
                [ typeOrAliasExpose "CustomLens" ]
                |> Just
            )
        ]
    , declaration =
        \{ fieldName } ->
            { documentation =
                emptyDocComment
                    |> markdown
                        ("`CustomLens` for the field `." ++ fieldName ++ "`.")
                    |> Just
            , name = fieldName
            , annotation =
                typed "CustomLens"
                    [ extRecordAnn "record"
                        [ ( fieldName, typeVar fieldName ) ]
                    , typeVar fieldName
                    ]
                    |> Just
            , implementation =
                let
                    { access, set } =
                        functionsForField fieldName
                in
                fqConstruct [ "CustomLens" ] "create" [ access, at ]
            }
    }
{ documentation : Maybe (Comment DocComment)
, name : String
, annotation : Maybe TypeAnnotation
, implementation : Expression
}

All the components to build a field lens declaration:

{-| [documentation]
-}
[name] : [annotation]
[name] =
    [implementation]

You can customize existing FieldLensDeclarations with withDocumentation and withName or create custom lens (functionsForField and getSetRecordForField can be helpful).

customLensDeclaration { fieldName } =
    { documentation =
        emptyDocComment
            |> markdown
                ("`CustomLens` for the field `." ++ fieldName ++ "`.")
            |> Just
    , name = fieldName
    , annotation =
        typed "CustomLens"
            [ extRecordAnn "record"
                [ ( fieldName, typeVar fieldName ) ]
            , typeVar fieldName
            ]
            |> Just
    , implementation =
        let
            { access, set } =
                functionsForField fieldName
        in
        fqConstruct [ "CustomLens" ] "create" [ access, at ]
    }
String
-> { access : Expression
, set : Expression
, update : Expression
}

access, set and update functions for a given record field.

functionsForField "score"


-->
{ access = accessFun ("." ++ fieldName)
, set =
    lambda
        [ varPattern "replacement"
        , varPattern "record"
        ]
        (update "record"
            [ ( fieldName
              , val "replacement"
              )
            ]
        )
, update =
    lambda
        [ varPattern "alter"
        , varPattern "record"
        ]
        (update "record"
            [ ( fieldName
              , applyBinOp
                    (access (val "record") fieldName)
                    piper
                    (fun "alter")
              )
            ]
        )
}
getSetRecordForField : String -> Expression

Generate a field lens implementation in the form

{ get = .score, set = \replacement record -> { record | score = replacement } }

This is equivalent to

let
    { access, set } =
        functionsForField fieldName
in
record [ ( "get", access ), ( "set", set ) ]
withDocumentation : Comment DocComment -> FieldLensDeclaration -> FieldLensDeclaration

The provided FieldLensGenerators in this package have no documentation comment.

You can generate your own documentation, though:

accessorsWithDocumentation { fieldName } =
    accessors { fieldName = fieldName }
        |> withDocumentation
            (emptyDocComment
                |> markdown
                    ("Accessor for the field `." ++ fieldName ++ "`.")
            )
withName : String -> FieldLensDeclaration -> FieldLensDeclaration

Use a different name for the generated lens.

accessorsWithFieldSuffix { fieldName } =
    accessors { fieldName = fieldName }
        |> withName (fieldName ++ "Field")