lue-bird/elm-morph/1.0.0

NOTE: the latest version is 3.0.1

Value.Morph

type alias MorphValue narrow =
MorphIndependently
(Value IndexOrName -> Result Error narrow)
(narrow -> Value IndexAndName)

Morph between a given narrow value and a generic value

The way it's set up, it allows tags that are either

unit : MorphValue ()

() MorphValue

Used in when morphing a variant with 0 attached values.

Basically every module here has a MorphValue, for example

of course you can combine them to reach other structures, like

Natural.Morph.integer
    |> Morph.over Integer.Morph.value

for a Natural

grouping

type alias MorphValueGroupEmptiable noPartPossiblyOrNever groupNarrow groupNarrowFurther =
PartsMorphEmptiable
noPartPossiblyOrNever
(Record IndexOrName -> Result PartsError groupNarrowFurther)
(groupNarrow -> Record IndexAndName)

possibly incomplete step from and to a Record

building:

= TagsMissing (Emptiable (Stacked Int) Never)
| ValueError
{ index : Int
, error : Error
}

What can go wrong while narrowing to a Record

groupNarrowAssemble
-> MorphValueGroupEmptiable
Possibly
groupNarrow_
groupNarrowAssemble

Start assembling multiple parts.

Continue with |> part and after you're done, |> groupFinish.

An example translated from elm/json

import Decimal exposing (Decimal)
import Decimal.Morph
import RecordWithoutConstructorFunction exposing (RecordWithoutConstructorFunction)
import String.Morph
import Value


type alias Cause =
    -- from lue-bird/elm-no-record-type-alias-constructor-function
    RecordWithoutConstructorFunction
        { name : String
        , percent : Decimal
        , per100k : Decimal
        }


value : MorphValue Cause
value =
    Value.Morph.group
        (\name percent per100k ->
            { name = name, percent = percent, per100k = per100k }
        )
        |> Value.Morph.part ( .name, "name" ) String.Morph.value
        |> Value.Morph.part ( .percent, "percent" ) Decimal.Morph.value
        |> Value.Morph.part ( .per100k, "per100k" ) Decimal.Morph.value
        |> Value.Morph.groupFinish

Another example for tuples

{-| `( ..., ... )` `MorphValue`


Just use a record with descriptive names instead!


-}
tuple2 :
    ( Morph part0
    , Morph part1
    )
    -> Morph ( part0, part1 )
tuple2 ( part0Morph, part1Morph ) =
    Morph.named "2-tuple"
        (Value.Morph.group
            (\part0 part1 -> ( part0, part1 ))
            |> Value.Morph.part ( Tuple.first, "part0" ) part0Morph
            |> Value.Morph.part ( Tuple.second, "part1" ) part1Morph
            |> Value.Morph.groupFinish
        )


{-| `( ..., ..., ... )` `MorphValue`


Just use a record with descriptive names instead!


-}
tuple3 :
    ( Morph part0
    , Morph part1
    , Morph part2
    )
    -> Morph ( part0, part1, part2 )
tuple3 ( part0Morph, part1Morph, part2Morph ) =
    Morph.named "3-tuple"
        (Value.Morph.group
            (\part0 part1 part2 -> ( part0, part1, part2 ))
            |> Value.Morph.part ( \( part0, _, _ ) -> part0, "part0" ) part0Morph
            |> Value.Morph.part ( \( _, part1, _ ) -> part1, "part1" ) part1Morph
            |> Value.Morph.part ( \( _, _, part2 ) -> part2, "part2" ) part2Morph
            |> Value.Morph.groupFinish
        )
( group -> partValueNarrow, String )
-> MorphValue partValueNarrow
-> MorphValueGroupEmptiable
noPartPossiblyOrNever_
group
(partValueNarrow -> groupNarrowFurther)
-> MorphValueGroupEmptiable noPartNever_ group groupNarrowFurther

Continue a group assembly MorphValue.

For example to morph a name String field, add

|> Value.Morph.part ( .name, "name" ) String.Morph.value

Once you've assembled all parts, end the builder with groupFinish.

