# `RDF.XSD.Datatype`
[🔗](https://github.com/rdf-elixir/rdf-ex/blob/v3.0.1/lib/rdf/xsd/datatype.ex#L1)

A behaviour for XSD datatypes.

A XSD datatype has three properties:

- A _value space_, which is a set of values.
- A _lexical space_, which is a set of _literals_ used to denote the values.
- A collection of functions associated with the datatype.

### Builtin XSD datatypes

RDF.ex comes with the following builtin implementations of XSD datatypes:

| `xsd:boolean` | `RDF.XSD.Boolean` |
| `xsd:float` | `RDF.XSD.Float` |
| `xsd:double` | `RDF.XSD.Double` |
| `xsd:decimal` | `RDF.XSD.Decimal` |
|   `xsd:integer` | `RDF.XSD.Integer` |
|     `xsd:long` | `RDF.XSD.Long` |
|       `xsd:int` | `RDF.XSD.Int` |
|         `xsd:short` | `RDF.XSD.Short` |
|           `xsd:byte` | `RDF.XSD.Byte` |
|     `xsd:nonPositiveInteger` | `RDF.XSD.NonPositiveInteger` |
|       `xsd:negativeInteger` | `RDF.XSD.NegativeInteger` |
|     `xsd:nonNegativeInteger` | `RDF.XSD.NonNegativeInteger` |
|       `xsd:positiveInteger` | `RDF.XSD.PositiveInteger` |
|       `xsd:unsignedLong` | `RDF.XSD.UnsignedLong` |
|         `xsd:unsignedInt` | `RDF.XSD.UnsignedInt` |
|           `xsd:unsignedShort` | `RDF.XSD.UnsignedShort` |
|             `xsd:unsignedByte` | `RDF.XSD.UnsignedByte` |
| `xsd:string` | `RDF.XSD.String` |
|   `xsd:normalizedString` | ❌ |
|     `xsd:token` | ❌ |
|       `xsd:language` | ❌ |
|       `xsd:Name` | ❌ |
|         `xsd:NCName` | ❌ |
|           `xsd:ID` | ❌ |
|           `xsd:IDREF` | ❌ |
|           `xsd:ENTITY` | ❌ |
|       `xsd:NMTOKEN` | ❌ |
| `xsd:dateTime` | `RDF.XSD.DateTime` |
|   `xsd:dateTimeStamp` | ❌ |
| `xsd:date` | `RDF.XSD.Date` |
| `xsd:time` | `RDF.XSD.Time` |
| `xsd:duration` | ❌ |
|  `xsd:dayTimeDuration` | ❌ |
|  `xsd:yearMonthDuration` | ❌ |
| `xsd:gYearMonth` | ❌ |
| `xsd:gYear` | ❌ |
| `xsd:gMonthDay` | ❌ |
| `xsd:gDay` | ❌ |
| `xsd:gMonth` | ❌ |
| `xsd:base64Binary` | `RDF.XSD.Base64Binary` |
| `xsd:hexBinary` | ❌ |
| `xsd:anyURI` | `RDF.XSD.AnyURI` |
| `xsd:QName` | ❌ |
| `xsd:NOTATION` | ❌ |

There are some notable difference in the implementations of some datatypes compared to
the original spec:

- `RDF.XSD.Integer` is not derived from `RDF.XSD.Decimal`, but implemented as a primitive datatype
- `RDF.XSD.Float` is not implemented as a primitive datatype, but derived from `RDF.XSD.Double`
  without further restrictions instead, since Erlang doesn't have a corresponding datatype

see <https://www.w3.org/TR/xmlschema11-2/#built-in-datatypes>

# `literal`

```elixir
@type literal() :: %{
  __struct__: t(),
  value: any(),
  uncanonical_lexical: uncanonical_lexical()
}
```

# `t`

```elixir
@type t() :: module()
```

# `uncanonical_lexical`

```elixir
@type uncanonical_lexical() :: String.t() | nil
```

# `applicable_facets`

```elixir
@callback applicable_facets() :: [RDF.XSD.Facet.t()]
```

The set of applicable facets of a `RDF.XSD.Datatype`.

# `base`

```elixir
@callback base() :: t() | nil
```

The base datatype from which a `RDF.XSD.Datatype` is derived.

Note: Since this library focuses on atomic types and the special `xsd:anyAtomicType`
specified as the base type of all primitive types in the W3C spec wouldn't serve any
purpose here, all primitive datatypes just return `nil` instead.

# `base_primitive`

```elixir
@callback base_primitive() :: t()
```

The primitive `RDF.XSD.Datatype` from which a `RDF.XSD.Datatype` is derived.

In case of a primitive `RDF.XSD.Datatype` this function returns this `RDF.XSD.Datatype` itself.

# `canonical_mapping`

```elixir
@callback canonical_mapping(any()) :: String.t()
```

Returns the standard lexical representation for a value of the value space of a `RDF.XSD.Datatype`.

# `derived_from?`

```elixir
@callback derived_from?(t()) :: boolean()
```

Checks if the `RDF.XSD.Datatype` is directly or indirectly derived from the given `RDF.XSD.Datatype`.

Note that this is just a basic datatype reflection function on the module level
and does not work with `RDF.Literal`s. See `c:RDF.Literal.Datatype.datatype?/1` instead.

# `elixir_mapping`

```elixir
@callback elixir_mapping(any(), Keyword.t()) :: any() | {any(), uncanonical_lexical()}
```

A mapping from Elixir values into the value space of a `RDF.XSD.Datatype`.

If the Elixir mapping for the given value can not be mapped into value space of
the XSD datatype an implementation should return `@invalid_value`
(which is just `nil` at the moment, so `nil` is never a valid value of a value space).

Otherwise, a tuple `{value, lexical}` with `value` being the internal representation
of the mapped value from the value space and `lexical` being the lexical representation
to be used for the Elixir value or `nil` if `c:init_valid_lexical/3` should be used
to determine the lexical form in general (i.e. also when initialized with a string
via the `c:lexical_mapping/2`). Since the later case is most often what you want,
you can also return `value` directly, as long as it is not a two element tuple.

# `init_invalid_lexical`

```elixir
@callback init_invalid_lexical(any(), Keyword.t()) :: String.t()
```

Produces the lexical representation of an invalid value.

The default implementation of the `_using__` macro just returns the `to_string/1`
representation of the value.

# `init_valid_lexical`

```elixir
@callback init_valid_lexical(any(), uncanonical_lexical(), Keyword.t()) ::
  uncanonical_lexical()
```

Produces the lexical representation to be used for a `RDF.XSD.Datatype` literal.

By default, the lexical representation of a `RDF.XSD.Datatype` is either the
canonical form in case it is created from a non-string Elixir value or, if it
is created from a string, just with that string as the lexical form.

But there can be various reasons for why this should be different for certain
datatypes. For example, for `RDF.XSD.Double`s given as Elixir floats, we want the
default lexical representation to be the decimal and not the canonical
exponential form. Another reason might be that additional options are given
which should be taken into account in the lexical form.

If the lexical representation for a given `value` and `lexical` should be the
canonical one, an implementation should return `nil`.

# `lexical_mapping`

```elixir
@callback lexical_mapping(String.t(), Keyword.t()) :: any()
```

A mapping from the lexical space of a `RDF.XSD.Datatype` into its value space.

# `primitive?`

```elixir
@callback primitive?() :: boolean()
```

Returns if the `RDF.XSD.Datatype` is a primitive datatype.

# `get`

Returns the `RDF.XSD.Datatype` for a datatype IRI.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
