Condição de cobertura liberal introduzida no GHC 7.7 quebra código válido no GHC 7.6
A ideia
Estou escrevendo umDSL, que é compilado para Haskell.
Os usuários dessa linguagem podem definir estruturas de dados imutáveis próprias e funções associadas. Por função associada, quero dizer uma função, que pertence a uma estrutura de dados. Por exemplo, o usuário pode escrever (no pseudocódigo "pitônico"):
data Vector a:
x,y,z :: a
def method1(self, x):
return x
(que é equivalente ao código a seguir, mas mostra também que as funções associadas se comportam como classes de tipo com suposição de mundo aberto):
data Vector a:
x,y,z :: a
def Vector.method1(self, x):
return x
Neste exemplo,method1
é uma função associada aVector
tipo de dados e pode ser usado comov.testid(5)
(Ondev
é instância deVector
tipo de dados).
Estou traduzindo esse código para o código Haskell, mas estou enfrentando um problema que estou tentando resolver por um longo tempo.
O problema
Estou tentando mover o código do GHC 7.6GHC 7.7 (que é o pré-lançamento do 7.8) (Versões mais recentes podem ser compiladasde fontes) O código funciona perfeitamente no GHC 7.6, mas não no GHC 7.7. Quero perguntar como posso corrigi-lo para que ele funcione na nova versão do compilador?
Código de exemplo
Vamos ver uma versão simplificada do código Haskell gerado (pelo meu compilador):
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
import Data.Tuple.OneTuple
------------------------------
-- data types
------------------------------
data Vector a = Vector {x :: a, y :: a, z :: a} deriving (Show)
-- the Vector_testid is used as wrapper over a function "testid".
newtype Vector_testid a = Vector_testid a
------------------------------
-- sample function, which is associated to data type Vector
------------------------------
testid (v :: Vector a) x = x
------------------------------
-- problematic function (described later)
------------------------------
testx x = call (method1 x) $ OneTuple "test"
------------------------------
-- type classes
------------------------------
-- type class used to access "method1" associated function
class Method1 cls m func | cls -> m, cls -> func where
method1 :: cls -> m func
-- simplified version of type class used to "evaluate" functions based on
-- their input. For example: passing empty tuple as first argument of `call`
-- indicates evaluating function with default arguments (in this example
-- the mechanism of getting default arguments is not available)
class Call a b where
call :: a -> b
------------------------------
-- type classes instances
------------------------------
instance (out ~ (t1->t1)) => Method1 (Vector a) Vector_testid out where
method1 = (Vector_testid . testid)
instance (base ~ (OneTuple t1 -> t2)) => Call (Vector_testid base) (OneTuple t1 -> t2) where
call (Vector_testid val) = val
------------------------------
-- example usage
------------------------------
main = do
let v = Vector (1::Int) (2::Int) (3::Int)
-- following lines equals to a pseudocode of ` v.method1 "test" `
-- OneTuple is used to indicate, that we are passing single element.
-- In case of more or less elements, ordinary tuples would be used.
print $ call (method1 v) $ OneTuple "test"
print $ testx v
O código compila e funciona bem com o GHC 7.6. Quando estou tentando compilá-lo com o GHC 7.7, estou recebendo o seguinte erro:
debug.hs:61:10:
Illegal instance declaration for
‛Method1 (Vector a) Vector_testid out’
The liberal coverage condition fails in class ‛Method1’
for functional dependency: ‛cls -> func’
Reason: lhs type ‛Vector a’ does not determine rhs type ‛out’
In the instance declaration for
‛Method1 (Vector a) Vector_testid out’
O erro é causado por novas regras de verificação do que dependências funcionais podem fazer, a saberliberal coverage condition
(tanto quanto eu sei, isso écoverage condition
relaxado usando-XUndecidableInstances
)
Algumas tentativas para corrigir o problema
Eu estava tentando superar esse problema alterando a definição deMethod1
para:
class Method1 cls m func | cls -> m where
method1 :: cls -> m func
O que resolve o problema com dependências funcionais, mas depois a linha:
testx x = call (method1 x) $ OneTuple "test"
não é mais permitido, causando um erro de compilação (nas versões 7.6 e 7.7):
Could not deduce (Method1 cls m func0)
arising from the ambiguity check for ‛testx’
from the context (Method1 cls m func,
Call (m func) (OneTuple [Char] -> s))
bound by the inferred type for ‛testx’:
(Method1 cls m func, Call (m func) (OneTuple [Char] -> s)) =>
cls -> s
at debug.hs:50:1-44
The type variable ‛func0’ is ambiguous
When checking that ‛testx’
has the inferred type ‛forall cls (m :: * -> *) func s.
(Method1 cls m func, Call (m func) (OneTuple [Char] -> s)) =>
cls -> s’
Probable cause: the inferred type is ambiguous
EDITAR:
Também é impossível resolver esse problema usando famílias de tipos (tanto quanto eu sei). Se substituirmosMethod1
digite classe e instâncias com o seguinte código (ou semelhante):
class Method1 cls m | ,cls -> m where
type Func cls
method1 :: cls -> m (Func cls)
instance Method1 (Vector a) Vector_testid where
type Func (Vector a) = (t1->t1)
method1 = (Vector_testid . testid)
Teríamos erro óbvioNot in scope: type variable ‛t1’
, porque as famílias de tipos não permitem usar tipos, o que não aparece no LHS da expressão de tipo.
A pergunta final
Como posso fazer essa ideia funcionar no GHC 7.7? Eu conheço o novoliberal coverage condition
permite que os desenvolvedores do GHC façam algum progresso com a verificação de tipo, mas de alguma forma deve ser possível portar a idéia que trabalha no GHC 7.6 em uma versão nunca compilada.
(sem forçar o usuário da minha DSL a introduzir outros tipos - tudo até agora, como instâncias de classe de tipo, estou gerando usando o Template Haskell)