Reversible State Monad (and parsers)

Good day, ladies and gentlemen!

I constantly write parsers and codecs. The implementation of both parsers and printers seems like massive code duplication. I wonder if state computation can be inverted since it is isomorphic in nature.

You can invert the composition of a pure function (Control.Lens.Iso did this by defining a composition operator over isomorphisms). As you can see

Iso bc cb . Iso ab ba = Iso (bc . ab) (ba . cb) -- from Lenses talk invert (f . g) = (invert g) . (invert f) -- pseudo-code 
In other words, to invert the composition of a function, you need to compose the inverted functions in the reverse order. Thus, given that all primitive isomorphic pairs are defined, we can compose them to obtain more complex pairs without duplicating code. The following is an example of a pure bi-directional calculation (using Control.Lens, an explanatory video can help you get an overview of lenses, Folds and Traversals):
 import Control.Lens tick :: Num a => Iso' aa tick = iso (+1) (subtract 1) -- define an isomorphic pair double :: Num a => Iso' aa double = iso (+2) (subtract 2) -- and another one threeTick :: Num a => Iso' aa -- These are composed via simple function composition! threeTick = double . tick main :: IO () main = do print $ (4 :: Int)^.tick -- => 5 print $ (4 :: Int)^.from tick -- => 3 print $ (4 :: Int)^.threeTick -- => 7, Composable print $ (4 :: Int)^.from threeTick -- => 1, YEAH 

As you can see, I did not need to put an inverted version of threeTick ; it is obtained by reverse composition automatically!

Now consider a simple parser.

 data FOO = FOO Int Int deriving Show parseFoo :: Parser FOO parseFoo = FOO <$> decimal <* char ' ' <*> decimal parseFoo' :: Parser FOO parseFoo' = do first <- decimal void $ char ' ' second <- decimal return $ FOO first second printFoo :: FOO -> BS.ByteString printFoo (FOO ab) = BS.pack(show a) <> BS.pack(" ") <> BS.pack(show b) main :: IO () main = do print $ parseOnly parseFoo "10 11" -- => Right (FOO 10 11) print $ parseOnly parseFoo' "10 11" -- => Right (FOO 10 11) print . printFoo $ FOO 10 11 -- => "10 11" print . parseOnly parseFoo . printFoo $ FOO 10 11 -- id 

You can see that both versions of parseFoo quite declarative (thanks to combinatorial analyzers). Note the similarities between parseFoo and printFoo . Can I detect isomorphisms over primitive parsers ( decimal and char ) and then just print the printer ( printFoo :: FOO -> String ) automatically? Ideally, parser combinators will also work.

I tried to override the monadic operator >>= to provide inverted semantics, but I did not. I feel that it is possible to define an inverted Claysley composition operator (composition of a monadic function) similar to composition inversion, but can it be used with ordinary monads?

 f :: a -> mb, inverse f :: b -> ma g :: b -> mc, inverse g :: c -> mb inverse (f >=> g) = (inverse f) <=< (inverse g) 

Why is inverse f type b -> ma and not mb -> a ? Answer: monadic side effect is an arrow attribute, not data type b . The dualization of the state monad is further discussed in a large video expert.

If there is a solution could you provide a working example of printFoo output? By the way, here is an interesting one that can help us find a solution.

+7
parsing haskell state
source share
1 answer

You may be interested in working further in the lens package for the Prism concept.

A Prism can be used as a β€œsmart constructor” to create something (for example, a fairly printed line) unconditionally and match it (for example, parse ).

You will have to ignore the laws or treat the laws only as private, since the line from which you exit the rather printed version most likely does not exactly correspond to the line that you analyzed.

+4
source share

All Articles