Reputation: 13
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.
Upvotes: 1
Views: 2618
Reputation: 912
@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
Upvotes: 2
Reputation: 80880
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)
Upvotes: 5