2017-07-18 122 views
1

我想在可變參數函數定義fmapFMAP了可變參數功能

type family VarArg (args :: [*]) e where 
    VarArg '[] e = e 
    VarArg (a ': as) e = a -> VarArg as e 

mapVarArg :: forall args e e' 
      . (e -> e') -> VarArg args e -> VarArg args e' 
mapVarArg f = _ 

這是最接近的解決方案,我發現:

mapVarArg :: forall args e e' . VarArgIso args 
      => (e -> e') -> VarArg args e -> VarArg args e' 
mapVarArg f = (^. Lens.from varArgIso) . fmap f . (^. varArgIso @args) 

data VarArgD (args :: [*]) e where 
    DNil :: e -> VarArgD '[] e 
    DCons :: (a -> VarArgD as e) -> VarArgD (a ': as) e 

class VarArgIso (args :: [*]) where 
    varArgIso :: Iso' (VarArg args e) (VarArgD args e) 

instance VarArgIso '[] where 
    varArgIso = iso DNil (\(DNil x) -> x) 

instance VarArgIso as => VarArgIso (a ': as) where 
    varArgIso = iso (\f -> DCons ((^. varArgIso) . f)) (\(DCons f) -> ((^. Lens.from varArgIso) . f)) 

instance Functor (VarArgD args) where 
    fmap f (DNil a) = DNil (f a) 
    fmap f (DCons g) = DCons (fmap f . g) 

是否有一個簡單的解決方案,或任何解決方案沒有額外的VarArgIso約束?

回答

1

我認爲非模板解決方案是不可能的,沒有額外的類約束。有一個簡單的,想必高效的實現具有重疊情況:

class VarArg a b c d where 
    mapVarArg :: (a -> b) -> c -> d 

instance (a ~ c, b ~ d) => VarArg a b c d where 
    mapVarArg = id 

instance {-# overlapping #-} (VarArg a b c2 d2, c1 ~ d1) => 
    VarArg a b (c1 -> c2) (d1 -> d2) where 
    mapVarArg f g = mapVarArg f . g 

如果您要更換overlappingincoherent,它也通常與多態/約束函數可以作爲參數:

> mapVarArg (+100) (+) 0 0 
100 

然而,隨着incoherent實例,部分應用mapVarArg-s傾向於具有不可用的推斷類型。

> let foo = mapVarArg (+100) (+) 
> :t foo 
foo :: (Num (a -> a -> a), Num a) => a -> a -> a