tipos tipograficas tipografia significado patines letras humanista familias ejemplos descargar decorativa dafont con haskell

haskell - tipograficas - ¿Puedo restringir una familia de tipos?



tipos de letras (4)

Ciertamente, lo más obvio sería simplemente escribir la restricción deseada directamente:

class (Differentiable (D f)) => Differentiable (f :: * -> *) where

Por desgracia, GHC se pone malhumorado y se niega a seguir el juego:

ConstrainTF.hs:17:1: Cycle in class declaration (via superclasses): Differentiable -> Differentiable In the class declaration for `Differentiable''

Entonces, como suele ser el caso cuando intentamos describir las restricciones de tipo lo suficientemente imaginativas como para dejar a GHC recalcitrante, debemos recurrir a algún tipo de engaño.

Recordando algunas características relevantes de GHC donde está involucrado el tipo de hackeo:

  • Es paranoico sobre la no determinación del nivel de tipo muy desproporcionada a las molestias reales que conlleva para el usuario.
  • Se comprometerá alegremente a tomar decisiones sobre clases e instancias antes de considerar toda la información disponible.
  • Obedientemente intentará verificar todo lo que haya engañado para comprometerse.

Estos son los principios tortuosos que subyacen a las viejas instancias faux-genéricas familiares, donde los tipos se unifican post-hoc con (~) para mejorar el comportamiento de inferencia de tipo de ciertos tipos de construcciones de hackeo.

En este caso, sin embargo, en lugar de pasar furtivamente la información del tipo más allá de GHC, tendríamos que evitar de alguna manera que GHC note una restricción de clase , que es un tipo completamente diferente de ... heeeey, waaaitaminute ....

import GHC.Prim type family DiffConstraint (f :: * -> *) :: Constraint type instance DiffConstraint f = Differentiable f class (DiffConstraint (D f)) => Differentiable (f :: * -> *) where type D f :: * -> *

¡Alce por su propio petardo!

Es el verdadero negocio, también. Estos son aceptados, como esperarías:

instance Differentiable (K a) where type D (K a) = K Void instance Differentiable I where type D I = K ()

Pero si le ofrecemos algunas tonterías en su lugar:

instance Differentiable I where type D I = []

GHC nos presenta precisamente el mensaje de error que nos gustaría ver:

ConstrainTF.hs:29:10: No instance for (Differentiable []) arising from the superclasses of an instance declaration Possible fix: add an instance declaration for (Differentiable []) In the instance declaration for `Differentiable I''

Hay un pequeño inconveniente, por supuesto, que es esto:

instance (Differentiable f, Differentiable g) => Differentiable (f :+: g) where type D (f :+: g) = D f :+: D g

... resulta menos que bien fundado, ya que le hemos dicho a GHC que verifique que, siempre que (f :+: g) sea Differentiable , también lo es (D f :+: D g) , que no termina bien (o en absoluto).

La forma más fácil de evitar esto sería, por lo general, repetir un montón de casos base explícitos, pero aquí GHC parece tener la intención de divergir cada vez que aparece una restricción Differentiable en un contexto de instancia. Asumiría que está innecesariamente ansioso por controlar las limitaciones de la instancia de alguna manera, y tal vez podría distraerse lo suficiente con otra capa de engaños, pero no estoy seguro de por dónde empezar inmediatamente y he agotado mi capacidad para el hackeo de esta noche después de la medianoche.

Un poco de discusión IRC sobre #haskell logró trotar ligeramente mi memoria sobre cómo GHC maneja las restricciones de contexto de clase, y parece que podemos arreglar un poco las cosas con una familia de restricciones más exigente. Usando esto:

type family DiffConstraint (f :: * -> *) :: Constraint type instance DiffConstraint (K a) = Differentiable (K a) type instance DiffConstraint I = Differentiable I type instance DiffConstraint (f :+: g) = (Differentiable f, Differentiable g)

Ahora tenemos una recursión mucho más correcta para las sumas:

instance (Differentiable (D f), Differentiable (D g)) => Differentiable (f :+: g) where type D (f :+: g) = D f :+: D g

Sin embargo, el caso recursivo no se puede dividir fácilmente para productos, y la aplicación de los mismos cambios mejoró solo en la medida en que recibí un desbordamiento de pila de reducción de contexto en lugar de simplemente colgar en un bucle infinito.

En esta reciente respuesta mía , casualmente abrí este viejo castaño (un programa tan antiguo, la mitad de él fue escrito en el siglo diecisiete por Leibniz y escrito en una computadora en los años setenta por mi padre). Dejaré fuera el bit moderno para ahorrar espacio.

class Differentiable f where type D f :: * -> * newtype K a x = K a newtype I x = I x data (f :+: g) x = L (f x) | R (g x) data (f :*: g) x = f x :&: g x instance Differentiable (K a) where type D (K a) = K Void instance Differentiable I where type D I = K () instance (Differentiable f, Differentiable g) => Differentiable (f :+: g) where type D (f :+: g) = D f :+: D g instance (Differentiable f, Differentiable g) => Differentiable (f :*: g) where type D (f :*: g) = (D f :*: g) :+: (f :*: D g)

Ahora, aquí está lo frustrante. No sé cómo estipular que D f debe ser en sí mismo diferenciable. Ciertamente, estas instancias respetan esa propiedad, y podría haber programas divertidos que puedas escribir y que hagan uso de la capacidad de seguir diferenciando un functor, disparando agujeros en más y más lugares: expansiones de Taylor, ese tipo de cosas.

