-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Safe updates to records. #39
Comments
|
Yes, but I don't use this constructor in my actual code any way. This was just for demonstration purposes.
Yes. This is because the more polymorphic a function is, the less ways there are to write it. Consider a function The most polymorphic type of this function is
However, it can also have the type
Now, you can be sure of the behaviour of Pretty much the only "reasonable" definition of
However, the compiler will accept all the following definitions of
Polymorphism limits the amount of information we have about a particular type, which in turn limits the possible manipulations we can perform with values of that type. In the above case, In our case, the only thing |
Constraint kinds allows you to manipulate
In this type, the constraint we have is a type parameter, instead of being fixed.
where |
But wouldn't a signature of fst::(a, b) -> a, be as polymorphic and work the same? |
The signatures are equivalent. I just added an explicit GHC automatically assumes all free type variables to be universally quantified. |
@wz1000 so, IIUC this type-machinery needs to be defined only once and then any record with regular lenses can use it:
Is that correct? Follow-up question: Does this scale to nested records? In the example above, a |
Yes.
You are missing the commas in the type level list
It'll work as long as you define the appropriate type classes with the necessary lenses and prisms to access the underlying fields. Also, this allows multiple methods of updating the same record. For instance, we may not want the user to update their password without reauthentication.
|
Sorry, I misunderstood. You want the underlying
then
|
@wz1000 can we have a usage sample at the end of the write-up? |
@saurabhnanda Done. |
Any thoughts on the core approach chalked out in the question at http://stackoverflow.com/questions/40171037/apply-a-function-to-all-fields-in-a-record-at-the-type-level/40171268# ? I do not really like the current solution to my question on Stackoverflow, but I got two alternative approaches on IRC: The basic idea is to have a bunch of tightly-related record-types and an easy way to generate them from a common source (eg a TH declaration, on the lines of what Persistent does). For example:
Once we have these types, it should be easy to write generic functions that do the following:
|
The recent PureScript update added some reflection-like features for working with records, and it enables some things that may be of interest. Here's a link to a Try PureScript demo that uses them to implement extensible records where some fields can be marked mandatory and some can be marked optional, and everything's statically checked as one might expect. |
The problem
We want a nice way to describe updates to a record. These updates are only allowed to edit certain fields of the record. Ideally, we don't want to have a new record to describe each kind of update as this leads to a lot of boilerplate and namespace pollution
Proposed solution
Let's consider a record
Tenant
with attributes like a name, backoffice domain, creation and updation times, status etc. Out of these, we only want the name and backoffice domain to be editable by the user.First, let's consider a generic way to represent updates to
Tenant
.This type can represent any kind of an update to a
Tenant
.In order to make this represent only specific kinds of updates, we first write typeclasses that implement lenses from
Tenant
to the fields we want to edit.Now, we can modify
TenantUpdater
Because this function is polymorphic over all
a
, the only way to create a function of this type is by composing variations ofset name
andset backofficeDomain
. Hence, this type is a safe way of representing updates to specific fields ofTenant
.We can generalise this concept a bit with the help of the
ConstraintKinds
extention.then
We can now define updaters for any other records we might have.
However, we have to define a new kind of
Constraint
for every suchUpdater
. In order to let us anonymously composeConstraints
, we can use this type family:Then
Now, we can modify our updater type to look like this:
Finally,
Usage example
We can define
FromJSON
instances that generate specificUpdater
s.Example here
We can apply an update to an entity like this:
Why the lens typeclasses cannot be automatically generated using Control.Lens.TH
Out of all the functions in
Control.Lens.TH
,makeFields
comes the closest to generating the typeclasses we want. Unfortunately it generates typeclasses parametrising over the field type, which complicates the usage ofAllC
andUpdater
.Example:
will generate
while we want
The text was updated successfully, but these errors were encountered: