a parser-printer: developer-friendly, general-purpose, great errors
One "morph" can convert between narrow ⇄ broad types which is surprisingly useful! Below some appetizers
MorphRowKnow parsers? MorphRow simply always creates a printer alongside. Think
Email/Id/Time/Path/Url.fromString ⇄ Email/Id/Time/Path/Url.toStringMidi.fromBitList ⇄ Midi.toBitListBuilding both in one is simpler and more reliable.
A 1:1 port of an example from elm/parser:
import Morph exposing (MorphRow, broad, match, grab)
import List.Morph
import String.Morph
type Boolean
= BooleanTrue
| BooleanFalse
| BooleanOr { left : Boolean, right : Boolean }
boolean : MorphRow Boolean Char
boolean =
Morph.recursive "boolean"
(\step ->
Morph.choice
(\variantTrue variantFalse variantOr booleanChoice ->
case booleanChoice of
BooleanTrue ->
variantTrue ()
BooleanFalse ->
variantFalse ()
BooleanOr arguments ->
variantOr arguments
)
|> Morph.rowTry (\() -> BooleanTrue)
(String.Morph.only "true")
|> Morph.rowTry (\() -> BooleanFalse)
(String.Morph.only "false")
|> Morph.rowTry BooleanOr (or step)
|> Morph.choiceFinish
)
or : MorphRow Boolean Char -> MorphRow { left : Boolean, right : Boolean } Char
or step =
let
spaces : MorphRow (List ()) Char
spaces =
Morph.named "spaces"
(Morph.whilePossible (String.Morph.only " "))
in
Morph.narrow
(\left right -> { left = left, right = right })
|> match (String.Morph.only "(")
|> match (broad [] |> Morph.overRow spaces)
|> grab .left step
|> match (broad [ () ] |> Morph.overRow spaces)
|> match (String.Morph.only "||")
|> match (broad [ () ] |> Morph.overRow spaces)
|> grab .right step
|> match (broad [] |> Morph.overRow spaces)
|> match (String.Morph.only ")")
"((true || false) || false)"
|> Morph.toNarrow
(boolean
|> Morph.rowFinish
|> Morph.over List.Morph.string
)
--> Ok (BooleanOr { left = BooleanOr { left = BooleanTrue, right = BooleanFalse }, right = BooleanFalse })What's different from writing a parser?
Morph.choice (\... -> case ... of ...) matches possibilities exhaustivelygrab ... ... also shows how to access the partbroad ... provides a "default value" for the printerMorph also doesn't have loop or a classic andThen! Instead we have atLeast, between, exactly, optional, while possible, until next, until last, ...
This allows the quality of errors to be different to what you're used to. Here's a section of the example app:

MorphValueEasily serialize from and to elm values independent of output format.
An example adapted from elm guide on custom types:
import Value.Morph exposing (MorphValue)
import Morph
import String.Morph
-- from lue-bird/elm-no-record-type-alias-constructor-function
import RecordWithoutConstructorFunction exposing (RecordWithoutConstructorFunction)
type User
= Anonymous
| SignedIn SignedIn
type alias SignedIn =
RecordWithoutConstructorFunction
{ name : String, status : String }
value : MorphValue User
value =
Morph.choice
(\variantAnonymous variantSignedIn user ->
case user of
Anonymous ->
variantAnonymous ()
SignedIn signedIn ->
variantSignedIn signedIn
)
|> Value.Morph.variant ( \() -> Anonymous, "Anonymous" ) Value.Morph.unit
|> Value.Morph.variant ( SignedIn, "SignedIn" ) signedInValue
|> Value.Morph.choiceFinish
signedInValue : MorphValue SignedIn
signedInValue =
Value.Morph.group
(\name status ->
{ name = name, status = status }
)
|> Value.Morph.part ( .name, "name" ) String.Morph.value
|> Value.Morph.part ( .statue, "status" ) String.Morph.value
|> Value.Morph.groupFinishsurprisingly easy and clean!
Morph.OneToOneThe simplest of them all: convert between any two types where nothing can fail. Think
List Bit ⇄ Bytes, see List.Morph.bytesValue ⇄ Json – both just elm union types, see Json.Morph.valueMorphThe parent of MorphRow, MorphValue, Morph.OneToOne etc.: convert between any two types. Think
Decimal (just digits) ⇄ Float with NaN and infinityAToZ ⇄ Char, see AToZ.Morph.charConfused? Hyped? Hit @lue up on anything on slack!
miniBill/elm-rope
allows our nested printer to still be O(n)lambda-phi/parser inspired MorphRow's initial designzwilias/elm-bytes-parser
showed me how to convert a list of bits from and to Bytes and gave me the courage to make MorphRow ... Bitselm-verify-examples and elm-review-documentation