I've recently tried to write my functions by using composition. But this one, renderCross'
is especially difficult to convert fully. How would I write this composition point free? It's the n
that I'm trying to make disappear. I've tried using <*>
, and pointfree.io uses liftA2
(but this results in very ugly code) so I would believe I'm at least somewhat close.
Maybe I could use the Reader monad, but would that be too complicated?
renderCross :: Int -> String
renderCross = spread . unlines . flip renderCross' [""]
where
renderCross' :: Int -> [String] -> [String]
renderCross' n = edge n . vert n . horiz n . mid n . horiz n . vert n . edge n
edge :: Int -> [String] -> [String]
edge i s = s ++ [space i ++ "ooo"]
vert :: Int -> [String] -> [String]
vert i s = s ++ replicate (i - 2) (space i ++ "o|o")
horiz :: Int -> [String] -> [String]
horiz i s = s ++ [horiz' ++ "|" ++ horiz']
where
horiz' = replicate i 'o'
mid :: Int -> [String] -> [String]
mid i s = s ++ [intercalate (replicate (i - 1) '-') ["o", "o", "o"]]
space :: Int -> String
space n = replicate (n - 1) ' '
EDIT:
-- spread out word, i.e. add a space between each character
spread = intersperse ' '
1 Answer 1
Yes, you could use the Reader monad to get a pointfree version of renderCross'
, like this:
renderCross :: Int -> String
renderCross = spread . unlines . renderCross' [""]
where
renderCross' :: [String] -> Int -> [String]
renderCross' = runReader . (edge >=> vert >=> horiz >=> mid >=> horiz >=> vert >=> edge)
edge :: [String] -> Reader Int [String]
edge s = asks (\i -> s ++ [space i ++ "ooo"])
vert :: [String] -> Reader Int [String]
vert s = asks (\i -> s ++ replicate (i - 2) (space i ++ "o|o"))
horiz :: [String] -> Reader Int [String]
horiz s = asks (\i -> let horiz' = replicate i 'o' in s ++ [horiz' ++ "|" ++ horiz'])
mid :: [String] -> Reader Int [String]
mid s = asks (\i -> s ++ [intercalate (replicate (i - 1) '-') ["o", "o", "o"]])
space :: Int -> String
space n = replicate (n - 1) ' '
This does the following:
- The functions
edge
,vert
,horiz
, andmid
now return aReader Int [String]
, their implementations useask
to access theInt
instead of using a plain function argument renderCross'
doesn't use function composition anymore, but(>=>)
, aka Kleisli composition. It's basically the same, but since you have functions that have a monadic return value, the simple function composition doesn't work anymore. Note that the order of composition is reversed compared to(.)
. You just don't see it in this example because it's the same forwards or backwards.renderCross'
usesrunReader
to feed in the argument to the composed function. Text-book application of the reader monad.- the type of
renderCross'
is different to your example. I switched the two arguments, to simplify the pointfree implementation. This has the additional benefit that you don't need toflip
it in the implementation ofrenderCross
.
So yes, it's possible to write a pointfree version of the function by using Reader
. Is it better than the original version? I'm not sure. It's definitely cool to have a pointfree implementation but you have to pay the price of more concepts that are used (Reader
, (>=>)
). Plus, in my experience, pointfree functions tend to be harder to understand than equivalent implementations with explicit arguments.
spread
? AFAICT, it's not in base, and I can't find a matching one on Hoogle. Would be helpful for trying out what the function does \$\endgroup\$