yamlet-1.0.0.0: docs/coming-from-yaml.md
# Coming from the yaml package
The [yaml](https://hackage.haskell.org/package/yaml) package decodes and
encodes with the instances of aeson. The instances of yamlet, the generic
ones too, read and write the same YAML as the instances of aeson, so files
written for the yaml package keep working, with the exceptions below.
## Switching in steps
The package [yamlet-aeson](https://hackage.haskell.org/package/yamlet-aeson)
decodes and encodes a type with its instances of aeson, wrapped in
`ViaAeson`, e.g. `decodeFile @(ViaAeson Config)`. A program can switch to
the parser of yamlet first and derive the instances of yamlet later, one
type at a time. A field whose type has only instances of aeson derives its
instances of yamlet via `ViaAeson`. A program that reads and writes both
JSON and YAML can keep the instances of aeson as the only ones, e.g. with
`deriving (FromYaml, ToYaml) via ViaAeson Config`.
Through `ViaAeson`, the rules of YAML 1.2 below apply, but the types follow
the instances of aeson, e.g. `1.0` is an `Int`.
## YAML 1.2
yamlet follows YAML 1.2 where the yaml package does not:
- `y`, `yes`, `on`, `n`, `no` and `off` are strings, not booleans. A
decoder that expects a `Bool` suggests `true` or `false`.
- `.5`, `+.5`, `.inf`, `-.Inf`, `.NaN` and similar values are floats. The
yaml package reads a number only if a digit comes first, after an
optional sign, e.g. `1`, `+1`, `007` or `0x1F`. It reads such values as
strings and writes such strings without quotes, although YAML 1.1 reads
them as floats too. A yamlet decoder that expects a string suggests
quotes.
- A scalar with a tag that is not of the core schema is a string, e.g.
`!secret 123` is the string `123`. The yaml package ignores such a tag
and reads `123` as a number.
- `<<` is an ordinary key. The yaml package merges the entries of a `<<`
key into its mapping, as YAML 1.1 does.
- U+2028 and U+2029 in a string are ordinary characters. In a string that
the yaml package writes in single quotes, e.g. `true` followed by U+2028,
it writes them as line breaks with indentation after them, so the string
that yamlet reads back keeps the spaces of the indentation.
- The keys of a mapping must be unique, so two equal keys are an error. The
yaml package keeps the value of the last one.
- Every line of a flow collection or of a quoted scalar must be indented
more than the key or the `-` of its entry, the closing bracket too. The
yaml package also reads lines with less indentation, e.g. a `}` at the
start of a line:
```yaml
server: {
port: 80
}
```
## Types
yamlet does not convert values to the types of JSON:
- The keys of a map keep their type, e.g. the keys of a `Map Int` are
integers. aeson writes every key as a string, so the yaml package writes
the key `1` as `'1'`, which yamlet does not decode as an `Int`. The other
way round, the yaml package reads every key as a string, e.g. the key
`404` or `true` of a `Map Text`. yamlet rejects such a key without
quotes.
- An `IntMap` and a map with keys that aeson cannot write as strings, e.g.
a `Map (Int, Int)`, are mappings in yamlet. aeson writes them as lists of
pairs.
- An infinite `Double` is `.inf` or `-.inf`. aeson writes the string `+inf`
or `-inf`, because JSON has no infinity, and yamlet reads these as
strings.
- A NaN `Double` is `.nan`. aeson writes `null`, which yamlet rejects for a
`Double`. The yaml package reads `.nan` as a string, so it does not decode
it as a `Double`.
- A float with an exponent beyond the range from -1000 to 1000 is an error,
e.g. `1e1001`, which keeps the decoding of untrusted input fast. The yaml
package reads `1e1001` as an infinite `Double`.
- A value must have the YAML type of its Haskell type. yamlet rejects some
values that aeson converts, e.g. `1.0` for an `Int`, `0.5` for a
`Rational` and `null` for a `Double`.
- The elements of a `Set` or an `IntSet` must be unique, so `[a, a]` is an
error. aeson keeps one of them.
- A `Fixed` value must be a multiple of the resolution of its type, so
`1.255` is an error for a `Centi`. aeson rounds it down to `1.25`.
- The mapping of a `Rational`, a `CalendarDiffDays` or a
`CalendarDiffTime` must have only the keys of the type, e.g. `numerator`
and `denominator`. aeson ignores other keys.
- `()` must be an empty list, `[]`, which is how aeson writes it. aeson
reads any value as `()`, and since aeson 2.2 also a missing field of type
`()`.
- `Proxy` has no instances, because it holds no value. aeson writes it as
`null` and reads any value as a `Proxy`.
## Generic instances
- A key that is not a field of the constructor is an error. aeson ignores
such a key. To ignore it in yamlet too, turn off the option
`rejectUnknownFields`.
- A type with one constructor without fields is the name of the
constructor, e.g. `Unit`. aeson writes `[]`.
- With the encoding `SingleField`, a constructor without fields is its
name, e.g. `Dot`. aeson writes `{Dot: []}`.
- A constructor with several fields without names is a compile error.
aeson writes the fields as a list. Give the fields names.
- A type cannot mix a constructor with named fields and a constructor with
one field without a name, except with the encoding `SingleField`. Such a
type is a compile error. aeson writes the field without a name under the
key `contents`.
The documentation of the instances in
[Yamlet.Decode](https://hackage.haskell.org/package/yamlet/docs/Yamlet-Decode.html)
and [Yamlet.Encode](https://hackage.haskell.org/package/yamlet/docs/Yamlet-Encode.html),
and of the options in
[Yamlet.Generic](https://hackage.haskell.org/package/yamlet/docs/Yamlet-Generic.html),
describes the remaining details.