2012-03-05 18 views
10

Quiero definir lo que parece requerir un tipo infinito.Deliberadamente definir el tipo infinito en haskell

requerido: una función de "comer" que se come todos sus argumentos, excepto "3" para el que regresa 3

eat 3 = 3 
eat x = eat 

Así que, básicamente, una expresión arbitraria como "(+) foldl comer (sólo 5) 3" evalúa a 3. Pero el problema aquí es el tipo de comer. ¿Qué debería ser eso?

lo más cerca que llegué a un código que se ejecuta era:

newtype Rec = MakeRec (Int -> Rec) 

eat :: Int -> Rec 
eat x = MakeRec eat 


instance Show Rec where 
    show _ = "EAT" 

Esto funciona bien para "comer 6" pero no para "comer 6 7" y no funciona si pongo (comer 3 = 3) en su definición.

No estoy seguro si esto es posible en Haskell. (¿Qué argumento usaría para mostrar que no es posible?)

ACTUALIZACIÓN: Como se indica en la solución a continuación, la información de tipo es necesaria en tiempo de compilación para que el compilador pueda saber si "come foldl 3 foldl" no es válido o no . Entonces, la solución exacta a este problema no es posible.

Respuesta

21

No es posible. Los tipos infinitos (que no son tipos de datos) están explícitamente prohibidos en Haskell, y es fácil producir código que los requiera, y por lo tanto produce un error (por ejemplo, intente let foo = (42, foo) in foo).

Puede, por supuesto, crearlos con newtype y data, como lo hizo, pero luego tiene que envolver y desenvolver explícitamente los valores dentro y fuera de los constructores.

Esta es una decisión de diseño explícita: con infinitos tipos, muchas expresiones obviamente erróneas que quisiéramos que el compilador rechazara deberían permitirse, y dado que se permitirían muchos más programas, muchos programas previamente sin ambigüedad se escribiría ambiguamente, requiriendo anotaciones de tipo explícito. Por lo tanto, se realiza una compensación: exigirle que sea explícito acerca de los usos bastante raros de los tipos infinitos a cambio de obtener mucha más ayuda del sistema de tipos de lo que haríamos de otra manera.

Dicho esto, no es una manera de definir algo similar a la función eat, utilizando las clases de tipos, pero no puede detener sólo cuando se le da un 3: si le ha dado un 3 o no puede solo se determinará en tiempo de ejecución, y los tipos se deciden en el momento de la compilación. Sin embargo, aquí es un valor de sobrecarga que puede ser tanto un Integer, y una función que solo se come su argumento:

class Eat a where 
    eat :: a 

instance Eat Integer where 
    eat = 3 

instance (Eat r) => Eat (a -> r) where 
    eat _ = eat 

El problema es que es necesario especificar los tipos precisamente cuando lo utiliza:

*Main> eat 1 foldr() 

<interactive>:6:1: 
    Ambiguous type variable `t0' in the constraint: 
     (Eat t0) arising from a use of `eat' 
    Probable fix: add a type signature that fixes these type variable(s) 
    In the expression: eat 1 foldr() 
    In an equation for `it': it = eat 1 foldr() 
*Main> eat 1 foldr() :: Integer 
3 

Esto es porque eat 1 foldr() podría ser Integer, pero también podría ser otra función, del mismo modo que usamos eat 1 y eat 1 foldr como funciones en la misma expresión. Nuevamente, obtenemos tipeo flexible, pero tenemos que especificar explícitamente los tipos que queremos a cambio.

Piense clase de tipos sobrecarga, como los literales numéricos sobrecargados (42 puede ser cualquier tipo que es una instancia de Num).

+0

No hay infinitos tipos en haskell está bien. Quería saber si hay un problema aquí que ** ** técnicamente ** no usa infinitos tipos pero aún funciona – Karan

+0

Expandí mi respuesta justo cuando publicaste ese comentario; Espero que ayude a responder esa pregunta adicional :) – ehird

+0

Entonces, comer siempre devuelve 3, y come cualquier tipo (a -> (b-> .. Entero)) – Karan

Cuestiones relacionadas