2014-07-19 10 views
5

Ho un numero di livello aUncurry per funzioni n-ario

data Z deriving Typeable 
data S n deriving Typeable 

e n-ario funzioni (codice dal pacchetto-vector fissa)

-- | Type family for n-ary functions. 
type family Fn n a b 
type instance Fn Z  a b = b 
type instance Fn (S n) a b = a -> Fn n a b 

-- | Newtype wrapper which is used to make 'Fn' injective. It's also a 
-- reader monad. 
newtype Fun n a b = Fun { unFun :: Fn n a b } 

ho bisogno funzione come

uncurryN :: Fun (n + k) a b -> Fun n a (Fun k a b) 

Ho letto diversi articoli sui calcoli a livello di caratteri, ma tutto sulla concatenazione di tipo lista sicura.

+0

Tipo concatenazione lista sicura? Da quando quello standard non è sicuro? –

+0

@uraf Forse è con liste eterogenee. – AndrewC

risposta

3

È possibile farlo senza alcun tipo classi con la costruzione di un tipo di dati che può rappresentare il tipo Nat a livello di dati:

data Nat = Z | S Nat 

type family Fn (n :: Nat) a b 
type instance Fn Z  a b = b 
type instance Fn (S n) a b = a -> Fn n a b 

type family Add (n :: Nat) (m :: Nat) :: Nat 
type instance Add Z   m = m 
type instance Add (S n)  m = S (Add n m) 

newtype Fun n a b = Fun { unFun :: Fn n a b } 

data SNat (n :: Nat) where 
    SZ :: SNat Z 
    SS :: SNat n -> SNat (S n) 

uncurryN :: forall n m a b . SNat n -> Fun (Add n m) a b -> Fun n a (Fun m a b) 
uncurryN SZ f = Fun f 
uncurryN (SS (n :: SNat n')) g = Fun (\x -> unFun (uncurryN n (Fun (unFun g x)) :: Fun n' a (Fun m a b))) 

Se non ti piace menzionare in modo esplicito il parametro n, questo è ok dato che puoi sempre andare avanti e indietro tra una funzione che accetta un parametro come una classe di tipo e che prende un parametro come dati:

class SingI (a :: k) where 
    type Sing :: k -> * 
    sing :: Sing a 

instance SingI Z where 
    type Sing = SNat 
    sing = SZ 

instance SingI n => SingI (S n) where 
    type Sing = SNat 
    sing = SS sing 

toNatSing :: (SNat n -> t) -> (SingI n => t) 
toNatSing f = f sing 

fromNatSing :: (SingI n => t) -> (SNat n -> t) 
fromNatSing f SZ = f 
fromNatSing f (SS n) = fromNatSing f n 

uncurryN' :: SingI n => Fun (Add n m) a b -> Fun n a (Fun m a b) 
uncurryN' = toNatSing uncurryN 
4

Ciò ha richiesto un po 'di attenzione nello scartare/riavvolgere il nuovo tipo Fun. Ho anche sfruttato l'estensione DataKinds.

{-# LANGUAGE DataKinds, KindSignatures, TypeFamilies, 
    MultiParamTypeClasses, ScopedTypeVariables, FlexibleInstances #-} 
{-# OPTIONS -Wall #-} 

-- | Type-level naturals. 
data Nat = Z | S Nat 

-- | Type family for n-ary functions. 
type family Fn (n :: Nat) a b 
type instance Fn Z  a b = b 
type instance Fn (S n) a b = a -> Fn n a b 

-- | Addition. 
type family Add (n :: Nat) (m :: Nat) :: Nat 
type instance Add Z   m = m 
type instance Add (S n)  m = S (Add n m) 

-- | Newtype wrapper which is used to make 'Fn' injective. 
newtype Fun n a b = Fun { unFun :: Fn n a b } 

class UncurryN (n :: Nat) (m :: Nat) a b where 
    uncurryN :: Fun (Add n m) a b -> Fun n a (Fun m a b) 

instance UncurryN Z m a b where 
    uncurryN g = Fun g 

instance UncurryN n m a b => UncurryN (S n) m a b where 
    uncurryN g = Fun (\x -> unFun (uncurryN (Fun (unFun g x)) :: Fun n a (Fun m a b))) 

{- An expanded equivalent with more signatures: 

instance UncurryN n m a b => UncurryN (S n) m a b where 
    uncurryN g = let f :: a -> Fn n a (Fun m a b) 
        f x = let h :: Fun (Add n m) a b 
           h = Fun ((unFun g :: Fn (Add (S n) m) a b) x) 
          in unFun (uncurryN h :: Fun n a (Fun m a b)) 
        in Fun f 
-} 
+2

Nota che stai ricorrendo solo con i parametri 'n', e in ogni caso' a', 'b',' m' sono completamente gratuiti. Questo è un indicatore che non dovrebbero essere parametri per la classe 'UncurryN', dovrebbero essere variabili libere. Questo migliorerà l'inferenza di tipo quando si usa 'uncurryN': se sono tutti parametri, il compilatore penserà che contano a quale classe viene scelta e richiederà che siano monomorfici o menzionati esplicitamente. – user2407038