Scala provides a handful of language features that are designed to make it easy
for users to define and work with
algebraic data types (ADTs).
You write some case classes that extend a sealed trait, you write some functions
that pattern match on those case classes,
and you're done: you have a nice linked list or rose tree or whatever.
Sometimes you can't use case classes to implement your variants, though,
or you don't want to put your case classes in your public API, and in these
situations pattern matching is typically much less useful. This blog post is about
the visitor pattern, which
is an alternative to pattern matching that provides many of its benefits, and about
the use of visitors we're planning for Circe 1.0.
I'm publishing this article as a blog post (rather than as part of the circe documentation) because
it's intended to be a discursive overview of the representation of JSON numbers in
circe 0.3.0, not as an up-to-date description of the implementation in whatever the
current circe version happens to be when you're reading this. For information about JSON numbers in
circe versions after 0.3.0, please see the project documentation (in particular the
changelogs and API docs).
Suppose we're writing a service that accepts JSON requests and returns some kind of response. If
there's a problem with a request—it's not even valid JSON, it doesn't match the schema we expect,
etc.—we want to return an error, and of course it'd be nice if these errors were actually useful to
Unfortunately "useful" in this context can mean lots of different things, and the differences will
usually depend at least in part on how involved a human was in creating the request. In the case of
validation errors—i.e. we successfully received some JSON, but it's not a shape we understand—then
if there's no human in sight, we generally only need to say something like "hey, we're not even
speaking the same language, you should probably go try somewhere else". A detailed breakdown of all
the reasons we don't understand the request is unlikely to be useful, so we might as well fail as
fast as possible and save resources.
If on the other hand a human was responsible for the content of the request, it's possible that
the caller will be able to make use of detailed information about all the problems with that
content. Suppose for example that the JSON comes from a web form or spreadsheet that for whatever
reason needs to be at least partially validated on the server side. In this case we probably don't
want to fail fast—we want to accumulate all of the errors and send them back together, so that the
human can correct them in a single pass.