No. Adding an instance for all v to your Listable class will become cumbersome to use due to overlapping instances.
A Vector va => v not isomorphic to a list, because it is limited in that the elements can be elements of a list. You will need a class that captures this restriction, something like
{-
Instead of adding instances of ConstrainedList for all types of Vector va => v , which lead us to the overlapping territory of the instances, we will instead define it only for the types of interest to us. Below we will consider all types with a Vector instance in a vector package.
import qualified Data.Vector.Primitive as VP import qualified Data.Vector.Generic as VG instance ConstrainedList VP.Vector where type Elem VP.Vector a = VG.Vector VP.Vector a toList = VG.toList fromList = VG.fromList
Instances for other types
You can write an instance of ConstrainedList for regular lists [] , which requires only an empty constraint for its elements.
instance ConstrainedList [] where type Elem [] a = () toList = id fromList = id
Anywhere where toList or fromList , an instance of Elem la will also be required.
cmap :: (ConstrainedList l, Elem la, Elem lb) => (a -> b) -> la -> lb cmap f = fromList . map f . toList
When we know the specific types for lists and elements, these functions will be easy to use without conflict with restrictions.
cmap (+1) [1,2,3,4]
Here be dragons
Do not try the following. If you are interested in a class of things that are isomorphic to lists without additional restrictions, just create another class for it. It just shows what you can do when you conceive yourself in a corner: call the dragon.
You can also write functions that require proof that there are no restrictions on ConstrainedList elements. This is a path in the area of the constraints package and programming styles that are not supported by GHC, but the constraints examples are not enough, so I'll leave it here.
{-# LANGUAGE TypeOperators #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ScopedTypeVariables #-} map' :: forall la b. (ConstrainedList l, () :=> Elem la, () :=> Elem lb) => (a -> b) -> la -> lb map' f = case (ins :: () :- Elem la) of { Sub Dict -> case (ins :: () :- Elem lb) of { Sub Dict -> fromList . map f . toList }}
We can verify that a ConstrainedList has no restrictions, simply by checking that Elem la ~ () , but this will not work if its restriction was written differently.
{-
() is not the same type as Any a , although () implies Any a . The constraint package captures such relationships by combining them into classes of classes Class and :=>
{-
All this work allows us to easily use functions without providing all of these dictionaries when a particular type of list is known.
map'' :: (a -> b) -> AList a -> AList b map'' = map'