Morph
for an ArraySized element lengthRange
each : MorphIndependently (narrowBeforeMap -> Result (Morph.ErrorWithDeadEnd deadEnd) narrowMapped) (broadBeforeUnmap -> broadUnmapped) -> MorphIndependently (ArraySized narrowBeforeMap (N.In narrowMin narrowMax) -> Result (Morph.ErrorWithDeadEnd deadEnd) (ArraySized narrowMapped (N.In narrowMin narrowMax))) (ArraySized broadBeforeUnmap broadRange -> ArraySized broadUnmapped broadRange)
Morph each element in an ArraySized
array : MorphIndependently (Array narrowElement -> Result error_ (ArraySized narrowElement (N.Min (N.Up0 narrowX_)))) (ArraySized broadElement broadRange_ -> Array broadElement)
Morph.OneToOne
from an Array
import ArraySized
import Array
Array.fromList [ 0, 1, 2, 3 ]
|> Morph.mapTo ArraySized.Morph.fromArray
--: ArraySized (Min (Up0 x_)) number_
Inverse of Array.Morph.arraySized
list : MorphIndependently (List narrowElement -> Result error_ (ArraySized narrowElement (N.Min (N.Up0 narrowX_)))) (ArraySized broadElement broadRange_ -> List broadElement)
Morph.OneToOne
from a List
import ArraySized
import Morph
[ 0, 1, 2, 3 ]
|> Morph.mapTo ArraySized.Morph.list
--: ArraySized (Min (Up0 x_)) number_
Inverse of List.Morph.arraySized
stack : MorphIndependently (Emptiable (Stacked narrowElement) narrowPossiblyOrNever -> Result error_ (ArraySized narrowElement (N.Min (N.On (N0OrAdd1 narrowPossiblyOrNever N0))))) (ArraySized broadElement (N.In (N.On (N0OrAdd1 broadPossiblyOrNever minFrom1_)) max_) -> Emptiable (Stacked broadElement) broadPossiblyOrNever)
Morph.OneToOne
from a stack
import ArraySized
import Morph
import Stack
Stack.topBelow 0 [ 1, 2, 3, 4 ]
|> Morph.mapTo ArraySized.Morph.stack
--: ArraySized (Min (Up1 x_)) number_
Inverse of Stack.Morph.arraySized
string : MorphIndependently (String -> Result error_ (ArraySized Char (N.Min (N.Up0 narrowX_)))) (ArraySized Char broadRange_ -> String)
Morph.OneToOne
from a String
to ArraySized ... Char
import ArraySized
import Morph
"0123"
|> Morph.mapTo ArraySized.Morph.string
--: ArraySized (Min (Up0 x_)) number_
Inverse of String.Morph.arraySized
exactly : N (N.In min max) -> MorphRowIndependently elementBeforeToBroad elementNarrow broadElement -> MorphRowIndependently (ArraySized elementBeforeToBroad (N.In min max)) (ArraySized elementNarrow (N.In min max)) broadElement
Match a value a given number of times
and return them as an ArraySized
âšī¸ Equivalent regular expression:
{n}
import Morph
import Char.Morph
import String.Morph
import AToZ exposing (AToZ(..))
import AToZ.Morph
import N exposing (n3)
import ArraySized
import List.Morph
-- we want `exactly 3` letters
"abc"
|> Morph.toNarrow
(exactly n3 (AToZ.Morph.lowerChar |> Morph.one)
|> Morph.rowFinish
|> Morph.over List.Morph.string
)
|> Result.map ArraySized.toList
--> Ok [ A, B, C ]
-- not 2 or 4, we want 3
"ab"
|> Morph.toNarrow
(exactly n3 (AToZ.Morph.lowerChar |> Morph.one)
|> Morph.rowFinish
|> Morph.over List.Morph.string
)
|> Result.toMaybe
--> Nothing
exactlyWith : MorphRow (N range) broadElement -> MorphRowIndependently elementNarrow elementBeforeToBroad broadElement -> MorphRowIndependently (ArraySized elementNarrow range) (ArraySized elementBeforeToBroad range) broadElement
Depending on the number parsed by a given morph, morph exactly that amount of elements.
To for example morph string bits where the first 16 bits represent the amount of code points that follow:
ArraySized.Morph.exactlyWith
(N.Morph.natural |> Morph.overRow (Natural.Morph.bits n16 Bytes.BE))
Char.Morph.bits
The name is pretty horrible, so suggestions are welcome!
If you need to parse a format like
[ length ] [ other necessary data you want to grab ] [ elements ]
Please open an issue and I'll add a version for it. This isn't exposed currently because the API for this would be jAnKy and the use-case kinda obscure.
using :
(complete -> toDecide)
-> MorphRow toDecide broadElement
-> (toDecide -> MorphRow complete broadElement)
-> MorphRow complete broadElement
This might look like the happy "andThen" that you love but it's more evil than that!
For example, you'd typically use "andThen" for versioning etc. â not with morphs.
Use Morph.choice
where each possibility expects a specific number.
using
is only unavoidable
when certain data that tells you how to proceed can have infinite possibilities.
Here's some ugly consequences if we use using
instead of a less generic exactlyWith
:
Its description
can barely tell users anything,
as our main morph is hidden behind a function :(
Adding Morph.named
to the whole using
morph helps but the quality is still notably lower.
Instead of recursively using using
, you should use
untilNext
, untilLast
, untilNextFold
, untilLastFold
to not blow the stack and get a nicer description
Its harder to reason about
It's less composable / less "independent"
It's easier than ever to get your toBroad
and toNarrow
functions out of sync
atLeast : N (N.In (N.On lowerLimit) (N.Up lowerLimitToBroad_ N.To broadLowerLimit)) -> MorphRowIndependently narrow beforeToBroad broadElement -> MorphRowIndependently (ArraySized narrow (N.Min (N.On lowerLimit))) (ArraySized beforeToBroad (N.In (N.On broadLowerLimit) max_)) broadElement
Match a value at least a given number of times
and return them as an ArraySized
.
âšī¸ Equivalent regular expression:
{min,}
import Morph
import List.Morph
import AToZ.Morph
import AToZ
import ArraySized
import N exposing (n3)
-- we want at least three letters, we are okay with more than three
"abcdef"
|> Morph.toNarrow
(ArraySized.Morph.atLeast n3 (AToZ.Morph.lowerChar |> Morph.one)
|> Morph.rowFinish
|> Morph.over List.Morph.string
)
|> Result.map ArraySized.toList
--> Ok [ A, B, C, D, E, F ]
-- but not two, that's sacrilegious
"ab"
|> Morph.toNarrow
(ArraySized.Morph.atLeast n3 (AToZ.Morph.lowerChar |> Morph.one)
|> Morph.rowFinish
|> Morph.over List.Morph.string
)
|> Result.toMaybe
--> Nothing
atLeast n0
âšī¸ Equivalent regular expression:
*
import Morph
import List.Morph
import AToZ.Morph
import AToZ exposing (AToZ(..))
import ArraySized
import N exposing (n0)
-- We want as many letters as there are.
"abc"
|> Morph.toNarrow
(ArraySized.Morph.atLeast n0 (AToZ.Morph.lowerChar |> Morph.one)
|> Morph.rowFinish
|> Morph.over List.Morph.string
)
|> Result.map ArraySized.toList
--> Ok [ A, B, C ]
-- even zero letters is okay
""
|> Morph.toNarrow
(ArraySized.Morph.atLeast n0 (AToZ.Morph.lowerChar |> Morph.one)
|> Morph.rowFinish
|> Morph.over List.Morph.string
)
|> Result.map ArraySized.toList
--> Ok []
As you can see, atLeast n0
will never fail.
If you want a to morph a List
instead of an ArraySized ... (Min (On N0))
,
you might as well use Morph.whilePossible
instead of
ArraySized.toList
|> Morph.overRow (ArraySized.Morph.atLeast n0)
atLeast n1
âšī¸ Equivalent regular expression:
+
import Morph
import List.Morph
import AToZ.Morph
import AToZ exposing (AToZ(..))
import ArraySized
import N exposing (n1)
-- we want as many letters as there are
"abc"
|> Morph.toNarrow
(ArraySized.Morph.atLeast n1 (AToZ.Morph.lowerChar |> Morph.one)
|> Morph.rowFinish
|> Morph.over List.Morph.string
)
|> Result.map ArraySized.toList
--> Ok [ A, B, C ]
-- but we want at least one
""
|> Morph.toNarrow
(ArraySized.Morph.atLeast n1 (AToZ.Morph.lowerChar |> Morph.one)
|> Morph.rowFinish
|> Morph.over List.Morph.string
)
|> Result.toMaybe
--> Nothing
import Stack
import Morph
import ArraySized.Morph exposing (atLeast)
import List.Morph
import AToZ.Morph
import AToZ exposing (AToZ(..))
import N exposing (n0)
tag =
atLeast n1 (AToZ.Morph.lowerChar |> Morph.one)
tags =
Morph.narrow Stack.onTopLay
|> grab Stack.top tag
|> grab Stack.removeTop
(List.Morph.arraySized
|> Morph.overRow
(atLeast n0
(Morph.narrow (\tag -> tag)
|> match separator
|> grab (\tag -> tag) tag
)
)
)
"a,bc,def"
|> Morph.toNarrow (tags |> Morph.rowFinish |> Morph.over List.Morph.string)
|> Result.map (Stack.map (\_ -> ArraySized.toList))
--â Ok
--â (Stack.topBelow [ A ]
--â [ [ B, C ]
--â , [ D, E, F ]
--â ]
--â )
Morph.narrow ...
|> grab ... (atLeast n0 (Morph.keep |> Morph.one))
|> grab ...
would only parse the first part until the end
because it always narrow
s.
Nothing after would ever be parsed, making the whole thing fail.
The maximum of the lower limit argument enables what's shown in the following example: "match any number of spaces and broaden to 1"
broad (ArraySized.one ())
|> Morph.overRow (atLeast n0 (String.Morph.only " "))
In this case, the minimum of the given "seed" before broadening ArraySized.one ()
is 1,
whereas the narrow result of atLeast n0
will have a minimum length of 0.
The maximum of the lower limit argument is a type-level proof that the "seed" minimum is greater or equal to the resulting narrow minimum length.
â for example will lead to a compile time error:
broad ArraySized.empty
|> Morph.overRow (atLeast n1 (String.Morph.only " "))
The argument to
|>
is of type:... ArraySized () (In #(On N0)# (Up0 maxX_)) ...
But it needs to be:
... ArraySized () (In #(On N1)# (Up0 maxX_)) ...
in_ : ( N (N.In min (N.Up minToMaxMin_ N.To maxMin)), N (N.In (N.On maxMin) max) ) -> MorphRow element broadElement -> MorphRow (ArraySized element (N.In min max)) broadElement
Match a value between a minimum and maximum number of times
and return them as an ArraySized
.
âšī¸ Equivalent regular expression:
{min,max}
import Morph
import AToZ.Morph
import AToZ exposing (AToZ(..))
import List.Morph
import ArraySized
import N exposing (n2, n4)
-- we want between two and four letters
"abc"
|> Morph.toNarrow
(in_ ( n2, n4 ) (AToZ.Morph.lowerChar |> Morph.one)
|> Morph.rowFinish
|> Morph.over List.Morph.string
)
|> Result.map ArraySized.toList
--> Ok [ A, B, C ]
"a"
|> Morph.toNarrow
(in_ ( n2, n4 ) (AToZ.Morph.lowerChar |> Morph.one)
|> Morph.rowFinish
|> Morph.over List.Morph.string
)
|> Result.toMaybe
--> Nothing
in_ ( n0, n1 )
Alternative to Maybe.Morph.row
which instead returns an ArraySized
.
âšī¸ Equivalent regular expression:
?
âšī¸ Equivalent regular expression:
{0,max}
import Morph
import AToZ exposing (AToZ(..))
import AToZ.Morph
import List.Morph
import ArraySized
import N exposing (n0, n3)
-- we want a maximum of three letters
"ab"
|> Morph.toNarrow
(in_ ( n0, n3 ) (AToZ.Morph.lowerChar |> Morph.one)
|> Morph.rowFinish
|> Morph.over List.Morph.string
)
|> Result.map ArraySized.toList
--> Ok [ A, B ]
-- even zero letters are fine
""
|> Morph.toNarrow
(in_ ( n0, n3 ) (AToZ.Morph.lowerChar |> Morph.one)
|> Morph.rowFinish
|> Morph.over List.Morph.string
)
|> Result.map ArraySized.toList
--> Ok []
sequenceMap : (element -> MorphRow elementNarrow broadElement) -> ArraySized element (N.In min max) -> MorphRow (ArraySized elementNarrow (N.In min max)) broadElement
From the elements in a given ArraySized
,
create MorphRow
s
that will be run in the same order, one after the other.
More details â List.Morph.sequenceMap
broadSequenceMap : (element -> MorphRow () broadElement) -> ArraySized element (N.In (N.On min_) (N.On max_)) -> MorphRow () broadElement
Match broad MorphRow
s
(those that can always produce the same broad value)
based on given input elements in sequence.
More details â List.Morph.broadSequenceMap