[Template Haskell Question] On defining recursive templates.

Hello Haskellers, I'm currently diving into Template Haskell and I just read the original TH paper [1]. There they give the following example of a generic zip function: -- | A generic zip function. Use (e.g.,) as $(zipN 3) xs ys zs. zipN :: Int -> ExpQ zipN n = [| let zp = $(mkZip n [| zp |]) in zp |] -- | Helper function for zipN. mkZip :: Int -> ExpQ -> ExpQ mkZip n contZip = lamE pYs (caseE (tupE eYs) [m1, m2]) where (pXs, eXs) = genPEs "x" n (pXSs, eXSs) = genPEs "xs" n (pYs, eYs) = genPEs "y" n allCons = tupP $ zipWith (\x xs -> [p| $x : $xs |]) pXs pXSs m1 = match allCons continue [] m2 = match wildP stop [] continue = normalB [| $(tupE eXs) : $(appsE (contZip:eXSs))|] stop = normalB (conE '[]) -- | Generates n pattern and expression variables. genPEs :: String -> Int -> ([PatQ], [ExpQ]) genPEs x n = (pats, exps) where names = map (\k -> mkName $ x ++ show k) [1..n] (pats, exps) = (map varP names, map varE names) This works as expected, e.g., `$(zipN 3) [1..3] [4..6] [7..9]' gives [(1,4,7),(2,5,8), (3,6,9)]. However, I found this definition of passing `[| zp |]' as a helper function slightly confusing, so I tried to make it more succinct and to call zipN directly in the recursion: zipN' :: Int -> ExpQ zipN' n = lamE pYs (caseE (tupE eYs) [m1, m2]) where (pXs, eXs) = genPEs "x" n (pXSs, eXSs) = genPEs "xs" n (pYs, eYs) = genPEs "y" n allCons = tupP $ zipWith (\x xs -> [p| $x : $xs |]) pXs pXSs m1 = match allCons continue [] m2 = match wildP stop [] continue = normalB [| $(tupE eXs) : $(appsE (zipN n:eXSs)) |] stop = normalB (conE '[]) This subtle change, however, causes the compiler to diverge and to get stuck at compiling splice `$(zipN' 3) [1..3] [4..6] [7..9]'... Could anyone explain to me why the first approach works, but the 2nd small deviation does not? Is it because the compiler keeps trying to inline the recursive call to (zip N) into the template indefinitely? Are there easier (more straightforward) alternative implementations to the (imo) slightly convoluted example of zipN from the paper? Any hints are very much appreciated. Thanks, Dominik. [1] http://research.microsoft.com/~simonpj/papers/meta-haskell/

Hello Haskellers, > > I'm currently diving into Template Haskell and I just read the > original TH paper [1]. There they give the following example of a > generic zip function: > > -- | A generic zip function. Use (e.g.,) as $(zipN 3) xs ys zs. > zipN :: Int -> ExpQ > zipN n = [| let zp = $(mkZip n [| zp |]) in zp |] > > -- | Helper function for zipN. mkZip :: Int -> ExpQ -> ExpQ > mkZip n contZip = lamE pYs (caseE (tupE eYs) [m1, m2]) > where > (pXs, eXs) = genPEs "x" n > (pXSs, eXSs) = genPEs "xs" n > (pYs, eYs) = genPEs "y" n > allCons = tupP $ zipWith (\x xs -> [p| $x : $xs |]) pXs pXSs > m1 = match allCons continue [] > m2 = match wildP stop [] > continue = normalB [| $(tupE eXs) : $(appsE (contZip:eXSs))|] > stop = normalB (conE '[]) > > -- | Generates n pattern and expression variables. > genPEs :: String -> Int -> ([PatQ], [ExpQ]) > genPEs x n = (pats, exps) > where names = map (\k -> mkName $ x ++ show k) [1..n] > (pats, exps) = (map varP names, map varE names) > > This works as expected, e.g., `$(zipN 3) [1..3] [4..6] [7..9]' gives > [(1,4,7),(2,5,8), (3,6,9)]. > > However, I found this definition of passing `[| zp |]' as a helper > function slightly confusing, so I tried to make it more succinct and to > call zipN directly in the recursion: > > zipN' :: Int -> ExpQ > zipN' n = lamE pYs (caseE (tupE eYs) [m1, m2]) > where > (pXs, eXs) = genPEs "x" n > (pXSs, eXSs) = genPEs "xs" n > (pYs, eYs) = genPEs "y" n > allCons = tupP $ zipWith (\x xs -> [p| $x : $xs |]) pXs pXSs > m1 = match allCons continue [] > m2 = match wildP stop [] > continue = normalB [| $(tupE eXs) : $(appsE (zipN n:eXSs)) |] > stop = normalB (conE '[]) > > This subtle change, however, causes the compiler to
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 Its not the compiler diverging, its your own code generating an infinite expression. [| zp |] is just a variable capturing the zp from the let expression. The generated code looks like this: let zp = \ y1 y2 y3 -> case (y1, y2, y3) of ((x1:xs1), (x2:xs2), (x3:xs3)) -> (x1, x2, x3) : zp xs1 xs2 xs3 _ -> [] in zp You on the other hand call zipN from zipN', so the above expression is inserted instead of zp. And in this expression there is again a zp, which you replaced... and so on... so it looks like this: \ y1 y2 y3 -> case (y1, y2, y3) of ((x1:xs1), (x2:xs2), (x3:xs3)) -> (x1, x2, x3) : (\ y1 y2 y3 -> case (y1, y2, y3) of ((x1:xs1), (x2:xs2), (x3:xs3)) -> (x1, x2, x3) : (\ y1 y2 y3 -> case (y1, y2, y3) of ((x1:xs1), (x2:xs2), (x3:xs3)) -> (x1, x2, x3) : (...) xs1 xs2 xs3 _ -> []) xs1 xs2 xs3 _ -> []) xs1 xs2 xs3 _ -> [] Hopefully it is now clear what is happening ; ) Jonas On 01/20/2016 08:01 PM, Dominik Bollmann wrote: diverge and to get > stuck at compiling splice `$(zipN' 3) [1..3] [4..6] [7..9]'... > > Could anyone explain to me why the first approach works, but the 2nd > small deviation does not? Is it because the compiler keeps trying to > inline the recursive call to (zip N) into the template indefinitely? > > Are there easier (more straightforward) alternative implementations to > the (imo) slightly convoluted example of zipN from the paper? > > Any hints are very much appreciated. > > Thanks, > Dominik. > > [1] http://research.microsoft.com/~simonpj/papers/meta-haskell/ > _______________________________________________ > Haskell-Cafe mailing list > Haskell-Cafe@haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQEcBAEBCAAGBQJWn9xYAAoJEM0PYZBmfhoBPT0IAJGgYDj2ISQheiA2OoTCB+k2 ELcbPWAlCFjpWqN4v2DUtTS1XSecJflvmusYyadGtW2s5OzBi1jOopwBFmB1KAz9 P8Lu4tM7OBbvlD5zQaIOD8rktOVtNrjT1r4mouu/dPPDgEF4ekVPkI4tphE3UD7Z grYG6eRsRix6dnFX/Ee+0/EYQeANPsAXUZiEcBbkGVUR2jY44MoycilrH5gjwzTS QSY2FZcAgeMqf1eBeYi3N3sMhCXH8zT2a2z+qzAVO6wMWTeXubXR6Hvk8naHf3Zv ttIw1N1RXE2ncpOQLjnw3NmYY3wmlq4t/tltr1ubUKb0a/1PU/nx3Q+H4YIsz3g= =v7H8 -----END PGP SIGNATURE-----
participants (2)
-
Dominik Bollmann
-
Jonas Scholl