world tutorial simbolos online hello descargar constructora company haskell

tutorial - haskell simbolos



Cómo escribo, "si typeclass a, entonces a también es una instancia de b según esta definición". (6)

(Editar: dejando el cuerpo para la posteridad, pero salta hasta el final para encontrar la solución real)

En la instance MyClass a => Show a declaración instance MyClass a => Show a , examinemos el error "Restricción no es menor que el encabezado de instancia". La restricción es la restricción de clase de tipo a la izquierda de ''=>'', en este caso MyClass a . El "encabezado de instancia" es todo después de la clase para la que está escribiendo una instancia, en este caso a (a la derecha de Show ). Una de las reglas de inferencia de tipo en GHC requiere que la restricción tenga menos constructores y variables que el encabezado. Esto es parte de lo que se llama las " Condiciones de Paterson ". Estos existen como una garantía de que la verificación de tipos termina.

En este caso, la restricción es exactamente la misma que la cabeza, es decir, a , por lo que no pasa esta prueba. Puede eliminar las comprobaciones de condición de Paterson habilitando las UndecidableInstances , muy probablemente con el {-# LANGUAGE UndecidableInstances #-} pragma.

En este caso, esencialmente está utilizando su clase MyClass como sinónimo de clase de clase para la clase Show . Crear sinónimos de clase como este es uno de los usos canónicos de la extensión UndecidableInstances, por lo que puede usarlo de forma segura aquí.

''Indecidible'' significa que GHC no puede probar que la verificación de tipo terminará. Aunque parezca peligroso, lo peor que puede pasar al habilitar las UndecidableInstances es que el compilador realizará un bucle, que finalmente terminará después de agotar la pila. Si se compila, entonces obviamente la verificación de tipo terminó, por lo que no hay problemas. La extensión peligrosa es IncoherentInstances, que es tan mala como suena.

Editar: otro problema hecho posible por este enfoque surge de esta situación:

instance MyClass a => Show a where data MyFoo = MyFoo ... deriving (Show) instance MyClass MyFoo where

Ahora hay dos instancias de Show for MyFoo , la de la cláusula derivada y la de las instancias de MyClass. El compilador no puede decidir cuál usar, por lo que rescatará con un mensaje de error. Si intentas hacer que las instancias de MyClass de tipos que no controlas tengan instancias de Show , tendrás que usar newtypes para ocultar las instancias Show existentes. Incluso los tipos sin instancias MyClass seguirán en conflicto porque la instance MyClass => Show a definición instance MyClass => Show a porque la definición realmente proporciona una implementación para todos los posibles a (la verificación de contexto aparece más adelante, no está involucrada con la selección de instancia)

Así que ese es el mensaje de error y cómo UndecidableInstances lo hace desaparecer. Desafortunadamente, es un gran problema usarlo en el código real, por razones que Edward Kmett explica. El ímpetu original fue evitar especificar una restricción Show cuando ya hay una restricción MyClass . Dado que, lo que haría es usar myShow de MyClass lugar de show . No necesitarás la restricción Show en absoluto.

Tengo una clase de tipo MyClass , y hay una función en ella que produce una String . Quiero usar esto para dar a entender una instancia de Show , para poder pasar los tipos que implementan MyClass para show . Hasta ahora tengo,

class MyClass a where someFunc :: a -> a myShow :: a -> String instance MyClass a => Show a where show a = myShow a

que da el error Constraint is no smaller than the instance head. También intenté,

class MyClass a where someFunc :: a -> a myShow :: a -> String instance Show (MyClass a) where show a = myShow a

que da el error, Class MyClass ''usado como un tipo''.

¿Cómo puedo expresar correctamente esta relación en Haskell? Gracias.

Debo añadir que deseo hacer un seguimiento de esto con instancias específicas de MyClass que emiten cadenas específicas en función de su tipo. Por ejemplo,

data Foo = Foo data Bar = Bar instance MyClass Foo where myShow a = "foo" instance MyClass Bar where myShow a = "bar" main = do print Foo print Bar


Como señaló Ed Kmett, esto no es posible en absoluto para su caso. Sin embargo, si tiene acceso a la clase para la que desea proporcionar una instancia predeterminada, puede reducir la repetición mínima con una implementación predeterminada y restringir el tipo de entrada con la firma predeterminada que necesita:

{-# LANGUAGE DefaultSignatures #-} class MyClass a where someFunc :: a -> Int class MyShow a where myShow :: a -> String default myShow :: MyClass a => a -> String myShow = show . someFunc instance MyClass Int where someFunc i = i instance MyShow Int main = putStrLn (myShow 5)

Tenga en cuenta que la única repetición real (bueno, aparte de todo el ejemplo) se reduce a la instance MyShow Int .

Vea a aeson s ToJSON para un ejemplo más realista.


Creo que sería mejor hacerlo al revés:

class Show a => MyClass a where someFunc :: a -> a myShow :: MyClass a => a -> String myShow = show


Deseo discrepar enérgicamente con las soluciones rotas planteadas hasta ahora.

instance MyClass a => Show a where show a = myShow a

Debido a la forma en que funciona la resolución de instancia, ¡esta es una instancia muy peligrosa para correr!

La resolución de la instancia avanza de manera efectiva con la concordancia de patrones en el lado derecho de cada instancia => , completamente sin tener en cuenta lo que está a la izquierda de => .

Cuando ninguna de esas instancias se superpone, esto es algo hermoso. Sin embargo, lo que está diciendo aquí es "Aquí hay una regla que debe usar para TODAS las instancias de Show. Cuando se le pida una instancia de show para cualquier tipo, necesitará una instancia de MyClass, así que consiga eso, y aquí está la implementación " - una vez que el compilador se ha comprometido a elegir usar su instancia, (solo en virtud del hecho de que ''a'' se unifica con todo) ¡no tiene ninguna posibilidad de retroceder y usar cualquier otra instancia!

Si activa {-# LANGUAGE OverlappingInstances, IncoherentInstances #-} , etc. para hacerlo compilar, obtendrá fallas no tan sutiles cuando vaya a escribir módulos que importen el módulo que proporciona esta definición y necesita usar cualquier otra instancia de Show. En última instancia, podrás obtener este código para compilar con suficientes extensiones, ¡pero lamentablemente no hará lo que crees que debería hacer!

Si lo piensas dado:

instance MyClass a => Show a where show = myShow instance HisClass a => Show a where show = hisShow

¿Cuál debería elegir el compilador?

Su módulo solo puede definir uno de estos, pero el código de usuario final importará un grupo de módulos, no solo el suyo. Además, si otro módulo define

instance Show HisDataTypeThatHasNeverHeardOfMyClass

el compilador estaría en su derecho de ignorar su instancia e intentar usar la suya.

La respuesta correcta, por desgracia, es hacer dos cosas.

Para cada instancia individual de MyClass, puede definir una instancia correspondiente de Show con la definición muy mecánica

instance MyClass Foo where ... instance Show Foo where show = myShow

Esto es bastante desafortunado, pero funciona bien cuando hay pocas instancias de MyClass en consideración.

Cuando tiene una gran cantidad de instancias, la forma de evitar la duplicación de código (para cuando la clase es considerablemente más complicada que mostrar) es definir.

newtype WrappedMyClass a = WrapMyClass { unwrapMyClass :: a } instance MyClass a => Show (WrappedMyClass a) where show (WrapMyClass a) = myShow a

Esto proporciona el nuevo tipo como un vehículo, por ejemplo, despacho. y entonces

instance Foo a => Show (WrappedFoo a) where ... instance Bar a => Show (WrappedBar a) where ...

es inequívoco, porque el tipo ''patrones'' para WrappedFoo a y WrappedBar a son disjuntos.

Hay una serie de ejemplos de este modismo corriendo en el paquete base .

En Control.Applicative hay definiciones para WrappedMonad y WrappedArrow por esta misma razón.

Idealmente, podrías decir:

instance Monad t => Applicative t where pure = return (<*>) = ap

pero efectivamente lo que esta instancia está diciendo es que cada Aplicativo debería derivarse primero encontrando una instancia para Monad, y luego enviándola a ella. Entonces, si bien tendría la intención de decir que cada Mónada es Aplicativa (por cierto, la implicación-como => lee) lo que realmente dice es que cada Aplicativo es una Mónada, porque tener una cabeza de ejemplo ''t'' concuerda con cualquier tipo. En muchos sentidos, la sintaxis para las definiciones de ''instancia'' y ''clase'' es al revés.


Puede compilarlo, pero no con Haskell 98, debe habilitar algunas extensiones de idioma:

{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE UndecidableInstances #-} -- at the top of your file

Las instancias flexibles están ahí para permitir el contexto en la declaración de instancia. Realmente no sé el significado de IndecidiblesInstancias, pero evitaría todo lo que pueda.