Comment puis-je tirer typeclass cas à partir de la contrainte de familles qui sont dans la portée?

0

La question

edit: j'ai suivi avec un plus question spécifique. Merci answerers ici, et je pense que le suivi de la question fait un meilleur travail d'expliquer une certaine confusion, j'ai présenté ici.


TL;DR je suis mal à obtenir des preuves de contraintes dans les expressions, tout en utilisant les GADTs à existentielle contraintes sur les constructeurs. (c'est un grave bouchée, désolé!)


J'ai distillé un problème à la suivante. J'ai une simple GADT qui représente points appelés X et de la fonction des applications appelées F. Les points X sont contraints d'être Objects.

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

Constrained s'agit des conteneurs dont les objets sont limités par quelque chose et Object c'est que quelque chose. (edit: mon vrai problème consiste à Category et Cartesian les classes de contrainte-catégories)

-- | 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)

Je voudrais écrire une expression:

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

Et alors que la solution la plus évidente œuvres, il devient rapidement détaillé lors de la construction de plus grandes expressions:

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

Je pense que la bonne solution devrait ressembler à quelque chose comme ceci:

-- 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))

Mais je ne peux toujours pas que la preuve de l' Object ix Int.

Je suis sûr que c'est plus simple que je pensais. J'ai essayé d'ajouter des contraintes à l' Object la contrainte de la famille dans le GADT instance de classe. J'ai essayé offrant des contraintes dans l'expression de sa signature. J'ai essayé QuantifiedConstraintsbien que , je ne suis pas sûr de comprendre parfaitement encore. Merci de m'aider sages!


Exécutable:

{-# 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

La meilleure réponse

1

Sans plus de contexte, il est difficile de dire quelle est la meilleure solution, mais ici, êtes un couple de possibilités:

Éviter de contraindre à tous

Comme il est, votre GADT ne semble pas avoir beaucoup de raisons pour restreindre X pour Object. C'est peut-être tout simplement pas nécessaires?

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

Au lieu de cela, la contrainte peut venir de l' extérieur quand c'est nécessaire.

Mordre la balle de contrainte listes, mais de les rendre plus agréable

Si vous avez beaucoup de différents types dans votre expression que tous doivent répondre à la même contrainte, vous pouvez utiliser une aide comme All

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

où il peut y avoir plusieurs types dans la liste en plus de Int; et/ou vous pouvez faire synonyme de contraintes telles que

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

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

Propager les contraintes en arrière à travers la structure de données elle-même

Si vous avez besoin de la contrainte sur la X des valeurs, il peut néanmoins être possible de l'exprimer d'une autre manière dans le GADT. Par exemple, si la fonction n'est pas une fonction générale, mais quelque chose qui est déjà contraint de l'accepter Objects, il pourrait être comme ceci:

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

Ce n'est pas directement aider avec le problème que vous posiez des questions sur, mais peut-être que la fonction est partagée ou quelque chose. Vous pourriez même avoir quelque chose comme

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

et puis

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

Puis à la fin, vous pourriez preuve de la X contrainte de juste la mise en Object ix String sur l'extérieur et recursing plus propInferrence. Mais ce serait probablement obtenir assez laborieux.

2021-11-23 18:30:17

J'ai demandé à une question de suivi. Wrt eliding contraintes, le suivi montre pourquoi j'en ai besoin. Wrt une liste des contraintes, je pense toujours que le standard allait devenir insufferably grand. Wrt YourFuncque voudrais vous présenter une tonne de à l'avant de la chaudière de la plaque (un nouveau prélude), bien que, probablement éliminer futur standard. Wrt InferrenceChain, J'ai du mal à la carte sur mon problème, mais peut-être que le suivi permet d'expliquer mieux? Merci btw!
Josh.F

woh, je viens de réaliser que vous êtes l'auteur de la bibliothèque, je me suis amusé avec, constrained-categoriesmerci pour la bibliothèque, c'est incroyable!
Josh.F

Eh bien, je suis heureux d'entendre que vous la trouverez utile!
leftaroundabout
1

Je pense que la bonne solution devrait ressembler à quelque chose comme ceci:

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

Malheureusement, cette solution n'a aucun sens. Le compilateur est justifiée en soulignant qu'il ne sait pas que Object ix Int est convaincu à ce point, puisque tous il sait que Constrained ix peut imposer certaines contrainte par Object ix Int.

Une solution grâce à la quantification

Alors peut-être ce que vous voulez, c'est une contrainte qui dit: "à ce stade, tous les Object ix a les contraintes que j'utilise sont satisfaits" - que nous pouvons essayer de le faire grâce à la quantification:

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

type Satisfied ix = forall a. Object ix a

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

Malheureusement, cela nous donne un GHC d'erreur:

• 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’

Depuis Object est un type de famille et non pas une classe ou d'un type de variable.

Re-architecture

Mais... pourquoi Object un type de la famille? En fait, pourquoi ne Constrained existent en tant que lawless classe avec pas de méthodes? Si nous voulons exercer des contraintes sur les combinaisons de conteneurs et de types, Haskell nous donne déjà les moyens de le faire - il suffit d'utiliser instance contraintes!

{-# LANGUAGE MultiParamTypeClasses #-}

class Object ix a

type Constrained ix = forall a. Object ix a

Parce que si nous avons

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

on pourrait traduire cela à

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

Ce qui rend cet exemple de compilation.

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

Cela fait sens. Malheureusement, ce que j'ai simplifié à l' Constraineddans mon vrai problème, qui est en fait Category et Cartesian à partir de cartesian-categories, qui sont légales avec des méthodes. Je ne sais pas un moyen, autre que TypeFamilies (c'est à dire une contrainte de la famille, ici) pour exprimer l'idée d'une catégorie dont les objets sont arbitrairement limité sous-types de Hask, donc, je ne pense pas que le rearchitecture de travail pour ce problème particulier.
Josh.F

Entendez-vous le categories bibliothèque? Je pense que vous avez besoin afin de fournir une plus motivant pourquoi le traitement de l' Object en tant que classe, ne fonctionne pas comme une approche, parce qu'il n'est pas évident pour moi de regarder ces classes.
Isaac van Bakel

Josh.F

en fait, voici un lien direct vers la classe: hackage.haskell.org/package/constrained-categories-0.4.1.0/docs/...
Josh.F

Vos exemples très descendre les mêmes chemins que je marchais. Wrt votre idée de rearchitecture, j'ai demandé à un suivi de la question qui montre pourquoi je ne peux pas, à savoir, la Object est un typefamily qui contraint le co/domaine et est une exigence à partir d'une autre bibliothèque.
Josh.F

Dans d'autres langues

Cette page est dans d'autres langues

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