¿Cómo puedo derivar typeclass casos de restricción de las familias que están en el ámbito?

0

Pregunta

edit: he seguido con una más pregunta específica. Gracias ms responden aquí, y creo que el seguimiento de la pregunta hace un mejor trabajo de explicar un poco de confusión he introducido aquí.


TL;DR estoy luchando para conseguir pruebas de restricciones en las expresiones, mientras que el uso de GADTs con existencial restricciones en la clasificación de constructores. (eso es un grave bocado, lo siento!)


He destilado un problema a la siguiente. Tengo una simple GADT que representa los puntos de llamada X y la función de las aplicaciones de llamada F. Los puntos X están obligados a ser Objects.

data GADT ix a where
  X :: Object ix a => a -> GADT ix a
  F :: (a -> b) -> GADT ix a -> GADT ix b

Constrained se refiere a los contenedores cuyos objetos están restringidos por algo y Object es que algo. (edit: mi problema real consiste en Category y Cartesian clases de limitado de categorías)

-- | I can constrain the values within containers of kind `* -> *`
class Constrained (ix :: * -> *) where
  type Object ix a :: Constraint

-- | Here's a trivial constraint. A more interesting one might include `Typeable a`, for ex
instance Constrained (GADT ix) where
  type Object (GADT ix) a = (Constrained ix, Object ix a)

Me gustaría escribir una expresión:

-- error: Could not deduce: Object ix Int arising from a use of ‘X’
ex0 :: GADT ix String
ex0 = F show (X (3 :: Int))

Y mientras que la solución obvia funciona, se vuelve rápidamente detallado cuando la construcción de grandes expresiones:

-- Typechecks, but eventually verbose
ex1 :: Object ix Int => GADT ix String
ex1 = F show (X (3 :: Int))

Creo que la solución correcta debería ser algo como esto:

-- error: Could not deduce: Object ix Int arising from a use of ‘X’
ex2 :: Constrained ix => GADT ix String
ex2 = F show (X (3 :: Int))

Pero todavía no puedo conseguir que la prueba de Object ix Int.

Estoy seguro de que es más sencillo de lo que yo estoy pensando. He intentado añadir restricciones a la Object restricción de la familia en el GADT instancia de la clase. He tratado de ofrecer las limitaciones en la expresión de la firma. He intentado QuantifiedConstraintsaunque , no estoy seguro de comprender plenamente todavía. Por favor ayuda me sabios!


Ejecutables:

