可逆状态monad(和解析器)

美好的一天,女士们,先生们!

我一直在写解析器和编解码器。 实现解析器和打印机似乎是大规模的代码重复。 我怀疑是否有可能颠倒有状态计算,因为它本质上是同构的。

可以反转纯函数组合(Control.Lens.Iso通过定义组合运算符超过同构)。 如可以观察到的那样,

Iso bc cb . Iso ab ba = Iso (bc . ab) (ba . cb) -- from Lenses talk
invert (f . g) = (invert g) . (invert f)        -- pseudo-code

换句话说,要反转一个函数组合,应该按照相反的顺序组合反函数。 因此,如果定义了所有原始同构对,就可以将它们组合起来,以获得更复杂的对,而不会出现代码重复。 这里是一个纯粹的双向计算的例子(使用Control.Lens,解释性视频可以帮助你获得透镜,折叠和遍历的总体思路):

import Control.Lens

tick :: Num a => Iso' a a
tick = iso (+1) (subtract 1)   -- define an isomorphic pair

double :: Num a => Iso' a a
double = iso (+2) (subtract 2) -- and another one

threeTick :: Num a => Iso' a a
-- 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

正如你所看到的,我不需要提供threeTick的反转版本; 它是通过自动向后合成获得的!

现在,让我们考虑一个简单的解析器。

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 a b) = 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

你可以看到parseFoo两个版本都是相当声明式的(感谢解析器组合器)。 请注意parseFooprintFoo之间的相似性。 我可以定义原语解析器( decimalchar )的同构,然后自动导出打印机( printFoo :: FOO -> String )吗? 理想情况下,解析器组合器也可以工作。

我试图重新定义一个monadic >>=操作符以提供反向语义,但我没有这样做。 我觉得可以类似于构图倒转定义倒置的Kleisli构成算子(一元函数构成),但是可以将它与普通单子一起使用吗?

f :: a -> m b,     inverse f :: b -> m a
g :: b -> m c,     inverse g :: c -> m b
inverse (f >=> g) = (inverse f) <=< (inverse g)

为什么inverse fb -> ma而不是mb -> a ? 答案是:monadic副作用是箭头的属性,而不是数据类型b的属性。 专家视频专家将进一步讨论国家单粒子二元化问题。

如果解决方案确实存在,您能否提供一个printFoo派生的工作示例? 顺便说一句,这是一篇有趣的论文,可以帮助我们找到解决方案。


您可能有兴趣进一步深入研究Prism概念的lens套件。

一个Prism可以被用作一个'聪明的构造函数'来无条件地构建一些东西(比如一个漂亮的打印字符串),并且匹配它(比如parse )。

你不得不忽视法律或将法律视为只能达到商数,因为你从漂亮打印中得到的字符串很可能不是你解析的字符串。

链接地址: http://www.djcxy.com/p/75555.html

上一篇: Invertible State monad (and parsers)

下一篇: How to get values of a Matrix which are non zero