Open user471 opened 9 years ago
I don't think so. The idea of only adding generic deriving to the compiler is so we don't have to add specific cases for all the potentially derivable classes that may come along - as the prelude isn't included in the compiler there's no guarantee that Show
, Eq
, Ord
, etc. will even exist in someone's project.
We're hoping to do something similar to generic deriving with newtype deriving though, which means you won't have to write explicit instances for newtypes at least.
But what is wrong with Haskell deriving syntax sugar?
data Foo = Foo Number String deriving( Eq, Show, Ord )
could generate all this code. Why cannot PS have this feature?
The idea of only adding generic deriving to the compiler is so we don't have to add specific cases for all the potentially derivable classes that may come along - as the prelude isn't included in the compiler there's no guarantee that
Show
,Eq
,Ord
, etc. will even exist in someone's project.
So? Why does compiler need to know about specific cases? We could write something like this
instance genericShow :: forall a. (Generic a) => Show a where
show = gShow
which means generic Show implementation And when compiler see this
data Foo = Foo Number String deriving(Show)
It search for generic Show implementation and use it to generate the instance code
instance showFoo :: Show Foo where
show = gShow
I don't see anything specific.
The first instance would be a potential option but having an instance like that precludes the ability of writing custom Show
classes for any type that you create a Generic
instance for, due to instance overlaps - so you'd either have the choice of fully Generic
or fully custom, the way things are now you have the option of mixing hand written and generic-based instances.
One option might be to somehow enhance class definitions to have a way of describing what an instance based on generics would look like, as that way yes, the compiler wouldn't need to know specifics. The deriving syntax would probably still be something like:
derive instance showFoo :: Show Foo
however, as explicitly named instances are here to stay.
instance like that precludes the ability of writing custom Show classes for any type that you create a Generic instance for
I don't get it. No one force you to use deriving(Show) syntax. You can always do everything manually. If we really want flexibility even for deriving then we could use import restriction.
module M1
instance genericShow :: forall a. (Generic a) => Show a where
show = gShow
module M2
instance otherGenericShow :: forall a. (Generic a) => Show a where
show = gShow2
import M1
data Foo = Foo Number String deriving(Show) // genericShow
import M2
data Foo = Foo Number String deriving(Show) // otherGenericShow
That (Generic a) => Show a
instance would have to be defined along with the Show
class as otherwise it would be an orphan.
Yes, but you should be able to define more specific instance
instance genericShow :: forall a. (Generic a) => Show a where
show = gShow
instance showFoo :: Show Foo where
show = "bar"
test = show $ Foo 1 "foo"
test == "bar" because showFoo more specific than genericShow
Unfortunately that isn't how instance resolution works, the concept of "more specific" gets rather slippery when you have multi-parameter typeclasses or instances with class contexts. It may still be worth exploring, but I suspect that someone more knowledgable than me will have a reason for why it's not allowed (Haskell also doesn't work this way).
Maybe it cannot work with all scenarios, but it differently could walk with simple one like this. There are many other possible solutions. What about import restriction? If there are two suitable instances
module Data.Generic
instance genericShow :: forall a. (Generic a) => Show a where
show = gShow
module M
instance showFoo :: Show Foo where
show = "bar"
import Data.Generic
import M
test = show $ Foo 1 "foo"
Compiler will force you to hide one instance
import Data.Generic hiding (genericShow)
import M
test = show $ Foo 1 "foo"
A syntax proposal from the channel:
derivable class Generic a => Show a where
show = gShow
instance showFoo :: Show Foo
i.e. a "derivable class" is sort of like a "default instance"
If you declare one -- and I guess really only one can exist per class, then if you declare an instance without methods, the derivable instance, if it matches, will fill in the instances for you...
Not arguing for this now or ever necessarily, but recording it for posterity as a sane approach.
@garyb
there's no guarantee that Show, Eq, Ord, etc. will even exist in someone's project.
I think there is. For example in haskell -XDerivingGeneric
wouldn't allow you to say deriving Generic
unless you import Data.Generic
.
The problem is of course (as you said earlier) that if someone defines their own Show
compiler might not be able to figure out that it's not the Show
that we were looking for.
We could go a bit duck-typish here, and do something like
derive (Show)
, check that
Show
is in the scopeShow
in the scope, is exactly the same as the Show
which we are thinking about, namely*
in the headshow :: a -> String
Becomes kind of ugly and complicated though.
Is there any plan to generate this code somehow automatically?