1
votes

I have this code:

module Coleccion where
  data Pila a= Pil [a] deriving (Show,Eq,Ord)
  data Cola a= Col [a] deriving (Show,Eq,Ord)

  class Coleccion n where
    esVacia::n->Bool
    insertar::n->a->n
    primero::n->a
    eliminar:: n->n
    size::n->Int;



  instance Coleccion (Pila a) where
    esVacia (Pil [])= True
    esVacia (Pil _ )= False
    primero (Pil x) = last x
    eliminar (Pil x)= (Pil (init x))
    size (Pil []) = 0
    size (Pil pila) = length pila;

And when I compile it with GHCI I have:

• Couldn't match expected type ‘a1’ with actual type ‘a’
      ‘a’ is a rigid type variable bound by
        the instance declaration at Coleccion.hs:18:12-29
      ‘a1’ is a rigid type variable bound by
        the type signature for:
          primero :: forall a1. Pila a -> a1
        at Coleccion.hs:21:5-11
    • In the expression: last x
      In an equation for ‘primero’: primero (Pil x) = last x
      In the instance declaration for ‘Coleccion (Pila a)’
    • Relevant bindings include
        x :: [a] (bound at Coleccion.hs:21:18)
        primero :: Pila a -> a1 (bound at Coleccion.hs:21:5)

The functions that I have a problem with them are functions that have a type distinct from n or predefined types (Bool, Integer...) like, for example, primero. For example, esVacia, size & eliminar are working OK. I don't know what is wrong in declarations or in instance code. Thank you.

2

2 Answers

5
votes

In your class declaration, the functions insertar and primera promise the consumer to work with any type a whatsoever (by the way: you're missing the insertar implementation). However, when implementing them in your class instance, you try to make it work with the specific a, which is the "contents" of Pila. The promise of the class is broken - the functions in the instance do not, in fact, work with any type a, - and so the compiler complains.

If you want to make this work, you need to declare class Coleccion in a way that says "n is a collection, which contains elements of type a, and here are the functions that should work with those types n and a".

One way to do this is to use an associated type:

class Coleccion n where
    type Elemento n
    esVacia::n->Bool
    insertar::n -> Elemento n -> n
    primero::n -> Elemento n
    eliminar:: n->n
    size::n->Int;

This declaration says: "whoever defines an instance of Coleccion for some type n, should also provide a way to determine what the type of elements of that collection is, and insertar and primero should work on that very type, not on some random unrelated one". You would implement it like this:

instance Coleccion (Pila a) where
    type Elemento (Pila a) = a
    ...
    primero (Pil x) = last x

Here, you say: "my collection is Pila a, and its elements are a, and here's how primero should work on it".

(NOTE: you'll need the TypeFamilies extension to do this)

Alternatively, you could use functional dependencies and two-parameter type class:

class Coleccion n el | n -> el where
    ...
    insertar::n -> el -> n
    primero::n -> el

This declaration says: "whenever you define an instance of Coleccion for some type n, you should also specify what the element el of that collection is". The "arrow" after the "pipe" says that the collection type should unambiguously determine what the element is - that is, you can't have one collection contain elements of several different types.

Then you implement it like this:

instance Coleccion (Pila a) a where
    ...
    primero (Pil x) = last x

(NOTE: you'll need FunctionalDependencies and MultiParamTypeClasses extensions for this)

1
votes

@Fyodor Soikin solution is great this one other way to do this

module Coleccion where
  data Pila a= Pil [a] deriving (Show,Eq,Ord)
  data Cola a= Col [a] deriving (Show,Eq,Ord)

  class Coleccion n where
    esVacia::n->Bool
    --insertar::n->a->n
    primero::n->n
    eliminar:: n->n
    size::n->Int



  instance Coleccion (Pila a) where
    esVacia (Pil [])= True
    esVacia (Pil _ )= False
    primero (Pil xs) = (Pil [(last xs)])
    eliminar (Pil xs)= (Pil (init xs))
    size (Pil []) = 0
    size (Pil xs) = length xs 

now using Monad , Functor and Applicative you can do anything you want