groupFinish : MorphValueGroupEmptiable Never record record -> MorphValue record

choice

variant union MorphValue

( possibilityNarrow -> choiceNarrow, String )
-> MorphValue possibilityNarrow
-> ChoiceMorphEmptiable
noTryPossiblyOrNever_
choiceNarrow
(Tagged IndexOrName)
( (possibilityNarrow -> Tagged IndexAndName)
-> choiceToBroadFurther
)
Error
-> ChoiceMorphEmptiable
noTryNever_
choiceNarrow
(Tagged IndexOrName)
choiceToBroadFurther
Error

Describe another variant MorphValue

When you're done, end the builder with |> Value.Morph.choiceFinish

type User
    = Guest GuestData
    | SignedIn SignedInData


value : MorphValue User
value =
    Morph.choice
        (\variantGuest variantSignedIn user ->
            case user of
                Guest guest ->
                    variantGuest guest


                SignedIn signedIn ->
                    variantSignedIn signedIn
        )
        |> Value.Morph.variant ( Guest, "guest" ) guestValue
        |> Value.Morph.variant ( SignedIn, "signed in" ) signedInValue
        |> Value.Morph.choiceFinish

where guestValue : MorphValue GuestData and signedInValue : MorphValue SignedInData.

If a variant has no attached thing, use Value.Morph.unit

signValue : MorphValue Sign
signValue =
    Morph.choice
        (\positive negative sign ->
            case sign of
                Positive ->
                    positive ()


                Negative ->
                    negative ()
        )
        |> Value.Morph.variant ( \() -> Positive, "Positive" ) Value.Morph.unit
        |> Value.Morph.variant ( \() -> Negative, "Negative" ) Value.Morph.unit
        |> Value.Morph.choiceFinish
ChoiceMorphEmptiable
Never
choiceNarrow
(Tagged IndexOrName)
(choiceNarrow -> Tagged IndexAndName)
Error
-> MorphValue choiceNarrow

tag

MorphIndependently
(Name -> Result error_ IndexOrName)
(IndexAndName -> Name)

with readable names. Use in combination with eachTag

See also compact

MorphIndependently
(Index -> Result error_ IndexOrName)
(IndexAndName -> Index)

With compact indexes. Use in combination with eachTag

See also descriptive

MorphIndependently
(tagBeforeMap -> Result (ErrorWithDeadEnd Never) tagMapped)
(tagBeforeUnmap -> tagUnmapped)
-> MorphIndependently
( Value tagBeforeMap
-> Result (ErrorWithDeadEnd never_) (Value tagMapped)
)
(Value tagBeforeUnmap -> Value tagUnmapped)

Morph.OneToOne from a Value, reducing the amount of tag information in both directions

For Value, it's

...
    |> Morph.over (eachTag compact)


-- or
...
    |> Morph.over (eachTag descriptive)

but the same thing works for Json, ... as well

spin your own

build on existing ones

{-| `Posix` `MorphValue`
-}
posixValue : MorphValue Posix
posixValue =
    Morph.named "posix"
        (Morph.oneToOne
            Time.posixToMillis
            Time.millisToPosix
            |> Morph.over Int.Morph.integer
            |> Morph.over Integer.Morph.value
        )

or define new atoms, composed structures, ... (↓ are used by Json for example)

MorphIndependently
( AtomOrComposed narrowAtom narrowComposed_
-> Result Error narrowAtom
)
(broadAtom -> AtomOrComposed broadAtom broadComposed_)

Morph to a AtomOrComposed's atom if possible

MorphIndependently
( AtomOrComposed narrowAtom_ narrowComposed
-> Result Error narrowComposed
)
(broadComposed -> AtomOrComposed broadAtom_ broadComposed)

Morph to a AtomOrComposed's composed if possible

supported default broad formats

Json

bits : MorphRow (Value String) Bit

MorphRow from Bits.

Since it takes a Value String, you use it with compact or descriptive to chain it with a MorphValue:

yourTypeMorphValue
    |> Morph.over (Value.Morph.eachTag Value.Morph.compact)
    |> Morph.overRow Value.Morph.bits