2012-08-01 17 views
1

是否有一個功能,或者我如何寫一個函數updateTuple,使得:模板哈斯克爾元組更新功能

$(updateTuple 5 (0, 2, 4)) (_ -> 'a', (*2), _ -> 42) (1, 2, 3, 'b', 'c') 
    -> ('a', 2, 6, 'b', 42) 

基本的updateTuple的第一個參數是元組更新的長度,而第二是這些元素的索引。它產生一個函數,它帶有兩個元組,第一個是更新函數,第二個是舊元組,並將這些更新函數應用於各個元素。

我翻遍了tuple-th,但我找不到任何可以用來輕鬆實現的地方。

編輯:$(updateTuple 5 [0, 2, 4])也可以。

+0

'$(updateTuple 5 [0,2,4])'語法正確嗎?你不能「部分申請」TH; 'updateTuple'必須仍然有一個不依賴於它的值的類型。 – dflemstr 2012-08-01 09:29:29

+0

@dflemstr:沒關係。 – Clinton 2012-08-01 09:34:28

回答

3

我有點想別人迴應,但沒事。這裏是我做了非常快的解決方案:

module Tuples (updateTuple) where 

import Language.Haskell.TH 

updateTuple :: Int -> [Int] -> Q Exp 
updateTuple len ixs = do 
    ixfns <- mapM (newIxFunName . (+1)) ixs 
    ixvns <- mapM newIxVarName [1..len] 
    let baseVals = map VarE ixvns 
     modVals = foldr applyFun baseVals $ ixs `zip` ixfns 
    return . LamE [matchTuple ixfns, matchTuple ixvns] $ TupE modVals 
    where 
    matchTuple = TupP . map VarP 
    newIxFunName = newIndexedName "fun" 
    newIxVarName = newIndexedName "var" 
    newIndexedName prefix = newName . (prefix ++) . show 
    applyFun (ix, fn) = modifyElem ix $ AppE $ VarE fn 

modifyElem :: Int -> (a -> a) -> [a] -> [a] 
modifyElem 0 f (x:xs) = f x : xs 
modifyElem n f (x:xs) = x : modifyElem (n - 1) f xs 
modifyElem n _ [] = error $ "index " ++ show n ++ " out of bounds" 

用例:

{-# LANGUAGE TemplateHaskell #-} 
module Main where 
import Tuples 

main :: IO() 
main = print $ $(updateTuple 5 [0, 2, 4]) 
       (\ _ -> 'a', (*2), \ _ -> 42) 
       (1, 2, 3, 'b', 'c') 

彙編(顯示生成的代碼):

$ ghc -ddump-splices -fforce-recomp main.hs 
[1 of 2] Compiling Tuples   (Tuples.hs, Tuples.o) 
[2 of 2] Compiling Main    (main.hs, main.o) 
Loading package ghc-prim ... linking ... done. 
Loading package integer-gmp ... linking ... done. 
Loading package base ... linking ... done. 
Loading package pretty-1.1.1.0 ... linking ... done. 
Loading package array-0.4.0.0 ... linking ... done. 
Loading package deepseq-1.3.0.0 ... linking ... done. 
Loading package containers-0.4.2.1 ... linking ... done. 
Loading package template-haskell ... linking ... done. 
main.hs:6:18-40: Splicing expression 
    updateTuple 5 [0, 2, 4] 
    ======> 
    \ (fun1_a1Cl, fun3_a1Cm, fun5_a1Cn) 
     (var1_a1Co, var2_a1Cp, var3_a1Cq, var4_a1Cr, var5_a1Cs) 
     -> (fun1_a1Cl var1_a1Co, var2_a1Cp, fun3_a1Cm var3_a1Cq, 
      var4_a1Cr, fun5_a1Cn var5_a1Cs) 
Linking main ... 

輸出:

$ ./main 
('a',2,6,'b',42) 

編輯:使lambda中的函數使用與變量相同的索引,使這種方式更有意義。

+0

爲了清楚起見,我應該添加一個註釋:原始問題在純haskell中沒有答案,因爲它需要依賴類型。必須使用類型系統或使用某種宏來決定編譯類型。後者要容易得多,但第一個仍然有可能。 – permeakra 2012-08-01 19:55:47