Skip to content
Anjunar/ DOCS

A typed filter API

makeProperty turns a selector into a named property handle. That is enough for a small query API: the compiler checks the property, the handle names it for display, and the accessor reads it.

API

where(_.year)("> 1950")(_ > 1950)

A filter keeps the property handle, a readable description and the test. inline passes the selector through to the macro.

scala
final case class Filter[T, V](property: PropertyWithAccessor[T, V], operator: String, test: V => Boolean) { def apply(value: T): Boolean = test(property.get(value)) def describe: String = s"${property.name} $operator" } inline def where[T, V](inline selector: T => V)(operator: String)(test: V => Boolean): Filter[T, V] = Filter(PropertySupport.makeProperty[T, V](selector), operator, test)
Live

Filter a list

Pick a filter. The label comes from the property handle, not from a string you typed twice.

Books

Three filters over one list.

filter.describe
year > 1950
books.filter(filter).map(_.title)
The Left Hand of Darkness, Foundation, The Dispossessed
Beyond lists

The same handle, other targets

property.name is what a database query, a URL parameter or a JSON Patch path needs. The test runs in memory; the name travels.

scala
def toQueryParameter(filter: Filter[?, ?], value: String): String = s"${filter.property.name}=${java.net.URLEncoder.encode(value, "UTF-8")}"
json-mapper works the same way

EntitySchema.property(_.title) is this pattern: a selector in, a named property handle with an accessor out.