You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
For each record type e.g. Get_foo that represents a set of query parameters, the generator also produces a function mkGet_foo whose parameters are only the values of Get_foo that are required. This can be nice compared to using the Get_foo constructor directly because when an API adds new optional query parameters to a resource, that doesn't have to be a breaking change to users of the client. However, we've found some drawbacks:
Since mkGet_foo uses positional arguments rather than named record fields, it's easy to accidentally transpose the arguments if they are of the same type e.g. Text.
We've had one bug where we constructed a value using mkGet_foo and immediately followed by a Get_foo record update that overwrote one of the required fields that mkGet_foo had set. There's no indication in the types and no easy way from looking at the user code to see which mkGet_foo parameters correspond to which Get_foo record fields.
I'd like to throw out an idea:
For each Get_foo type, generate two corresponding record types
Required_Get_foo
Optional_Get_foo
which divide up the required and optional parameters respectively
Change mkGet_foo (or add a different function) to build a Get_foo from a Required_Get_foo and an Optional_Get_foo.
Define a constant Optional_Get_foo that has Nothing for all the fields, any of the following would work:
I think both Monoid and Default have the advantage that they can be derived with generics rather than writing yet more template haskell.
So parameter construction then could go from
parameters::Get_foo
parameters = (mkGet_foo a b){ someOptionalParameter = c }
to:
parameters::Get_foo
parameters =
mkGet_foo'
Required_Get_foo
{ requiredParameter = a
, anotherRequiredPameter = b
}
$ def
{ someOptionalParameter = c
}
That way we have the advantages of both the mk function and of record construction:
All the parameters are given explicitly by name.
Adding optional parameters doesn't cause a breaking change at the use site.
One additional thought: To limit the explosion in the number of new generated identifiers introduced, a type class with an associated data family might be a nice way to go about it.
For each record type e.g.
Get_foo
that represents a set of query parameters, the generator also produces a functionmkGet_foo
whose parameters are only the values ofGet_foo
that are required. This can be nice compared to using theGet_foo
constructor directly because when an API adds new optional query parameters to a resource, that doesn't have to be a breaking change to users of the client. However, we've found some drawbacks:mkGet_foo
uses positional arguments rather than named record fields, it's easy to accidentally transpose the arguments if they are of the same type e.g.Text
.mkGet_foo
and immediately followed by aGet_foo
record update that overwrote one of the required fields thatmkGet_foo
had set. There's no indication in the types and no easy way from looking at the user code to see whichmkGet_foo
parameters correspond to whichGet_foo
record fields.I'd like to throw out an idea:
For each
Get_foo
type, generate two corresponding record typesRequired_Get_foo
Optional_Get_foo
which divide up the required and optional parameters respectively
Change
mkGet_foo
(or add a different function) to build aGet_foo
from aRequired_Get_foo
and anOptional_Get_foo
.Define a constant
Optional_Get_foo
that hasNothing
for all the fields, any of the following would work:mempty
(if a semigroup operation is also wanted)I think both
Monoid
andDefault
have the advantage that they can be derived with generics rather than writing yet more template haskell.So parameter construction then could go from
to:
That way we have the advantages of both the
mk
function and of record construction:One additional thought: To limit the explosion in the number of new generated identifiers introduced, a type class with an associated data family might be a nice way to go about it.
Then adding support for this feature is mostly just a matter of generating
Parameters
instances.The text was updated successfully, but these errors were encountered: