Avoiding repeated instance declarations in Haskell The 2019 Stack Overflow Developer Survey Results Are InRemoving repetitions from multiple instance declarationsGetting started with HaskellWhat is Haskell actually useful for?Large-scale design in Haskell?Speed comparison with Project Euler: C vs Python vs Erlang vs Haskellhaskell — any way to generate “deriving” instances for roughly-tuple-isomorphic data types?Why can't GHC derive instances for Monoid?Using custom instance when deriving an instance via GeneralizedNewtypeDerivingHow to make aliases with yaml library in Haskell?Custom ToJSON instance for Persistent KeyWhat does deriving do/mean in Haskell?
Origin of "cooter" meaning "vagina"
What is the meaning of the verb "bear" in this context?
Are there incongruent pythagorean triangles with the same perimeter and same area?
What do the Banks children have against barley water?
Which Sci-Fi work first showed weapon of galactic-scale mass destruction?
Is an up-to-date browser secure on an out-of-date OS?
Can one be advised by a professor who is very far away?
Why do some words that are not inflected have an umlaut?
Multiply Two Integer Polynomials
Why do UK politicians seemingly ignore opinion polls on Brexit?
Can we generate random numbers using irrational numbers like π and e?
FPGA - DIY Programming
Am I thawing this London Broil safely?
What is the closest word meaning "respect for time / mindful"
Apparent duplicates between Haynes service instructions and MOT
Did 3000BC Egyptians use meteoric iron weapons?
How to answer pointed "are you quitting" questioning when I don't want them to suspect
Does a dangling wire really electrocute me if I'm standing in water?
Is this app Icon Browser Safe/Legit?
Can you compress metal and what would be the consequences?
Does the shape of a die affect the probability of a number being rolled?
Protecting Dualbooting Windows from dangerous code (like rm -rf)
Does coating your armor in silver add any effects?
What did it mean to "align" a radio?
Avoiding repeated instance declarations in Haskell
The 2019 Stack Overflow Developer Survey Results Are InRemoving repetitions from multiple instance declarationsGetting started with HaskellWhat is Haskell actually useful for?Large-scale design in Haskell?Speed comparison with Project Euler: C vs Python vs Erlang vs Haskellhaskell — any way to generate “deriving” instances for roughly-tuple-isomorphic data types?Why can't GHC derive instances for Monoid?Using custom instance when deriving an instance via GeneralizedNewtypeDerivingHow to make aliases with yaml library in Haskell?Custom ToJSON instance for Persistent KeyWhat does deriving do/mean in Haskell?
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;
My question seems to be closely related to this
one.
My code parses a yaml file, rearanges the objects and writes a new yaml file. It works perfectly well, but there is a particularly ugly part in it.
I have to declare my data structures as instances of FromJson
and ToJson
like this:
instance FromJSON Users where
parseJSON = genericParseJSON (defaultOptions fieldLabelModifier = body_noprefix )
instance ToJSON Users where
toJSON = genericToJSON (defaultOptions fieldLabelModifier = body_noprefix )
The problem is that I have to repeat this for 8 or so other cases:
instance FromJSON Role where
parseJSON = genericParseJSON (defaultOptions fieldLabelModifier = body_noprefix )
instance ToJSON Role where
toJSON = genericToJSON (defaultOptions fieldLabelModifier = body_noprefix )
...
...
I could not figure out how to avoid this repetition. Is there some method to declare the two functions just once (for example in a new class) and let all these data types derive from it?
Solution (see also accepted answer by dfeuer):
I personally like this solution. You'll need to add
-# language DerivingVia #-
-# language UndecidableInstances #-
newtype NP a = NP unNP::a
instance (Generic a, GFromJSON Zero (Rep a)) => FromJSON (NP a) where
parseJSON = fmap NP . genericParseJSON
(defaultOptions fieldLabelModifier = body_noprefix )
instance (Generic a, GToJSON Zero (Rep a)) => ToJSON (NP a) where
toJSON = genericToJSON (defaultOptions fieldLabelModifier = body_noprefix ) . unNP
Then you can declare the types like this:
data User = User ... deriving (Show, Generic)
deriving FromJSON via (NP User)
deriving ToJSON via (NP User)
haskell instance dry
add a comment |
My question seems to be closely related to this
one.
My code parses a yaml file, rearanges the objects and writes a new yaml file. It works perfectly well, but there is a particularly ugly part in it.
I have to declare my data structures as instances of FromJson
and ToJson
like this:
instance FromJSON Users where
parseJSON = genericParseJSON (defaultOptions fieldLabelModifier = body_noprefix )
instance ToJSON Users where
toJSON = genericToJSON (defaultOptions fieldLabelModifier = body_noprefix )
The problem is that I have to repeat this for 8 or so other cases:
instance FromJSON Role where
parseJSON = genericParseJSON (defaultOptions fieldLabelModifier = body_noprefix )
instance ToJSON Role where
toJSON = genericToJSON (defaultOptions fieldLabelModifier = body_noprefix )
...
...
I could not figure out how to avoid this repetition. Is there some method to declare the two functions just once (for example in a new class) and let all these data types derive from it?
Solution (see also accepted answer by dfeuer):
I personally like this solution. You'll need to add
-# language DerivingVia #-
-# language UndecidableInstances #-
newtype NP a = NP unNP::a
instance (Generic a, GFromJSON Zero (Rep a)) => FromJSON (NP a) where
parseJSON = fmap NP . genericParseJSON
(defaultOptions fieldLabelModifier = body_noprefix )
instance (Generic a, GToJSON Zero (Rep a)) => ToJSON (NP a) where
toJSON = genericToJSON (defaultOptions fieldLabelModifier = body_noprefix ) . unNP
Then you can declare the types like this:
data User = User ... deriving (Show, Generic)
deriving FromJSON via (NP User)
deriving ToJSON via (NP User)
haskell instance dry
add a comment |
My question seems to be closely related to this
one.
My code parses a yaml file, rearanges the objects and writes a new yaml file. It works perfectly well, but there is a particularly ugly part in it.
I have to declare my data structures as instances of FromJson
and ToJson
like this:
instance FromJSON Users where
parseJSON = genericParseJSON (defaultOptions fieldLabelModifier = body_noprefix )
instance ToJSON Users where
toJSON = genericToJSON (defaultOptions fieldLabelModifier = body_noprefix )
The problem is that I have to repeat this for 8 or so other cases:
instance FromJSON Role where
parseJSON = genericParseJSON (defaultOptions fieldLabelModifier = body_noprefix )
instance ToJSON Role where
toJSON = genericToJSON (defaultOptions fieldLabelModifier = body_noprefix )
...
...
I could not figure out how to avoid this repetition. Is there some method to declare the two functions just once (for example in a new class) and let all these data types derive from it?
Solution (see also accepted answer by dfeuer):
I personally like this solution. You'll need to add
-# language DerivingVia #-
-# language UndecidableInstances #-
newtype NP a = NP unNP::a
instance (Generic a, GFromJSON Zero (Rep a)) => FromJSON (NP a) where
parseJSON = fmap NP . genericParseJSON
(defaultOptions fieldLabelModifier = body_noprefix )
instance (Generic a, GToJSON Zero (Rep a)) => ToJSON (NP a) where
toJSON = genericToJSON (defaultOptions fieldLabelModifier = body_noprefix ) . unNP
Then you can declare the types like this:
data User = User ... deriving (Show, Generic)
deriving FromJSON via (NP User)
deriving ToJSON via (NP User)
haskell instance dry
My question seems to be closely related to this
one.
My code parses a yaml file, rearanges the objects and writes a new yaml file. It works perfectly well, but there is a particularly ugly part in it.
I have to declare my data structures as instances of FromJson
and ToJson
like this:
instance FromJSON Users where
parseJSON = genericParseJSON (defaultOptions fieldLabelModifier = body_noprefix )
instance ToJSON Users where
toJSON = genericToJSON (defaultOptions fieldLabelModifier = body_noprefix )
The problem is that I have to repeat this for 8 or so other cases:
instance FromJSON Role where
parseJSON = genericParseJSON (defaultOptions fieldLabelModifier = body_noprefix )
instance ToJSON Role where
toJSON = genericToJSON (defaultOptions fieldLabelModifier = body_noprefix )
...
...
I could not figure out how to avoid this repetition. Is there some method to declare the two functions just once (for example in a new class) and let all these data types derive from it?
Solution (see also accepted answer by dfeuer):
I personally like this solution. You'll need to add
-# language DerivingVia #-
-# language UndecidableInstances #-
newtype NP a = NP unNP::a
instance (Generic a, GFromJSON Zero (Rep a)) => FromJSON (NP a) where
parseJSON = fmap NP . genericParseJSON
(defaultOptions fieldLabelModifier = body_noprefix )
instance (Generic a, GToJSON Zero (Rep a)) => ToJSON (NP a) where
toJSON = genericToJSON (defaultOptions fieldLabelModifier = body_noprefix ) . unNP
Then you can declare the types like this:
data User = User ... deriving (Show, Generic)
deriving FromJSON via (NP User)
deriving ToJSON via (NP User)
haskell instance dry
haskell instance dry
edited Mar 21 at 15:48
fata
asked Mar 20 at 19:18
fatafata
234
234
add a comment |
add a comment |
3 Answers
3
active
oldest
votes
This is what the fairly new DerivingVia
extension is for, among other things.
-# language DerivingVia #-
newtype NP a = NP unNP::a
instance (Generic a, GFromJSON Zero (Rep a)) => FromJSON (NP a) where
parseJSON = fmap NP . genericParseJSON
(defaultOptions fieldLabelModifier = body_noprefix )
instance (Generic a, GToJSON Zero (Rep a)) => ToJSON (NP a) where
toJSON = genericToJSON (defaultOptions fieldLabelModifier = body_noprefix ) . unNP
Now, you can write
deriving via (NP User) instance FromJSON User
Or
data User = ...
deriving Generic
deriving (FromJSON, ToJSON) via (NP User)
and so on.
This doesn't save a lot over leftaroundabout's answer as it is. However, once you add a definition of toEncoding
, it starts to look worthwhile.
Caution: I have tested none of this.
I see this as the most elegant solution until now. There is just a small typo here:ToJSON =
should betoJSON =
.
– fata
Mar 21 at 15:50
add a comment |
Like,
noPrefixParseJSON :: (Generic a, GFromJSON Zero (Rep a)) => Value -> Parser a
noPrefixParseJSON
= genericParseJSON (defaultOptions fieldLabelModifier = body_noprefix )
noPrefixToJSON :: (Generic a, GToJSON Zero (Rep a)) => a -> Value
noPrefixToJSON
= genericToJSON (defaultOptions fieldLabelModifier = body_noprefix )
instance FromJSON User where parseJSON = noPrefixParseJSON
instance ToJSON User where toJSON = noPrefixToJSON
instance FromJSON Role where parseJSON = noPrefixParseJSON
instance ToJSON Role where toJSON = noPrefixToJSON
...
Sure this is still kind of repetitive, but I'd say this isn't any cause of worry any more.
It's a good idea to extract the function definitions but the main problem still remains in my opinion. Maybe a meta programming solution via template haskell is the correct way to go?
– fata
Mar 20 at 20:59
add a comment |
If explicitly declaring all those similar instances proves too onerous, perhaps you could parameterize your datatypes with a phantom type like
data User x = User aa :: Int, bb :: Bool deriving Generic
data Role x = Role xx :: Int, dd :: Bool deriving Generic
and then define a "marker" datatype like
data Marker
This datatype will only serve as a peg on which to hang instances like the following
-# language UndecidableInstances #-
instance (Generic (f Marker), GFromJSON Zero (Rep (f Marker))) => FromJSON (f Marker) where
parseJSON = noPrefixParseJSON
Would it be worth it? Likely not, because the definition of your datatypes becomes more complex. On the other hand, you could change aspects of the serialization by varying the marker, so you gain some flexibility.
That instance overlaps a lot. I think a newtype taking a phantom marker (or even a list of markers) would be a lot cleaner. That could also be useful for theDerivingVia
approach I outlined in my answer, if you want to do a lot of type-level work up front.
– dfeuer
Mar 22 at 1:51
@dfeuer I believe it overlaps with more concrete types also parametrized with Marker, but the idea is that Marker will be used only with entities defined in the same module, and for those we wanted "uniform" instances anyway. A problem I see with the newtype is that ifRole
is nested withinUser
, we also have to put the newtype there, which seems inconvenient.
– danidiaz
Mar 22 at 7:24
I'm pretty sure it will also affect instance resolution in some other circumstances. For example, suppose you want the instance forConst Int a
, wherea
is neither specified nor inferred. Having your instance in scope will cause resolution to fail where it would otherwise succeed, because the compiler now wants to know thata
is not the marker type.
– dfeuer
Mar 22 at 7:56
@dfeuer The GHC User Guide states that "It is fine for there to be a potential of overlap […] an error is only reported if a particular constraint matches more than one [instance]" downloads.haskell.org/~ghc/latest/docs/html/users_guide/… So, as long as we don't requireFromJSON
from aConst Int Marker
in our program, we won't get any overlapping instances error.
– danidiaz
Mar 22 at 18:30
Huh .... It looks like I was overly optimistic about resolution without that instance. I'm pretty sure I've encountered situations where that hurt when messing around withData.Constraint.Forall
; I'll have to dig a bit further and see if I can produce a real example.
– dfeuer
Mar 22 at 20:09
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55268631%2favoiding-repeated-instance-declarations-in-haskell%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
This is what the fairly new DerivingVia
extension is for, among other things.
-# language DerivingVia #-
newtype NP a = NP unNP::a
instance (Generic a, GFromJSON Zero (Rep a)) => FromJSON (NP a) where
parseJSON = fmap NP . genericParseJSON
(defaultOptions fieldLabelModifier = body_noprefix )
instance (Generic a, GToJSON Zero (Rep a)) => ToJSON (NP a) where
toJSON = genericToJSON (defaultOptions fieldLabelModifier = body_noprefix ) . unNP
Now, you can write
deriving via (NP User) instance FromJSON User
Or
data User = ...
deriving Generic
deriving (FromJSON, ToJSON) via (NP User)
and so on.
This doesn't save a lot over leftaroundabout's answer as it is. However, once you add a definition of toEncoding
, it starts to look worthwhile.
Caution: I have tested none of this.
I see this as the most elegant solution until now. There is just a small typo here:ToJSON =
should betoJSON =
.
– fata
Mar 21 at 15:50
add a comment |
This is what the fairly new DerivingVia
extension is for, among other things.
-# language DerivingVia #-
newtype NP a = NP unNP::a
instance (Generic a, GFromJSON Zero (Rep a)) => FromJSON (NP a) where
parseJSON = fmap NP . genericParseJSON
(defaultOptions fieldLabelModifier = body_noprefix )
instance (Generic a, GToJSON Zero (Rep a)) => ToJSON (NP a) where
toJSON = genericToJSON (defaultOptions fieldLabelModifier = body_noprefix ) . unNP
Now, you can write
deriving via (NP User) instance FromJSON User
Or
data User = ...
deriving Generic
deriving (FromJSON, ToJSON) via (NP User)
and so on.
This doesn't save a lot over leftaroundabout's answer as it is. However, once you add a definition of toEncoding
, it starts to look worthwhile.
Caution: I have tested none of this.
I see this as the most elegant solution until now. There is just a small typo here:ToJSON =
should betoJSON =
.
– fata
Mar 21 at 15:50
add a comment |
This is what the fairly new DerivingVia
extension is for, among other things.
-# language DerivingVia #-
newtype NP a = NP unNP::a
instance (Generic a, GFromJSON Zero (Rep a)) => FromJSON (NP a) where
parseJSON = fmap NP . genericParseJSON
(defaultOptions fieldLabelModifier = body_noprefix )
instance (Generic a, GToJSON Zero (Rep a)) => ToJSON (NP a) where
toJSON = genericToJSON (defaultOptions fieldLabelModifier = body_noprefix ) . unNP
Now, you can write
deriving via (NP User) instance FromJSON User
Or
data User = ...
deriving Generic
deriving (FromJSON, ToJSON) via (NP User)
and so on.
This doesn't save a lot over leftaroundabout's answer as it is. However, once you add a definition of toEncoding
, it starts to look worthwhile.
Caution: I have tested none of this.
This is what the fairly new DerivingVia
extension is for, among other things.
-# language DerivingVia #-
newtype NP a = NP unNP::a
instance (Generic a, GFromJSON Zero (Rep a)) => FromJSON (NP a) where
parseJSON = fmap NP . genericParseJSON
(defaultOptions fieldLabelModifier = body_noprefix )
instance (Generic a, GToJSON Zero (Rep a)) => ToJSON (NP a) where
toJSON = genericToJSON (defaultOptions fieldLabelModifier = body_noprefix ) . unNP
Now, you can write
deriving via (NP User) instance FromJSON User
Or
data User = ...
deriving Generic
deriving (FromJSON, ToJSON) via (NP User)
and so on.
This doesn't save a lot over leftaroundabout's answer as it is. However, once you add a definition of toEncoding
, it starts to look worthwhile.
Caution: I have tested none of this.
edited Mar 22 at 3:41
answered Mar 21 at 2:14
dfeuerdfeuer
33.8k349133
33.8k349133
I see this as the most elegant solution until now. There is just a small typo here:ToJSON =
should betoJSON =
.
– fata
Mar 21 at 15:50
add a comment |
I see this as the most elegant solution until now. There is just a small typo here:ToJSON =
should betoJSON =
.
– fata
Mar 21 at 15:50
I see this as the most elegant solution until now. There is just a small typo here:
ToJSON =
should be toJSON =
.– fata
Mar 21 at 15:50
I see this as the most elegant solution until now. There is just a small typo here:
ToJSON =
should be toJSON =
.– fata
Mar 21 at 15:50
add a comment |
Like,
noPrefixParseJSON :: (Generic a, GFromJSON Zero (Rep a)) => Value -> Parser a
noPrefixParseJSON
= genericParseJSON (defaultOptions fieldLabelModifier = body_noprefix )
noPrefixToJSON :: (Generic a, GToJSON Zero (Rep a)) => a -> Value
noPrefixToJSON
= genericToJSON (defaultOptions fieldLabelModifier = body_noprefix )
instance FromJSON User where parseJSON = noPrefixParseJSON
instance ToJSON User where toJSON = noPrefixToJSON
instance FromJSON Role where parseJSON = noPrefixParseJSON
instance ToJSON Role where toJSON = noPrefixToJSON
...
Sure this is still kind of repetitive, but I'd say this isn't any cause of worry any more.
It's a good idea to extract the function definitions but the main problem still remains in my opinion. Maybe a meta programming solution via template haskell is the correct way to go?
– fata
Mar 20 at 20:59
add a comment |
Like,
noPrefixParseJSON :: (Generic a, GFromJSON Zero (Rep a)) => Value -> Parser a
noPrefixParseJSON
= genericParseJSON (defaultOptions fieldLabelModifier = body_noprefix )
noPrefixToJSON :: (Generic a, GToJSON Zero (Rep a)) => a -> Value
noPrefixToJSON
= genericToJSON (defaultOptions fieldLabelModifier = body_noprefix )
instance FromJSON User where parseJSON = noPrefixParseJSON
instance ToJSON User where toJSON = noPrefixToJSON
instance FromJSON Role where parseJSON = noPrefixParseJSON
instance ToJSON Role where toJSON = noPrefixToJSON
...
Sure this is still kind of repetitive, but I'd say this isn't any cause of worry any more.
It's a good idea to extract the function definitions but the main problem still remains in my opinion. Maybe a meta programming solution via template haskell is the correct way to go?
– fata
Mar 20 at 20:59
add a comment |
Like,
noPrefixParseJSON :: (Generic a, GFromJSON Zero (Rep a)) => Value -> Parser a
noPrefixParseJSON
= genericParseJSON (defaultOptions fieldLabelModifier = body_noprefix )
noPrefixToJSON :: (Generic a, GToJSON Zero (Rep a)) => a -> Value
noPrefixToJSON
= genericToJSON (defaultOptions fieldLabelModifier = body_noprefix )
instance FromJSON User where parseJSON = noPrefixParseJSON
instance ToJSON User where toJSON = noPrefixToJSON
instance FromJSON Role where parseJSON = noPrefixParseJSON
instance ToJSON Role where toJSON = noPrefixToJSON
...
Sure this is still kind of repetitive, but I'd say this isn't any cause of worry any more.
Like,
noPrefixParseJSON :: (Generic a, GFromJSON Zero (Rep a)) => Value -> Parser a
noPrefixParseJSON
= genericParseJSON (defaultOptions fieldLabelModifier = body_noprefix )
noPrefixToJSON :: (Generic a, GToJSON Zero (Rep a)) => a -> Value
noPrefixToJSON
= genericToJSON (defaultOptions fieldLabelModifier = body_noprefix )
instance FromJSON User where parseJSON = noPrefixParseJSON
instance ToJSON User where toJSON = noPrefixToJSON
instance FromJSON Role where parseJSON = noPrefixParseJSON
instance ToJSON Role where toJSON = noPrefixToJSON
...
Sure this is still kind of repetitive, but I'd say this isn't any cause of worry any more.
edited Mar 21 at 22:38
dfeuer
33.8k349133
33.8k349133
answered Mar 20 at 19:38
leftaroundaboutleftaroundabout
80.6k3121240
80.6k3121240
It's a good idea to extract the function definitions but the main problem still remains in my opinion. Maybe a meta programming solution via template haskell is the correct way to go?
– fata
Mar 20 at 20:59
add a comment |
It's a good idea to extract the function definitions but the main problem still remains in my opinion. Maybe a meta programming solution via template haskell is the correct way to go?
– fata
Mar 20 at 20:59
It's a good idea to extract the function definitions but the main problem still remains in my opinion. Maybe a meta programming solution via template haskell is the correct way to go?
– fata
Mar 20 at 20:59
It's a good idea to extract the function definitions but the main problem still remains in my opinion. Maybe a meta programming solution via template haskell is the correct way to go?
– fata
Mar 20 at 20:59
add a comment |
If explicitly declaring all those similar instances proves too onerous, perhaps you could parameterize your datatypes with a phantom type like
data User x = User aa :: Int, bb :: Bool deriving Generic
data Role x = Role xx :: Int, dd :: Bool deriving Generic
and then define a "marker" datatype like
data Marker
This datatype will only serve as a peg on which to hang instances like the following
-# language UndecidableInstances #-
instance (Generic (f Marker), GFromJSON Zero (Rep (f Marker))) => FromJSON (f Marker) where
parseJSON = noPrefixParseJSON
Would it be worth it? Likely not, because the definition of your datatypes becomes more complex. On the other hand, you could change aspects of the serialization by varying the marker, so you gain some flexibility.
That instance overlaps a lot. I think a newtype taking a phantom marker (or even a list of markers) would be a lot cleaner. That could also be useful for theDerivingVia
approach I outlined in my answer, if you want to do a lot of type-level work up front.
– dfeuer
Mar 22 at 1:51
@dfeuer I believe it overlaps with more concrete types also parametrized with Marker, but the idea is that Marker will be used only with entities defined in the same module, and for those we wanted "uniform" instances anyway. A problem I see with the newtype is that ifRole
is nested withinUser
, we also have to put the newtype there, which seems inconvenient.
– danidiaz
Mar 22 at 7:24
I'm pretty sure it will also affect instance resolution in some other circumstances. For example, suppose you want the instance forConst Int a
, wherea
is neither specified nor inferred. Having your instance in scope will cause resolution to fail where it would otherwise succeed, because the compiler now wants to know thata
is not the marker type.
– dfeuer
Mar 22 at 7:56
@dfeuer The GHC User Guide states that "It is fine for there to be a potential of overlap […] an error is only reported if a particular constraint matches more than one [instance]" downloads.haskell.org/~ghc/latest/docs/html/users_guide/… So, as long as we don't requireFromJSON
from aConst Int Marker
in our program, we won't get any overlapping instances error.
– danidiaz
Mar 22 at 18:30
Huh .... It looks like I was overly optimistic about resolution without that instance. I'm pretty sure I've encountered situations where that hurt when messing around withData.Constraint.Forall
; I'll have to dig a bit further and see if I can produce a real example.
– dfeuer
Mar 22 at 20:09
add a comment |
If explicitly declaring all those similar instances proves too onerous, perhaps you could parameterize your datatypes with a phantom type like
data User x = User aa :: Int, bb :: Bool deriving Generic
data Role x = Role xx :: Int, dd :: Bool deriving Generic
and then define a "marker" datatype like
data Marker
This datatype will only serve as a peg on which to hang instances like the following
-# language UndecidableInstances #-
instance (Generic (f Marker), GFromJSON Zero (Rep (f Marker))) => FromJSON (f Marker) where
parseJSON = noPrefixParseJSON
Would it be worth it? Likely not, because the definition of your datatypes becomes more complex. On the other hand, you could change aspects of the serialization by varying the marker, so you gain some flexibility.
That instance overlaps a lot. I think a newtype taking a phantom marker (or even a list of markers) would be a lot cleaner. That could also be useful for theDerivingVia
approach I outlined in my answer, if you want to do a lot of type-level work up front.
– dfeuer
Mar 22 at 1:51
@dfeuer I believe it overlaps with more concrete types also parametrized with Marker, but the idea is that Marker will be used only with entities defined in the same module, and for those we wanted "uniform" instances anyway. A problem I see with the newtype is that ifRole
is nested withinUser
, we also have to put the newtype there, which seems inconvenient.
– danidiaz
Mar 22 at 7:24
I'm pretty sure it will also affect instance resolution in some other circumstances. For example, suppose you want the instance forConst Int a
, wherea
is neither specified nor inferred. Having your instance in scope will cause resolution to fail where it would otherwise succeed, because the compiler now wants to know thata
is not the marker type.
– dfeuer
Mar 22 at 7:56
@dfeuer The GHC User Guide states that "It is fine for there to be a potential of overlap […] an error is only reported if a particular constraint matches more than one [instance]" downloads.haskell.org/~ghc/latest/docs/html/users_guide/… So, as long as we don't requireFromJSON
from aConst Int Marker
in our program, we won't get any overlapping instances error.
– danidiaz
Mar 22 at 18:30
Huh .... It looks like I was overly optimistic about resolution without that instance. I'm pretty sure I've encountered situations where that hurt when messing around withData.Constraint.Forall
; I'll have to dig a bit further and see if I can produce a real example.
– dfeuer
Mar 22 at 20:09
add a comment |
If explicitly declaring all those similar instances proves too onerous, perhaps you could parameterize your datatypes with a phantom type like
data User x = User aa :: Int, bb :: Bool deriving Generic
data Role x = Role xx :: Int, dd :: Bool deriving Generic
and then define a "marker" datatype like
data Marker
This datatype will only serve as a peg on which to hang instances like the following
-# language UndecidableInstances #-
instance (Generic (f Marker), GFromJSON Zero (Rep (f Marker))) => FromJSON (f Marker) where
parseJSON = noPrefixParseJSON
Would it be worth it? Likely not, because the definition of your datatypes becomes more complex. On the other hand, you could change aspects of the serialization by varying the marker, so you gain some flexibility.
If explicitly declaring all those similar instances proves too onerous, perhaps you could parameterize your datatypes with a phantom type like
data User x = User aa :: Int, bb :: Bool deriving Generic
data Role x = Role xx :: Int, dd :: Bool deriving Generic
and then define a "marker" datatype like
data Marker
This datatype will only serve as a peg on which to hang instances like the following
-# language UndecidableInstances #-
instance (Generic (f Marker), GFromJSON Zero (Rep (f Marker))) => FromJSON (f Marker) where
parseJSON = noPrefixParseJSON
Would it be worth it? Likely not, because the definition of your datatypes becomes more complex. On the other hand, you could change aspects of the serialization by varying the marker, so you gain some flexibility.
edited Mar 21 at 0:14
answered Mar 20 at 22:13
danidiazdanidiaz
17.5k33058
17.5k33058
That instance overlaps a lot. I think a newtype taking a phantom marker (or even a list of markers) would be a lot cleaner. That could also be useful for theDerivingVia
approach I outlined in my answer, if you want to do a lot of type-level work up front.
– dfeuer
Mar 22 at 1:51
@dfeuer I believe it overlaps with more concrete types also parametrized with Marker, but the idea is that Marker will be used only with entities defined in the same module, and for those we wanted "uniform" instances anyway. A problem I see with the newtype is that ifRole
is nested withinUser
, we also have to put the newtype there, which seems inconvenient.
– danidiaz
Mar 22 at 7:24
I'm pretty sure it will also affect instance resolution in some other circumstances. For example, suppose you want the instance forConst Int a
, wherea
is neither specified nor inferred. Having your instance in scope will cause resolution to fail where it would otherwise succeed, because the compiler now wants to know thata
is not the marker type.
– dfeuer
Mar 22 at 7:56
@dfeuer The GHC User Guide states that "It is fine for there to be a potential of overlap […] an error is only reported if a particular constraint matches more than one [instance]" downloads.haskell.org/~ghc/latest/docs/html/users_guide/… So, as long as we don't requireFromJSON
from aConst Int Marker
in our program, we won't get any overlapping instances error.
– danidiaz
Mar 22 at 18:30
Huh .... It looks like I was overly optimistic about resolution without that instance. I'm pretty sure I've encountered situations where that hurt when messing around withData.Constraint.Forall
; I'll have to dig a bit further and see if I can produce a real example.
– dfeuer
Mar 22 at 20:09
add a comment |
That instance overlaps a lot. I think a newtype taking a phantom marker (or even a list of markers) would be a lot cleaner. That could also be useful for theDerivingVia
approach I outlined in my answer, if you want to do a lot of type-level work up front.
– dfeuer
Mar 22 at 1:51
@dfeuer I believe it overlaps with more concrete types also parametrized with Marker, but the idea is that Marker will be used only with entities defined in the same module, and for those we wanted "uniform" instances anyway. A problem I see with the newtype is that ifRole
is nested withinUser
, we also have to put the newtype there, which seems inconvenient.
– danidiaz
Mar 22 at 7:24
I'm pretty sure it will also affect instance resolution in some other circumstances. For example, suppose you want the instance forConst Int a
, wherea
is neither specified nor inferred. Having your instance in scope will cause resolution to fail where it would otherwise succeed, because the compiler now wants to know thata
is not the marker type.
– dfeuer
Mar 22 at 7:56
@dfeuer The GHC User Guide states that "It is fine for there to be a potential of overlap […] an error is only reported if a particular constraint matches more than one [instance]" downloads.haskell.org/~ghc/latest/docs/html/users_guide/… So, as long as we don't requireFromJSON
from aConst Int Marker
in our program, we won't get any overlapping instances error.
– danidiaz
Mar 22 at 18:30
Huh .... It looks like I was overly optimistic about resolution without that instance. I'm pretty sure I've encountered situations where that hurt when messing around withData.Constraint.Forall
; I'll have to dig a bit further and see if I can produce a real example.
– dfeuer
Mar 22 at 20:09
That instance overlaps a lot. I think a newtype taking a phantom marker (or even a list of markers) would be a lot cleaner. That could also be useful for the
DerivingVia
approach I outlined in my answer, if you want to do a lot of type-level work up front.– dfeuer
Mar 22 at 1:51
That instance overlaps a lot. I think a newtype taking a phantom marker (or even a list of markers) would be a lot cleaner. That could also be useful for the
DerivingVia
approach I outlined in my answer, if you want to do a lot of type-level work up front.– dfeuer
Mar 22 at 1:51
@dfeuer I believe it overlaps with more concrete types also parametrized with Marker, but the idea is that Marker will be used only with entities defined in the same module, and for those we wanted "uniform" instances anyway. A problem I see with the newtype is that if
Role
is nested within User
, we also have to put the newtype there, which seems inconvenient.– danidiaz
Mar 22 at 7:24
@dfeuer I believe it overlaps with more concrete types also parametrized with Marker, but the idea is that Marker will be used only with entities defined in the same module, and for those we wanted "uniform" instances anyway. A problem I see with the newtype is that if
Role
is nested within User
, we also have to put the newtype there, which seems inconvenient.– danidiaz
Mar 22 at 7:24
I'm pretty sure it will also affect instance resolution in some other circumstances. For example, suppose you want the instance for
Const Int a
, where a
is neither specified nor inferred. Having your instance in scope will cause resolution to fail where it would otherwise succeed, because the compiler now wants to know that a
is not the marker type.– dfeuer
Mar 22 at 7:56
I'm pretty sure it will also affect instance resolution in some other circumstances. For example, suppose you want the instance for
Const Int a
, where a
is neither specified nor inferred. Having your instance in scope will cause resolution to fail where it would otherwise succeed, because the compiler now wants to know that a
is not the marker type.– dfeuer
Mar 22 at 7:56
@dfeuer The GHC User Guide states that "It is fine for there to be a potential of overlap […] an error is only reported if a particular constraint matches more than one [instance]" downloads.haskell.org/~ghc/latest/docs/html/users_guide/… So, as long as we don't require
FromJSON
from a Const Int Marker
in our program, we won't get any overlapping instances error.– danidiaz
Mar 22 at 18:30
@dfeuer The GHC User Guide states that "It is fine for there to be a potential of overlap […] an error is only reported if a particular constraint matches more than one [instance]" downloads.haskell.org/~ghc/latest/docs/html/users_guide/… So, as long as we don't require
FromJSON
from a Const Int Marker
in our program, we won't get any overlapping instances error.– danidiaz
Mar 22 at 18:30
Huh .... It looks like I was overly optimistic about resolution without that instance. I'm pretty sure I've encountered situations where that hurt when messing around with
Data.Constraint.Forall
; I'll have to dig a bit further and see if I can produce a real example.– dfeuer
Mar 22 at 20:09
Huh .... It looks like I was overly optimistic about resolution without that instance. I'm pretty sure I've encountered situations where that hurt when messing around with
Data.Constraint.Forall
; I'll have to dig a bit further and see if I can produce a real example.– dfeuer
Mar 22 at 20:09
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55268631%2favoiding-repeated-instance-declarations-in-haskell%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown