2012-08-01 2 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

+0

@dflemstr : 괜찮습니다. – Clinton

답변

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) 

편집 : 람다의 함수는 변수와 같은 색인을 사용하여 더 많은 의미가 그런 식으로합니다.

+0

그냥 분명히하기 위해, 필자는 메모를 추가해야합니다 : 원래의 질문은 종속 된 유형이 필요하기 때문에 순수한 haskell에는 응답이 없습니다. 하나는 타입 시스템을 사용하거나 어떤 종류의 매크로를 사용하여 컴파일 유형을 결정해야합니다. 후자는 훨씬 쉽지만 첫 번째는 여전히 가능합니다. – permeakra