File: MonadFix.hs

package info (click to toggle)
haskell-aeson 2.2.3.0-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 9,076 kB
  • sloc: haskell: 13,153; makefile: 11
file content (112 lines) | stat: -rw-r--r-- 3,876 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecursiveDo #-}
module UnitTests.MonadFix (monadFixTests) where

import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (testCase, Assertion, (@?=))

import qualified Data.Map as Map -- Lazy

import Data.Aeson
import Data.Aeson.Types
import qualified Data.Aeson.KeyMap as KM

-------------------------------------------------------------------------------
-- MonadFix
-------------------------------------------------------------------------------

monadFixDecoding1 :: (Value -> Data.Aeson.Types.Parser [Char]) -> Assertion
monadFixDecoding1 p = do
    fmap (take 10) (parseMaybe p value) @?= Just "xyzxyzxyzx"
  where
    value = object
        [ "foo" .= ('x', "bar" :: String)
        , "bar" .= ('y', "quu" :: String)
        , "quu" .= ('z', "foo" :: String)
        ]

monadFixDecoding2 :: (Value -> Data.Aeson.Types.Parser [Char]) -> Assertion
monadFixDecoding2 p = do
    fmap (take 10) (parseMaybe p value) @?= Nothing
  where
    value = object
        [ "foo" .= ('x', "bar" :: String)
        , "bar" .= ('y', "???" :: String)
        , "quu" .= ('z', "foo" :: String)
        ]

monadFixDecoding3 :: (Value -> Data.Aeson.Types.Parser [Char]) -> Assertion
monadFixDecoding3 p =
    fmap (take 10) (parseMaybe p value) @?= Nothing
  where
    value = object
        [ "foo" .= ('x', "bar" :: String)
        , "bar" .= Null
        , "quu" .= ('z', "foo" :: String)
        ]

monadFixDecoding4 :: (Value -> Data.Aeson.Types.Parser [Char]) -> Assertion
monadFixDecoding4 p =
    fmap (take 10) (parseMaybe p value) @?= Nothing
  where
    value = object
        [ "els" .= ('x', "bar" :: String)
        , "bar" .= Null
        , "quu" .= ('z', "foo" :: String)
        ]

-- Parser with explicit references
monadFixParserA :: Value -> Data.Aeson.Types.Parser [Char]
monadFixParserA = withObject "Rec" $ \obj -> mdo
    let p'' :: Value -> Data.Aeson.Types.Parser String
        p'' "foo" = return foo
        p'' "bar" = return bar
        p'' "quu" = return quu
        p'' _     = fail "Invalid reference"

    let p' :: Value -> Data.Aeson.Types.Parser [Char]
        p' v = do
            (c, cs) <- liftParseJSON Nothing p'' (listParser p'') v
            return (c : cs)

    foo <- explicitParseField p' obj "foo"
    bar <- explicitParseField p' obj "bar"
    quu <- explicitParseField p' obj "quu"
    return foo

-- Parser with arbitrary references!
monadFixParserB :: Value -> Data.Aeson.Types.Parser [Char]
monadFixParserB = withObject "Rec" $ \obj -> mdo
    let p'' :: Value -> Data.Aeson.Types.Parser String
        p'' key' = do
            key <- parseJSON key'
            -- this is ugly: we look whether key is in original obj
            -- but then query from refs.
            --
            -- This way we are lazier. Map.traverse isn't lazy enough.
            case KM.lookup key obj of
                Just _  -> return (refs Map.! key)
                Nothing -> fail "Invalid reference"

    let p' :: Value -> Data.Aeson.Types.Parser [Char]
        p' v = do
            (c, cs) <- liftParseJSON Nothing p'' (listParser p'') v
            return (c : cs)

    refs <- traverse p' (KM.toMap obj)
    case Map.lookup "foo" refs of
        Nothing   -> fail "No foo node"
        Just root -> return root

monadFixTests :: TestTree
monadFixTests = testGroup "MonadFix"
    [ testCase "Example1a" $ monadFixDecoding1 monadFixParserA
    , testCase "Example2a" $ monadFixDecoding2 monadFixParserA
    , testCase "Example3a" $ monadFixDecoding3 monadFixParserA
    , testCase "Example4a" $ monadFixDecoding4 monadFixParserA

    , testCase "Example1b" $ monadFixDecoding1 monadFixParserB
    , testCase "Example2b" $ monadFixDecoding2 monadFixParserB
    , testCase "Example3b" $ monadFixDecoding3 monadFixParserB
    , testCase "Example4b" $ monadFixDecoding4 monadFixParserB
    ]