Como posso derivar typeclass instâncias de restrição de famílias que estão no escopo?

0

Pergunta

edit: eu tenho seguido com uma pergunta específica. Obrigado respondentes aqui, e eu acho que o acompanhamento pergunta faz um trabalho melhor de explicar alguma confusão que eu apresentei aqui.


TL;DR eu estou lutando para obter provas de restrições de expressões, enquanto o uso de GADTs com existencial restrições sobre os construtores. (é um bocado grave, desculpe!)


Eu já destilada, um problema para o seguinte. Eu tenho um simples GADT que representa os pontos de chamada X função e aplicações de chamada F. Pontos X são constrangido 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 refere-se a recipientes cujos objetos são restringidos por algo e Object é que algo. (edit: o meu real problema envolve Category e Cartesian classes de restrita-categorias)

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

Eu gostaria de escrever uma expressão:

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

E enquanto a solução óbvia funciona, ele rapidamente se torna detalhado quando a construção de maiores expressões:

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

Eu acho que a solução correta deve ser algo como isto:

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

Mas eu ainda não consigo obter essa prova de Object ix Int.

Eu tenho certeza que é mais simples do que eu estou pensando. Eu tentei adicionar restrições para o Object restrição de família no GADT instância de classe. Eu tentei oferecendo restrições na expressão da assinatura. Eu tentei QuantifiedConstraintsembora, eu não tenho certeza se completamente compreendê-lo ainda. Por favor, ajude-me sábios!


Runnable:

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

Melhor resposta

1

Sem mais contexto, é difícil dizer qual é a melhor solução, mas aqui estão algumas possibilidades:

Evitar constrangimento em todos os

Como está, o seu GADT não parece ter muita razão para restringir o X para Object. Talvez isso não é necessário?

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

Em vez disso, a restrição pode vir do lado de fora quando necessário.

Morder a bala de restrição de listas, mas torná-los mais agradáveis

Se você tem muitos tipos diferentes em sua expressão de que todos precisamos para cumprir a mesma restrição, você pode usar um auxiliar como All

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

onde pode haver mais tipos na lista, além de Inte/ou ; você pode fazer sinônimo restrições tais como

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

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

Propagar as restrições para trás através da estrutura de dados em si

Se você precisa fazer a restrição na X os valores, no entanto, pode ser possível expressar isso de outra forma no GADT. Por exemplo, se a função é não uma função geral, mas algo que já está restrita a apenas aceitar Objects, você poderia tê-lo assim:

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

Isso não ajudar diretamente com o problema que você estava perguntando sobre, mas talvez a função é compartilhada, ou algo assim. Você poderia até mesmo ter algo como

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

e, em seguida,

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

Em seguida, no final você pode prova de que o X restrição de só colocar em Object ix String do lado de fora e recursing mais propInferrence. Mas isso provavelmente iria ficar muito complicadas.

2021-11-23 18:30:17

Eu pedi um acompanhamento pergunta. Wrt eliding restrições, o acompanhamento mostra o porquê de eu precisar deles. Wrt uma restrição, eu ainda acho que o clichê iria se tornar insuportavelmente grande. Wrt YourFunc, que iria introduzir uma tonelada de frente caldeiras (um novo prelúdio), embora, provavelmente, eliminar a futura clichê. Wrt InferrenceChain, Eu estou lutando para mapear para o meu problema, mas, talvez, o acompanhamento ajuda a explicar melhor? Obrigado btw!
Josh.F

woh, eu só percebi que você é o autor da biblioteca, eu estou brincando com, constrained-categories, obrigado pela biblioteca, ela é incrível!
Josh.F

Bem, eu estou contente de ouvir que seja útil!
leftaroundabout
1

Eu acho que a solução correta deve ser algo como isto:

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

Infelizmente, esta solução não faz qualquer sentido. O compilador é justificado em dizer que ele não sabe que Object ix Int está satisfeito neste ponto, uma vez que tudo o que ele sabe é que Constrained ix pode impor alguma restrição através de Object ix Int.

Uma solução através de quantificação

Então, talvez o que você quer é uma restrição que diz: "neste ponto, todos os Object ix a restrições de uso são satisfeitos" - que podemos tentar e fazer através de quantificação:

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

type Satisfied ix = forall a. Object ix a

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

Infelizmente, o que nos dá uma GHC erro:

• 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 é um tipo de família, e não uma classe ou tipo de variável.

Re-arquitetura

Mas... por que é Object um tipo de família? Na verdade, por que Constrained existe como um lawless classe com métodos não? Se queremos exercer restrições em combinações de contentores e de tipos de Haskell nos dá os meios para fazer isso basta usar instância restrições!

{-# LANGUAGE MultiParamTypeClasses #-}

class Object ix a

type Constrained ix = forall a. Object ix a

Porque se nós temos

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

podemos traduzir isso para

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

O que torna este exemplo de compilação.

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

Isso faz sentido. Infelizmente, o que eu tenho simplificado para Constrainedno meu real problema, é, na verdade, Category e Cartesian a partir de cartesian-categories, que são legais com métodos. Eu não sei uma maneira, outros de TypeFamilies (ou seja, uma restrição de família, aqui) para expressar a idéia de uma classe cujos objetos são arbitrariamente restrita subtipos de Hask, então, eu não acho que o rearchitecture vai trabalhar para este problema em particular.
Josh.F

Você quer dizer a categories biblioteca? Eu acho que você precisa de mais motivação exemplo do por que o tratamento Object como uma classe não funciona como uma abordagem, porque não é óbvio para mim, olhando para essas classes.
Isaac van Bakel

atirar, então, desculpe, é um presente! hackage.haskell.org/package/constrained-categories-0.4.1.0
Josh.F

na verdade, aqui está um link direto para a classe: hackage.haskell.org/package/constrained-categories-0.4.1.0/docs/...
Josh.F

Seus exemplos muito desça as mesmos caminhos que eu andei. Wrt a sua ideia para rearchitecture ele, eu pedi um acompanhamento questão que mostra por que eu não posso, a saber, a Object é um typefamily que restringe o co/domínio e é um requisito de outra biblioteca.
Josh.F

Em outros idiomas

Esta página está em outros idiomas

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