Open buggymcbugfix opened 6 years ago
Hey, good questions!
What is the usual name for this and where can I find out more?
I don't know if they have a standard name. I used to refer to them as "types parametrized by a functor", but I find it too long. The parameterized-utils package defines analogs of the FunctorB
/ TraversableB
classes (but without the automatic derivation of instances); and there they call them "structures that accept a single parametric type parameter", which is probably worse :smiley:
I'm sure the idea of putting a type-parameter f
in front of your concrete types in a record goes back a long time. I seem to remember I first read someone mentioning it in the haskell-cafe mailing list many years ago, but I'm now unable to find it. It is a sort of folk-knowledge, I think, and I've seen data types defined in that way in the past in the wild, but unfortunately I don't know of any place to go read about it.
Also, where can I find example uses?
I gave a talk about the library at one one of the haskell meetups in London some time ago. The video is not up yet, I think, but the slides were for the most part code. Let me know if you need help reconstructing the context.
I'm using this mostly at work and I don't currently have other code examples in public repos, but these are some scenarios were I found it useful:
Working with data that needs to be read from outside (from a db, an http request) and "validated" before you can process it. Specially so, if you want to be able to know, which fields are ok, and which ones are rejected (e.g., to report them to the user). For instance, you have some Form f
representing a web-form, and you could have all these versions of the type:
A Form Identity
is one with all the data validated,
a Form (Const Text)
is a raw form where you've just collected the text input from the user,
a Form (Either ValidationError)
is one where where you've parsed each individual field and contain either a value or an error message,
a Form Parser
describes how to parse each individual field.
Using bzipWith
you can write something with type Form Parser -> Form (Const Text) -> Form (Either ValidationError)
to do the parsing. You can check if it is valid by using btraverse
to get a Form (Either ValidationError) -> Maybe (Form Identity)
. This is conceptually the same example you will find in the slides, but in another setting.
Similarly, you may have two versions of a Form Identity
, and you want to produce a "diff" that contains only the values that are different in the new version. Using bzipWith
and btraverse
you then get a function Form Identity -> Form Identity -> Maybe (Form Maybe)
, that returns Nothing
if the two versions are equal, or Just diff
in case there is at least one field where they differ.
You've stored these diffs in some sort of event log, which you can think of as a [Form Maybe]
. You are given this log and want to reconstruct the final value of the form. So you use bmap
to turn this into a [Form Last]
and because you should be able to get a Monoid
instance for Form Last
, you can mconcat
this list to get a Form Last
which you can then turn into a Form Maybe
or a Maybe (Form Identity)
, etc.
Of course, in real life, you probably have many types of different web-forms, so you should substitute the concrete Form
type by a form
type-variable in all the examples above! And if every type of form is to be saved on a database, you can use something like this to describe the mapping of each form with some primary key to a db:
data DbTable pk form = DbTable TableName (Column pk) (Form Column)
data Column a = Column ColumnName (ToDb a) (FromDb a)
And because a TraversableB
can be folded, you can use a DbTable pk form
to programmatically generate the inserts, updates, selects, etc. or even generate your full db schema.
Hope this makes some sense!
Thank you for this detailed reply! I think the context of the talk would be quite helpful, I presume I will have to pester Derek? If not, I will try to reconstruct, but what you have written here already makes a lot of sense and seems very sensible.
I will try to make use of Barbies in my code and see how I fare and what the caveats may be.
@isovector explores these concepts pretty deeply in this blog post and calls them "higher-kinded data" :)
This library looks super interesting. The README states that barbies are a common Haskell idiom.
What is the usual name for this and where can I find out more?
Also, where can I find example uses?