propensive / adversaria

Providing typeclass interfaces to user-defined Scala annotations

Version Matrix

GitHub Workflow

Adversaria

Adversaria is a tiny library which provides a few tools to make it easier to work with static annotations in Scala, by making them available through typeclass interfaces.

Features

  • access all annotations on a type through a typeclass
  • resolve a typeclass instance only if a type has an annotated field
  • makes annotations more useful and accessible in Scala
  • no macro code is required to use annotations

Getting Started

The nature of annotations in Scala is that they are very rarely the best solution for any task. The can, however, be convenient in certain circumstances, and this small domain is where Adversaria aims to help.

Currently three use cases are supported:

  • getting all the annotations applied to a particular type
  • finding the parameter of a case class to which a particular annotation has been applied
  • getting all the annotations applied to a particular case class field

The list of supported use cases may grow as additional suggestions are received.

Examples

If we were to define the following annotations,

import scala.annotation.StaticAnnotation

final case class id() extends StaticAnnotation
final case class count(n: Int) extends StaticAnnotation

we could apply them to some case classes, such as,

@count(10)
case class Company(name: String)

case class Person(name: String, @id email: String)

Perhaps we would like to find out the annotations on Company. We can get this information by requesting an implicit TypeMetadata[Company] value, and accessing its annotations field, like so,

import adversaria._

val info = implicitly[TypeMetadata[Company]]
println(info.annotations)

> List(count(10))

The TypeMetadata implicit should resolve for any type, regardless of whether it has any annotations or not. Its annotations method will return a list of annotations on that case class.

Another supported use case is to find the field of a case class which has been annotated with a particular annotation, if and only if that annotation exists. We use the FindMetadata typeclass for this. It takes two type parameters: the type of the annotation, and the type to check for, respectively,

import adversaria._

val idField = implicitly[FindMetadata[id, Person]]
println(idField.get(Person("John Smith", "test@example.com)))

> test@example.com

However, attempting to resolve such an implicit on a case class which has no field annotated with that annotation, for example,

val idField = implicitly[FindMetadata[id, Company]]

will fail with at compiletime with the message,

adversaria: could not find a parameter annotated with type @id

Status

Adversaria is classified as fledgling. Propensive defines the following five stability levels for open-source projects:

  • embryonic: for experimental or demonstrative purposes only, without guarantee of longevity
  • fledgling: of proven utility, seeking contributions, but liable to significant redesigns
  • maturescent: major design decisions broady settled, seeking probatory adoption and refinement of designs
  • dependable: production-ready, subject to controlled ongoing maintenance and enhancement; tagged as version 1.0 or later
  • adamantine: proven, reliable and production-ready, with no further breaking changes ever anticipated

Availability

Adversaria’s source is available on GitHub, and may be built with Fury by cloning the layer propensive/adversaria.

fury layer clone -i propensive/adversaria

or imported into an existing layer with,

fury layer import -i propensive/adversaria

A binary is available on Maven Central as com.propensive:adversaria-core_<scala-version>:0.18.0. This may be added to an sbt build with:

libraryDependencies += "com.propensive" %% "adversaria-core" % "0.18.0"

Contributing

Contributors to Adversaria are welcome and encouraged. New contributors may like to look for issues marked label: good first issue.

We suggest that all contributors read the Contributing Guide to make the process of contributing to Adversaria easier.

Please do not contact project maintainers privately with questions, as other users cannot then benefit from the answers.

Author

Adversaria was designed and developed by Jon Pretty, and commercial support and training is available from Propensive OÜ.

License

Adversaria is copyright © 2017-20 Jon Pretty & Propensive OÜ, and is made available under the Apache 2.0 License.