[Haskell-cafe] Optional "Default" instances of classes in certain subcases (but too broad)
Juan Casanova
juan.casanova at ed.ac.uk
Fri Oct 4 21:08:32 UTC 2019
Hello again,
Before my question, I notice I am using this list quite a bit. I hope
I am no abusing or misusing it by doing so, though. Sometimes I wonder
what's the line between what I should ask here and what I should ask
in StackOverflow, if that line even exists. If anyone feels I may be
abusing or misusing, please let me know (in public or in private).
Very simple question: Would it make sense, or does it already exist, a
way to implement *optional* default instances of classes that can be
used more directly than currently.
I am *not* talking about providing a default implementation of a class
when declaring it. I know you can do this.
I am talking about when you know you can provide a generic instance of
a class for a wide range of situations *which depends on some
constraints*. For example:
instance Bifunctor f => Functor (f a) where
fmap = bimap id
Of course, this is a terrible idea, because the constraints are not
checked when verifying overlap and the like, and so this is very
likely to break your program. What I tend to do nowadays is to add a
function:
fmapFromBiMap :: Bifunctor f => (b -> c) -> (f a b) -> (f a c)
fmapFromBiMap = bimap id
and then use it each time (the following is obviously a silly example):
instance Functor (Either a) where
fmap = fmapFromBiMap
The only problem I see with this is that I need to remember (specially
with my own type classes): 1. That I provided this "default"
implementation. 2. Its name. I know these are fairly small things to
complain about, but once again when you do it 100 times it becomes
annoying, specially for a person who absolutely does not like doing
things that I know would be able to explain how to automate.
So, what I would expect is for it to be something like declaring the
default instance but only use it for the types that explicitly ask for
it. E.g.:
optional instance Bifunctor f => Functor (f a) where
fmap = bimap id
instance Functor (Either a) using optional
The main important point here is that I expect this to check the
constraints in compile time for the class that I am giving (and if it
contains type variables, only use those that are guaranteed to be
complied, of course). This means that if I did:
instance Functor (f a) using optional
I *do not* expect it to be okay with it and automatically add the
constraint Bifunctor f => to the instance. I expect it to give me a
compile error: "No matching optional instance found".
Of course, there could be several optional instances that overlap when
using optional. But then, GHC would give me the error indicating what
the options are *and I would know that it's because I put optional*
and I could just replace it with the specific one to use. Maybe by
giving them names?
optional instance fmapFromBiMap Bifunctor f => Functor (f a) where
fmap = bimap id
instance Functor (Either a) using optional -- If this does not work
because it overlaps with other optionals, then GHC tells me, indicates
the options, and I replace with:
instance Functor (Either a) using fmapFromBiMap
This avoids the problem of defining the instance in general (that it
will overlap with basically anything), should be easy to type check
and prevents having to keep an index in your mind about useful
functions that you implemented or didn't.
I wonder if the "deriving" family of functionalities have anything
like this, but my search has not been fruitful. Maybe using Generics?
Maybe I just need good autocomplete so that I can find the function
easily... it still feels like this would make sense.
Juan.
--
The University of Edinburgh is a charitable body, registered in
Scotland, with registration number SC005336.
More information about the Haskell-Cafe
mailing list