Reputation: 959
I'm writing an interpreter of sorts in Haskell, it's been super interesting so far. I'm in the Codegen step (take the parser result, pretty print it into code), One thing I tried to do is the following:
I have Modules and my modules have declarations.
codegen :: Module -> Either String ByteString
codegen Module { what = "module", declarations = decls } = Right $ foldM (\output decl ->
output ++ (codegenDecl decl)) (empty :: ByteString) decls -- generate declarations
codegen Module { what = s } = Left $ "Bad module 'what' key " ++ s
codegenDecl :: Declaration -> Either String ByteString
codegenDecl Declaration { what = dt, name = dn, argnames = Just al, constructors = Just lc } = Right $ "Declaration " ++ dn ++ " of type " ++ dt
The pattern-matched variable decls
is decls :: [Declaration]
and I'm using the Either monad for error tracking. What I expect from
foldM (\output decl ->
output ++ (codegenDecl decl)) (empty :: ByteString) decls
Is to concat all the ByteStrings if all the declarations are correct, or return a Left $ "Error writing a declaration"
But I think I'm missing a couple of things here as the typecheker complains. If either of the declarations fails, I want the whole Module to fail. If all of them succeed, I want to concatenate them into one ByteString.
[decls] --------> Right result -------> Right $ foldM (++) accumulator result |
| ^ |
| -----------------------------------
|
|-----------> Left err ------------> Left $ err
The bottom part seems to be what the >>= operator does for Either, so that makes me think there's a stylish, monadic way to do what I want without cases and the such. I'd be curious to find out what's the best style here.
Upvotes: 2
Views: 652
Reputation: 152817
This isn't really an answer to the question, since it doesn't address your questions about foldM
... but I wouldn't even use foldM
at all. I think it's cleaner to do all your codegenDecl
ing, then concatenate the results separately. This will have two advantages:
ByteString
concatenation operation, which can build an appropriately-sized buffer first then walk it once to fill it. This will be more efficient than doing repeated appends, which will have to rewalk early ByteString
s many times and allocate many buffers.mapM
instead of foldM
-- the reader can pay less attention and still come to the right conclusion about what's happening.Here's how that would look:
mconcat <$> mapM codegenDecl decls
Upvotes: 3
Reputation: 33439
(++) :: [a] -> [a] -> [a]
appends lists
output :: ByteString
codegenDecl decl :: Either String ByteString
output
doesn't match the expected type of the first argument of (++)
(unless it was redefined somewhere, e.g., in basic-prelude), and codegenDecl
doesn't match the expected type of the second argument of (++)
.
This lambda should typecheck (with (<>)
from Data.Monoid
):
\output decl -> fmap (output <>) (codegenDecl decl)
Upvotes: 2