lue-bird / elm-morph / List.Morph

Morph for an elm/core List element

alter

each : MorphIndependently (beforeToNarrow -> Result (Morph.ErrorWithDeadEnd deadEnd) narrow) (beforeToBroad -> broad) -> MorphIndependently (List beforeToNarrow -> Result (Morph.ErrorWithDeadEnd deadEnd) (List narrow)) (List beforeToBroad -> List broad)

Morph all elements. On the narrowing side all narrowed values must be Ok for it to not result in a Morph.Error

If the element Morph is OneToOne, each will always succeed with the type knowing it does

sequence

sequenceMap : (element -> MorphRow narrow broadElement) -> List element -> MorphRow (List narrow) broadElement

From the elements in a given List, create MorphRows that will be run in the same order, one after the other.

Some also call this "traverse" (or "for" when the arguments are flipped)

import Morph exposing (MorphRow)
import String.Morph
import AToZ

"helloTHEREcooorwhat"
    |> Morph.toNarrow
        (List.Morph.sequenceMap casedStringOnly [ "hello", "there", "coo", "or", "what" ]
            |> Morph.rowFinish
            |> Morph.over List.Morph.string
        )
--> Ok [ AToZ.CaseLower, AToZ.CaseUpper, AToZ.CaseLower, AToZ.CaseLower, AToZ.CaseLower ]

casedStringOnly : String -> MorphRow AToZ.Case Char
casedStringOnly string =
    Morph.choice
        (\lower upper cased ->
            case cased of
                AToZ.CaseLower -> lower ()
                AToZ.CaseUpper -> upper ()
        )
        |> Morph.rowTry (\() -> AToZ.CaseLower)
            (String.Morph.only (string |> String.toLower))
        |> Morph.rowTry (\() -> AToZ.CaseUpper)
            (String.Morph.only (string |> String.toUpper))
        |> Morph.choiceFinish

Don't try to be clever with this.

The usual Morph.narrow(\... -> ...) |>grab-match chain is often more explicit, descriptive and type-safe.

Because of this, List.Morph only exposes sequenceMap, not sequence, making misuse a bit more obvious.

If each element's MorphRow will always produce the same broad value like String.Morph.only, use broadSequenceMap

broadSequenceMap : (element -> MorphRow () broadElement) -> List element -> MorphRow () broadElement

Match broad MorphRows (those that can always produce the same broad value) based on given input elements in sequence.

This can get verbose, so create helpers with it where you see common patterns!

import Morph exposing (MorphRow)
import Char.Morph
import List.Morph

textOnly : String -> MorphRow () Char
textOnly stringConstant =
    List.Morph.broadSequenceMap
        (Char.Morph.only >> Morph.one)
        (stringConstant |> String.toList)

-- Match a specific character, case sensitive
"abc"
    |> Morph.toNarrow
        (textOnly "abc" |> Morph.rowFinish |> Morph.over List.Morph.string)
--> Ok ()

-- It fails if it's not _exactly_ the same
"abC"
    |> Morph.toNarrow
        (textOnly "abc" |> Morph.rowFinish |> Morph.over List.Morph.string)
    |> Result.toMaybe
--> Nothing

Note that textOnly is available as String.Morph.only. Others aren't, tho, like when matching only a specific sequence of Bits

transform

stack : MorphIndependently (Emptiable (Stacked broadElement) Possibly -> Result error_ (List broadElement)) (List narrowElement -> Emptiable (Stacked narrowElement) Possibly)

Morph.OneToOne from a stack

import Stack
import Morph

Stack.topBelow 0 [ 12, 3 ]
    |> Morph.mapTo List.Morph.stack
--> [ 0, 12, 3 ]

Inverse of Stack.Morph.list

array : MorphIndependently (Array narrowElement -> Result error_ (List narrowElement)) (List element -> Array element)

Morph.OneToOne from an Array

import Array
import Morph

Array.fromList [ 0, 1, 2, 3 ]
    |> Morph.mapTo List.Morph.array
--> [ 0, 1, 2, 3 ]

Inverse of Array.Morph.list

arraySized : MorphIndependently (ArraySized narrowElement narrowRange_ -> Result error_ (List narrowElement)) (List broadElement -> ArraySized broadElement (N.Min (N.Up0 broadX_)))

Morph.OneToOne from an ArraySized

import ArraySized
import Morph

ArraySized.l4 0 1 2 3
    |> Morph.mapTo List.Morph.arraySized
--> [ 0, 1, 2, 3 ]

Inverse of ArraySized.Morph.list

string : MorphOrError (List Char) String error_

Morph.OneToOne from a String to a List Char.

Inverse of String.Morph.list

set : MorphIndependently (Set narrowElement -> Result error_ (List narrowElement)) (List comparableBroadElement -> Set comparableBroadElement)

Morph.OneToOne from a Set

import Set
import Morph

Set.fromList [ 0, 1, 2, 3 ]
    |> Morph.mapTo List.Morph.set
--> [ 0, 1, 2, 3 ]

Inverse of Set.Morph.list

dict : MorphIndependently (Dict broadKey broadValue -> Result error_ (List { key : broadKey, value : broadValue })) (List { key : comparableNarrowKey, value : narrowValue } -> Dict comparableNarrowKey narrowValue)

Morph.OneToOne from a Dict key value to a List { key : key, value : value }.

import Dict
import List.Morph
import Morph

Dict.empty
    |> Dict.insert 0 'a'
    |> Dict.insert 1 'b'
    |> Morph.mapTo List.Morph.dict
--> [ { key = 0, value = 'a' }, { key = 1, value = 'b' } ]

Inverse of Dict.Morph.list

bytes : MorphOrError (Emptiable (Stacked Bit) Possibly) Bytes error_

Morph.OneToOne from Bytes to a list of individual bits. Now you can morph them as a row!

value : Value.Morph.Internal.MorphValue element -> Value.Morph.Internal.MorphValue (List element)

List MorphValue