2011-01-26 11 views
9

¿Hay alguna manera de definir una restricción de clase para un parámetro del constructor de valores?Especificación de restricciones de clase en los constructores de valores

Algo como esto:

data Point2D = (Num a) => Point a a 

modo que punto puede tomar cualquier argumento, siempre y cuando pertenezcan a la clase Num?

+0

En general, es mejor poner sus restricciones de tipo donde realmente se necesitan. No es el tipo de datos, sino los métodos donde se necesita el tipo Num, por lo que debe declararse allí. El sistema tipo se encargará del resto. – amccausl

Respuesta

11

Puede usar ExistentialQuantification o GADTs, pero ninguno va a hacer lo que quiera. Nunca podrá hacer aritmética con dos valores de Point2D. Todo lo que sabes sobre el contenido es que son alguna instancia de de Num. Le está diciendo al compilador que descarte toda otra información sobre ellos. Esto significa que le está diciendo al compilador que descarte cualquier información que pueda tener sobre un par particular de valores de Point2D que contenga el mismo tipo. Y sin esa información, no podrá hacer ninguna aritmética sobre los valores de dos Point2D juntos.

Esto casi seguro no es lo que desea. No puede escribir una función distance, por ejemplo. ¿Qué uso posible podría tener para un tipo tan limitado? Todo lo que puede hacer con ellos es convertir sus contenidos a String.

Editar:

creo ver lo que estás tratando de hacer. Solo quiere asegurarse de que todo en un Point2D sea un número. No creo que realmente quieras borrar el tipo.

En ese caso, me gustaría ir con la versión GADT, con un cambio muy importante:

{-# LANGUAGE GADTs #-} 
data Point2D a where 
    Point :: (Num a) => a -> a -> Point2D a 

El resultado final de esto es que sólo se puede utilizar el constructor Point con dos valores del mismo instancia de Num, pero no pierdes el tipo. Además, gracias al uso de GADTs, la coincidencia de patrones en el constructor Point recupera el contexto numérico para usted, que es básicamente lo que usted esperaría.

Pero creo que lo más importante aquí es no tirar el tipo de contenido. Hacerlo hace que el tipo sea básicamente imposible de trabajar.

+0

La recuperación de contextos de instancia para los miembros de datos parece funcionar solo cuando coinciden patrones en el tipo de datos (ya sea con '(Punto a b)' o con nombres de campo de registro 'Punto {x = a, y = b}'). Pero, ¿hay alguna forma de tener esto para funciones de acceso de campo? Es decir, cuando se accede a un 'Point'' p' con una expresión como '(x p)'. – Lii

4

Sí, pero tendrá que darse cuenta de que el significado de su restricción es diferente de los tipos genéricos habituales.

Por lo general, los medicamentos genéricos como en type Foo a = (a, a) significan

para todo tipoa, Foo a consta de dos a 's

Sin embargo, en su ejemplo, es necesario frase que de otro modo:

Para som e tipoa, Point2D se compone de dos a 's

o

Hay un tipo a que Point2D consiste en

Por lo tanto, el tipo genérico no es universal (para todos los tipos ...), pero existencial (existe algún tipo ...). Bajo GHC, podemos permitir que esto a través de la extenstion

{-# ExistentialQuantification #-} 

como se describe en este article on the topic. Su código, después de todo, es

data Point2D = forall a . Num a => Point a a 
2

¡Seguro!

Esto debería hacer lo que quiera:

{-# LANGUAGE GADTs #-} 

data Point2D a where 
    Point :: Num a => a -> a -> Point2D a 

p :: Num a => a -> a -> Point2D a 
p = Point 

sumP :: Point2D a -> Point2D a -> a 
sumP (Point a b) (Point c d) = a + b + c + d 

También puede utilizar existensials, pero entonces no puede hacer nada con los datos después de la coincidencia de patrones en él.

Cuestiones relacionadas