{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE InstanceSigs #-}

module Test where

import Data.Kind
import Data.Functor.Identity
import Data.Functor.Const

-- | I can constrain the values within containers of kind `* -> *`
class Constrained (ix :: * -> *) where
  type Object ix a :: Constraint

-- | Here's a trivial constraint. A more interesting one might include `Typeable a`, for instance
instance Constrained (GADT ix) where
  type Object (GADT ix) a = (Constrained ix, Object ix a)

-- | A demo GADT that has function application ('F'), and points ('X'). The
-- points are constrained.
data GADT ix a where
  X :: Object ix a => a -> GADT ix a
  F :: (a -> b) -> GADT ix a -> GADT ix b

-- -- Broken
-- -- error: Could not deduce: Object ix Int arising from a use of ‘X’
-- ex0 :: GADT ix String
-- ex0 = F show (X (3 :: Int))

-- Typechecks
-- but for larger programs becomes verbose, requiring many explicit constraints
ex1 :: Object ix Int => GADT ix String
ex1 = F show (X (3 :: Int))

-- -- What I want, but, it's broken
-- ex2 :: Constrained ix => GADT ix String
-- ex2 = F show (X (3 :: Int))
2

Mejor respuesta

1

Sin más contexto, es difícil decir cuál es la mejor solución, pero aquí hay un par de posibilidades:

Impedir que se limite a todos

Tal y como está, su GADT no parece tener mucha razón para restringir X a Object. Quizá este no sea necesario?

data GADT ix a where
  X :: a -> GADT ix a
  F :: (a -> b) -> GADT ix a -> GADT ix b

En su lugar, la restricción podría venir desde el exterior cuando es necesario.

Morder la bala de restricción de las listas, sino que los hacen más agradable

Si usted tiene muchos tipos diferentes en su expresión que todos tenemos que cumplir con la misma restricción, usted puede usar un ayudante como All

ex2' :: All (Object ix) '[Int] => GADT ix String
ex2' = F show (X (3 :: Int))

donde puede haber más tipos en la lista además de Int; y/o usted puede hacer sinónimo restricciones tales como

type StdObjs ix = (Object ix Int, Object x Bool, ...)

ex2'' :: StdObjs ix => GADT ix String
ex2'' = F show (X (3 :: Int))

Propagar las restricciones hacia atrás a través de la estructura de datos de la misma

Si usted necesita la restricción en la X valores, sin embargo, puede ser posible expresar esto de otra manera en el GADT. Por ejemplo, si la función no es una función general, sino algo que ya está restringido sólo para aceptar Objects, usted podría tener algo como esto:

data YourFunc ix a b where
  YourFunc :: Object ix a => (a->b) -> YourFunc ix a b

show' :: Object ix Int => YourFunc ix Int String
show' = YourFunc show

Esto no ayudan directamente con el problema que preguntaban, pero tal vez la función es compartida o algo. Usted podría incluso tener algo como

class Object ix a => InferrenceChain ix a where
  type PreElem ix a :: Type
  propInferrence :: (InferrenceChain ix (PreElem a) => r) -> r

y, a continuación,

data YourFunc ix a b where
  YourFunc :: InferrenceChain ix a
                 => (PreElem a -> a) -> YourFunc (PreElem a) a

Luego en la final se podía prueba de la X restricción de sólo poner en Object ix String en el exterior y recursing más propInferrence. Pero esto sería, probablemente, llegar a ser muy engorroso.

2021-11-23 18:30:17

Le he pedido a una pregunta de seguimiento. Wrt eliding limitaciones, el seguimiento de la muestra por qué la necesito. Wrt una lista de restricciones, todavía creo que el repetitivo sería insufferably grande. Wrt YourFunc, que había introducir una tonelada de hasta el frente de la caldera de la placa (un nuevo preludio), aunque, probablemente eliminar futuro repetitivo. Wrt InferrenceChain, Estoy luchando por mapa sobre mi problema, pero tal vez el seguimiento ayuda a explicar mejor? Gracias por cierto!
Josh.F

woh, me di cuenta de que eres el autor de la biblioteca estoy jugando con el, constrained-categoriesgracias por la biblioteca, es increíble!
Josh.F

Bueno, me alegra escuchar que les sea útil!
leftaroundabout
1

Creo que la solución correcta debería ser algo como esto:

-- error: Could not deduce: Object ix Int >arising from a use of ‘X’
ex2 :: Constrained ix => GADT ix String
ex2 = F show (X 3)

Desafortunadamente, esta solución no tiene ningún sentido. El compilador se justifica señalando que no sabe que Object ix Int está satisfecho en este punto, ya que lo único que sabe es que Constrained ix puede imponer algunas restricciones a través de Object ix Int.

Una solución a través de la cuantificación

Así que tal vez lo que desea es una restricción que dice: "en este punto, todos los Object ix a restricciones de uso son satisfechos" - que podemos hacer a través de la cuantificación:

{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ConstraintKinds #-}

type Satisfied ix = forall a. Object ix a

ex2 :: Satisfied ix => GADT ix String
ex2 = F show (X 3)

Por desgracia, lo que nos da una GHC error:

• Quantified predicate must have a class or type variable head:
        forall a. Object ix a
• In the quantified constraint ‘forall a. Object ix a’
  In the type synonym declaration for ‘Satisfied’

Desde Object es un tipo de la familia y no de una clase o tipo de variable.

Re-arquitectura

Pero... ¿por qué Object un tipo de familia? De hecho, ¿por qué Constrained existe como un lawless clase con métodos no? Si queremos ejercer restricciones en las combinaciones de los recipientes y de tipos de Haskell ya nos da los medios para hacerlo - sólo tiene que utilizar instancia limitaciones!

{-# LANGUAGE MultiParamTypeClasses #-}

class Object ix a

type Constrained ix = forall a. Object ix a

Porque si tenemos

instance (...<some stuff>...) => Constrained Foo where
  type Object ix a = (...<some def>...)

podríamos traducir a

instance (...<some stuff>..., ...<some def>...) 
  => Object ix a

Lo que hace este ejemplo de compilación.

ex2 :: Constrained ix => GADT ix String
ex2 :: F show (X 3)
2021-11-23 10:52:50

Esto tiene sentido. Por desgracia, lo he simplificado para Constraineden mi verdadero problema, es en realidad Category y Cartesian de cartesian-categoriesque pasara con los métodos. No sé de una manera, otros de TypeFamilies (es decir, una restricción de la familia, aquí) para expresar la idea de una clase cuyos objetos son arbitrariamente restringida de los subtipos de Hask, así que, no creo que el rearchitecture de trabajo para este problema en particular.
Josh.F

¿Te refieres a los categories la biblioteca? Creo que usted necesita para proporcionar una más motivador ejemplo de por qué el tratamiento de la Object como una clase no funciona como un enfoque, porque no es obvio para mí a partir de la observación de esas clases.
Isaac van Bakel

disparar, así que lo siento, es este! hackage.haskell.org/package/constrained-categories-0.4.1.0
Josh.F

en realidad, aquí hay un link directo a la clase: hackage.haskell.org/package/constrained-categories-0.4.1.0/docs/...
Josh.F

Sus ejemplos muy mucho ir por el mismo camino que recorrí. Wrt su idea rearchitecture, me he pedido un seguimiento de la cuestión que muestra por qué yo no puedo, a saber, la Object es un typefamily que restringe la co/dominio y es un requisito de otra biblioteca.
Josh.F

En otros idiomas

Esta página está en otros idiomas

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Slovenský
..................................................................................................................