Me gustaría poder decir algo como

class Differentiable f where type D f instance Differentiable (D f)

y requieren un control de que las declaraciones de instancia tienen definiciones de type para las cuales existen las instancias necesarias.

Tal vez cosas más mundanas como

class SortContainer c where type WhatsIn c instance Ord (WhatsIn c) ...

también sería bueno Eso, por supuesto, tiene la solución fundep

class Ord w => SortContainer c w | c -> w where ...

pero intentar el mismo truco para Differentiable parece ... bueno ... involucionado.

Entonces, ¿hay alguna solución ingeniosa que me permita una diferenciabilidad repetible? (Creo que podría construir una representación GADT yy ... pero ¿hay alguna manera de que funcione con las clases?)

¿Y hay algún inconveniente obvio con la sugerencia de que deberíamos poder exigir restricciones en las familias de tipo (y, supongo, de datos) cuando las declaramos, y luego verificar que las instancias las satisfagan?


Con las nuevas UndecidableSuperclasses en GHC 8

class Differentiable (D f) => Differentiable (f :: Type -> Type) where

trabajos.


Esto se puede lograr de la misma manera que sugiere Edward con una pequeña implementación de Dict . Primero, eliminemos las extensiones e importaciones de idiomas.

{-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE RankNTypes #-} import Data.Proxy

TypeOperators es solo para su problema de ejemplo.

Tiny Dict

Podemos hacer nuestra propia implementación pequeña de Dict . Dict usa un GADT y ConstraintKinds para capturar cualquier restricción en el constructor para un GADT.

data Dict c where Dict :: c => Dict c

withDict y withDict2 introducir la restricción mediante la coincidencia de patrones en GADT. Solo necesitamos poder razonar sobre los términos con una o dos fuentes de restricciones.

withDict :: Dict c -> (c => x) -> x withDict Dict x = x withDict2 :: Dict a -> Dict b -> ((a, b) => x) -> x withDict2 Dict Dict x = x

Tipos infinitamente diferenciables

Ahora podemos hablar de tipos infinitamente diferenciables, cuyas derivadas también deben ser diferenciables

class Differentiable f where type D f :: * -> * d2 :: p f -> Dict (Differentiable (D f)) -- This is just something to recover from the dictionary make :: a -> f a

d2 toma un proxy para el tipo y recupera el diccionario para tomar la segunda derivada. El proxy nos permite especificar fácilmente de qué tipo de d2 estamos hablando. Podemos llegar a diccionarios más profundos aplicando d :

d :: Dict (Differentiable t) -> Dict (Differentiable (D t)) d d1 = withDict d1 (d2 (pt (d1))) where pt :: Dict (Differentiable t) -> Proxy t pt = const Proxy

Capturando el dictonario

Los tipos de funcionador polinomial, productos, sumas, constantes y cero son infinitamente diferenciables. Definiremos los testigos d2 para cada uno de estos tipos

data K x = K deriving (Show) newtype I x = I x deriving (Show) data (f :+: g) x = L (f x) | R (g x) deriving (Show) data (f :*: g) x = f x :&: g x deriving (Show)

El cero y las constantes no requieren ningún conocimiento adicional para capturar el Dict sus derivados

instance Differentiable K where type D K = K make = const K d2 = const Dict instance Differentiable I where type D I = K make = I d2 = const Dict

Tanto la suma como el producto requieren que los diccionarios de los derivados de ambos componentes puedan deducir el diccionario para su derivada.

instance (Differentiable f, Differentiable g) => Differentiable (f :+: g) where type D (f :+: g) = D f :+: D g make = R . make d2 p = withDict2 df dg $ Dict where df = d2 . pf $ p dg = d2 . pg $ p pf :: p (f :+: g) -> Proxy f pf = const Proxy pg :: p (f :+: g) -> Proxy g pg = const Proxy instance (Differentiable f, Differentiable g) => Differentiable (f :*: g) where type D (f :*: g) = (D f :*: g) :+: (f :*: D g) make x = make x :&: make x d2 p = withDict2 df dg $ Dict where df = d2 . pf $ p dg = d2 . pg $ p pf :: p (f :*: g) -> Proxy f pf = const Proxy pg :: p (f :*: g) -> Proxy g pg = const Proxy

Recuperando el diccionario

Podemos recuperar el diccionario de restricciones que de otra manera no tendríamos la información adecuada para deducir. Differentiable f normalmente solo permitiría que el uso llegue a make :: a -> fa , pero no para make :: a -> D fa o make :: a -> D (D f) a .

make1 :: Differentiable f => p f -> a -> D f a make1 p = withDict (d2 p) make make2 :: Differentiable f => p f -> a -> D (D f) a make2 p = withDict (d (d2 p)) make


Su mejor opción podría ser definir algo utilizando el paquete de constraints :

import Data.Constraint class Differentiable (f :: * -> *) where type D f :: * -> * witness :: p f -> Dict (Differentiable (D f))

luego puede abrir manualmente el diccionario siempre que necesite recurse.

Esto le permitiría emplear la forma general de la solución en la respuesta de Casey, pero no hacer que el compilador (o tiempo de ejecución) gire para siempre en la inducción.