How to instantiate ListIsomorphic for shared vectors?

Given the following class:

class ListIsomorphic l where toList :: la -> [a] fromList :: [a] -> la 

How to write an instance for vector types using Data.Vector.Generic ? This does not work:

 instance (V.Vector va) => ListIsomorphic v where toList = V.toList fromList = V.fromList 

Giving me:

 test.hs:31:10: Variable 'a' occurs more often than in the instance head in the constraint: V.Vector va (Use UndecidableInstances to permit this) In the instance declaration for 'ListIsomorphic v' 
+4
source share
2 answers

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

 {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE TypeFamilies #-} import Data.Constraint class ConstrainedList l where type Elem la :: Constraint toList :: Elem la => la -> [a] fromList :: Elem la => [a] -> la 

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.

 {-# LANGUAGE FlexibleInstances #-} class Any a instance Any a data AList a = AList {getList :: [a]} deriving (Show) instance ConstrainedList AList where type Elem AList a = Any a toList = getList fromList = AList 

() 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 :=>

 {-# LANGUAGE MultiParamTypeClasses #-} -- class () => Any a instance Class () (Any a) where cls = Sub Dict -- instance () => Any a instance () :=> Any a where ins = Sub Dict 

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' 
+6
source

I often run into this problem. Here are two solutions I came up with:

  • Change class parameters:

     class ListIsomorphic la where toList :: la -> [a] fromList :: [a] -> la instance (V.Vector va) => Listable va where ... 
  • Use constraint types

     class ListIsomorphic l where type C la :: Constraint toList :: la -> [a] fromList :: [a] -> la instance Listable v where type C va = (V.Vector va) ... 
+5
source

All Articles