2011-04-21 9 views
6

¡Tiempo de programación genérico!Uso de Typeable para aplicar parcialmente en tiempo de ejecución (cualquier tipo de hora coincide)

Si tengo una función:

f :: a1 -> a2 -> a3 -> ... -> an 

y un valor

v :: aX -- where 1 <= x < n 

Sin saber en tiempo de compilación cuál de los argumentos de f el valor v es el tipo correcto para (si los hay) ¿Puedo aplicar parcialmente f a v? (usando Typeable, Data, TH o cualquier otro truco)

Un poco más sólidamente, ¿puedo construir la función g (abajo) en tiempo de ejecución? ¡En realidad no tiene que ser polimórfico, todos mis tipos serán monomórficos!

g :: (a1 -> a2 -> a3 -> a4 -> a5) -> a3 -> (a1 -> a2 -> a4 -> a5) 
g f v = \x y z -> f x y v z 

sé que, utilizando tipable (typeRepArgs específicamente), v es la tercera argumento de f, pero eso no quiere decir que tenga una forma de aplicar parcialmente f.

Mi código, probablemente se vería como:

import Data.Typeable 

data Box = forall a. Box (TyRep, a) 

mkBox :: Typeable a => a -> Box 
mkBox = (typeOf a, a) 

g :: Box -> Box -> [Box] 
g (Box (ft,f)) (Box (vt,v)) = 
    let argNums = [n | n <- [1..nrArgs], isNthArg n vt ft] 
    in map (mkBox . magicApplyFunction f v) argNums 

isNthArg :: Int -> TyRep -> TyRep -> Bool 
isNthArg n arg func = Just arg == lookup n (zip [1..] (typeRepArgs func)) 

nrArgs :: TyRep -> Int 
nrArgs = (\x -> x - 1) . length . typeRepArgs 

¿Hay algo que se puede poner en práctica el magicApplyFunction?

EDIT: finalmente volví a jugar con esto. La función mágica aplicar es:

buildFunc :: f -> x -> Int -> g 
buildFunc f x 0 = unsafeCoerce f x 
buildFunc f x i = 
     let !res = \y -> (buildFunc (unsafeCoerce f y) x (i-1)) 
     in unsafeCoerce res 
+0

No hay nada de malo en ** g **, por lo que puedo ver, y el polimorfismo tampoco importa. Del mismo modo que puedes usar el flip cada vez que tienes una función y el segundo argumento, ya sabes. – Ingo

+0

¡Pero no tengo 'g' en tiempo de compilación! ¿Cómo puedo generarlo en tiempo de ejecución? IIRC, no puedo pasar funciones hacia adelante y hacia atrás a/from hint –

+0

Escribir la función para aplicarla a través de th no debería ser un problema - el problema es qué tipo de valor recuperas (Dynamic/Box) es, como te das cuenta, tu única opción. – sclv

Respuesta

2

No voy a escribir toda la solución aquí por ahora, pero estoy seguro que esto se puede hacer simplemente con Data.Dynamic y Typeable. La fuente de dynApply y funResultTy debe proporcionar los elementos clave:

dynApply :: Dynamic -> Dynamic -> Maybe Dynamic 
dynApply (Dynamic t1 f) (Dynamic t2 x) = 
    case funResultTy t1 t2 of 
    Just t3 -> Just (Dynamic t3 ((unsafeCoerce f) x)) 
    Nothing -> Nothing 


funResultTy :: TypeRep -> TypeRep -> Maybe TypeRep 
funResultTy trFun trArg 
    = case splitTyConApp trFun of 
     (tc, [t1,t2]) | tc == funTc && t1 == trArg -> Just t2 
     _ -> Nothing 

Para simplificar las cosas, tendría type Box = (Dynamic, [Either TypeRep Dynamic]). Este último comienza como una lista de tipos de argumentos. magicApply buscaría el primer TypeRep coincidente en el cuadro y sustituiría el valor Dynamic. Entonces podría tener un extract que dado un Box al cual todos los argumentos se han aplicado mágicamente, en realidad realiza las llamadas dynApply para producir el resultado dinámico resultante.

+0

¿No es esto simplemente comprobar dinámicamente que 'x' es el tipo correcto para el primer argumento de' f' (sin reordenar/ordenar los argumentos)? Si 'f :: Int -> Double -> Bool' y' x :: Double', entonces deseo combinar 'f' y' x' para obtener 'h :: Int -> Bool'. ¿Extrañé el punto de tu respuesta? –

+0

@TomMD eso es lo que hacen dyNapply y funResultTy. Solo copian/pegan de las definiciones de la biblioteca. * cómo * lo hacen muestra cómo extraer la lista completa de argumentos de una función, que a su vez puede usarse para implementar la solución que esbocé debajo de ese código. – sclv

+0

Ah, entiendo tu punto. Jugaré con esto un poco, ¡gracias! –

1

Hm .. Tipo solo? ¿Qué hay de las buenas superposiciones de edad?

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, TypeFamilies, 
UndecidableInstances, IncoherentInstances, ScopedTypeVariables #-} 

class Magical a b c where 
    apply :: a -> b -> c 

instance (AreEqual a c e, Magical' e (a -> b) c r) => Magical (a -> b) c r where 
    apply f a = apply' (undefined :: e) f a 


class Magical' e a b c where 
    apply' :: e -> a -> b -> c 

instance (r ~ b) => Magical' True (a -> b) a r where 
    apply' _ f a = f a 

instance (Magical b c d, r ~ (a -> d)) => Magical' False (a -> b) c r where 
    apply' _ f c = \a -> apply (f a) c 


data True 
data False 

class AreEqual a b r 
instance (r ~ True) => AreEqual a a r 
instance (r ~ False) => AreEqual a b r 


test :: Int -> Char -> Bool 
test i c = True 

t1 = apply test (5::Int) 
t2 = apply test 'c' 
Cuestiones relacionadas