point-free style での書き方

Haskell で普通の記法から point-free style へと変形する方針について考えてみた.
きっかけは,この書き込み
http://pc12.2ch.net/test/read.cgi/tech/1242876647/11

module Main(main) where

import Data.Char
import Control.Monad
import Data.List

main = do { str <- getLine
          ; let xss = fun38 . map digitToInt $ str
          ; display xss
          ; putStr "last = "
          ; putChar . intToDigit . head . last $ xss
          }

fun38 [] = []
fun38 xs = xs : fun38 (zipWith (\a b -> (a + b) `mod` 10) xs (tail xs))

display = zipWithM ((putStrLn .) . (. (intersperse ' ' . map intToDigit)) . (++) . flip replicate ' ') [0..]

これの zipWithM*1 の引数がぱっと見理解できなかったので,通常の記法からここまで行く自分なりの過程を辿ることにする.
方針としては,

f x = g $ h x
f = g . h

f x = x + i
f = (+ i)

*2

f x y = g y x
f = flip g

に基づいて変形していく.


普通に書けばこうだろう.

\i xs -> putStrLn $ (replicate i ' ') ++ (intersperse ' ' $ map intToDigit xs)

まずは xs を消すことから始める.これはそれほど複雑ではなく,

\i xs -> putStrLn $ (++) (replicate i ' ') (intersperse ' ' $ map intToDigit xs)
\i xs -> putStrLn $ (++) (replicate i ' ') $ intersperse ' ' $ map intToDigit xs
\i -> putStrLn . (++) (replicate i ' ') . intersperse ' ' . map intToDigit

となる.
ここで,簡単のために

f :: [Int] -> String
f = intersperse ' ' . map intToDigit

とおいて話を進める.

\i -> putStrLn . (++) (replicate i ' ') . f
\i -> putStrLn . (. f) ((++) (replicate i ' '))
\i -> (putStrLn .) $ (. f) ((++) (replicate i ' '))
\i -> (putStrLn .) $ (. f) $ (++) $ replicate i ' '
\i -> (putStrLn .) $ (. f) $ (++) $ flip replicate ' ' i
(putStrLn .) . (. f) . (++) . flip replicate ' '

あとは f を戻せば

(putStrLn .) . (. (intersperse ' ' . map intToDigit)) . (++) . flip replicate ' '

と,最初の式になる.


はっきり言って,これくらいになってくると point-free style で書くべきではないでしょうね.
しかしまぁ,一種のパズルとして楽しむのもアリだと思います.

おまけ

なぜ display はこれほどまで複雑な point-free style で書いてあるのに,fun38 の zipWith はそうではないんだろうか…
これも上記と同様に point-free style に書き直せる.

fun38 :: [Int] -> [[Int]]
fun38 [] = []
fun38 xs = xs : fun38 (zipWith ((flip mod 10 .) . (+)) xs (tail xs))

*1:どうでもいいけど,zipWithM_ のほうが適切だと思う

*2:+ は任意の中置演算子でよい