Recursively change a JSON data structure in Haskell - json

I am trying to write a function that will take a JSON object, make a change to every string value in it and return a new JSON object. So far my code is:
applyContext :: FromJSON a => a -> a
applyContext x =
case x of
Array _ -> map applyContext x
Object _ -> map applyContext x
String _ -> parseValue x
_ -> x
However, the compiler complains about second second case line:
Couldn't match expected type `[b0]' with actual type `a'
`a' is a rigid type variable bound by
the type signature for:
applyContext :: forall a. FromJSON a => a -> a
at app\Main.hs:43:17
I'm guessing that is because map is meant to work on lists, but I would have naively expected it to use Data.HashMap.Lazy.map instead, since that is what the type actually is in that case. If I explicitly use that function I get
Couldn't match expected type `HashMap.HashMap k0 v20' with actual type `a'
which also makes sense, since I haven't constrained a to that extent because then it wouldn't work for the other cases. I suspect that if I throw enough explicit types at this I could make it work but it feels like it should be a lot simpler. What is an idiomatic way of writing this function, or if this is good then what would be the simplest way of getting the types right?

First of all, what FromJSON a => a does mean? It's type of some thing what says: it can be thing with any type but only from class FromJSON. This class can contain types which very differently constructed and you can't do any pattern matching. You can only do what is specified in the class FromJSON declaration by programmer. Basically, there is one method parseJSON :: FromJSON a => Value -> Parser a.
Secondly, you should use some isomorphic representation of JSON for your work. The type Value is good one. So, you can do the main work by the function like Value -> Value. After that, you can compose this fuction with parseJSON and toJSON for generalse types.
Like this:
change :: Value -> Value
change (Array x) = Array . fmap change $ x
change (Object x) = Object . fmap change $ x
change (String x) = Object . parseValue $ x
change x = x
apply :: (ToJSON a, FromJSON b) => (Value -> Value) -> a -> Result b
apply change = fromJSON . change . toJSON
unsafeApply :: (ToJSON a, FromJSON b) => (Value -> Value) -> a -> b
unsafeApply change x = case apply change x of
Success x -> x
Error msg -> error $ "unsafeApply: " ++ msg
applyContext :: (ToJSON a, FromJSON b) => a -> b
applyContext = unsafeApply change
You can write more complicated transformations like Value -> Value with lens and lens-aeson. For example:
import Control.Lens
import Control.Monad.State
import Data.Aeson
import Data.Aeson.Lens
import Data.Text.Lens
import Data.Char
change :: Value -> Value
change = execState go
where
go = do
zoom values go
zoom members go
_String . _Text . each %= toUpper
_Bool %= not
_Number *= 10
main = print $ json & _Value %~ change
where json = "{\"a\":[1,\"foo\",false],\"b\":\"bar\",\"c\":{\"d\":5}}"
Output will be:
"{\"a\":[10,\"FOO\",true],\"b\":\"BAR\",\"c\":{\"d\":50}}"

Related

Easiest way to enter a JSON literal in Haskell ghci

I'm testing out some functions in ghci before I put them into a project, and I'm straining to find the simplest way to enter json literals in the ghci without a ton of boilerplate
For example, I'd like to try out:
myFuncThatTakesAJson
on inputs
x = {}
y = {"a": 1}
But when I try entering these:
import Text.JSON
Prelude> x = parseJSON "{}"
Prelude> y = parseJSON "{'a' : 1}"
I see errors like:
<interactive>:17:5: error:
Variable not in scope: parseJSON :: [Char] -> t
y = fromJSON "{'a' : 1}"
<interactive>:20:5: error:
Data constructor not in scope: FromJSON :: [Char] -> t
Is there any way to allow json objects to be entered as strings without defining full data types with custom FromJSON instances?
There sure is! The type of a raw JSON value is Value. With that in mind, here's a couple of ways of constructing a Value in GHCi:
with parseJSON: since Value also has a FromJSON instance, you can use decode :: FromJSON a => ByteString -> Maybe a on it. Note that aeson is encouraging you to use ByteString instead of the inefficient default String. You can gloss over this detail by making string literals be overloaded by turning on -XOverloadedStrings:
ghci> import Data.ByteString
ghci> import Data.Aeson
ghci> :set -XOverloadedStrings
ghci> x = decode "{}" :: Maybe Value
x :: Maybe Value
ghci> y = decode "{\"a\" : 1}" :: Maybe Value -- note: single quotes won't work
y :: Maybe Value
with quasiquotes: you can also make use of the aesonQQ quasiquoter to get compile time JSON literals (this won't matter much in the REPL, beyond looking nicer, but in real code it means no actual parsing at runtime):
ghci> import Data.Aeson.QQ.Simple
ghci> :set -XQuasiQuotes
ghci> x = [aesonQQ| {} |]
x :: Value
ghci> y = [aesonQQ| {"a" : 1} |]
y :: Value

How to handle variability of JSON objects in Haskell?

Some REST service has variable returning JSONs, for example some fields can appear or disappear depending on the parameters of the request, the structure itself may change, nesting, etc.
So, this leads to avalanche-type growth in the number of types (along with FromJSON instances). Options are to:
try to make a lot of fields under Maybe (but this does not help very much with the variability in structure)
to introduce a lot of types
to create different phantom types (actually no big difference with prev.)
The 1. has drawback that if your call with some fixed parameters always returns good knows fields, you have to handle Nothing cases too, code becomes more complex. The 2. and 3. is tiring.
What is the most simple/convenient way to handle such variability in Haskell (if you use Aeson, sure, another option is to avoid Aeson usage)?
A possible solution to the existing/non-existing fields problem using type-level computation.
Some required extensions and imports:
{-# LANGUAGE DeriveGeneric, ScopedTypeVariables, DataKinds, KindSignatures,
TypeApplications, TypeFamilies, TypeOperators, FlexibleContexts #-}
import Data.Aeson
import Data.Proxy
import GHC.Generics
import GHC.TypeLits
Here's a data type (to be used promoted) that indicates if some field is absent or present. Also a type family that maps absent types to ():
data Presence = Present
| Absent
type family Encode p v :: * where
Encode Present v = v
Encode Absent v = ()
Now we can define a parameterized record containing all possible fields, like this:
data Foo (a :: Presence)
(b :: Presence)
(c :: Presence) = Foo {
field1 :: Encode a Int,
field2 :: Encode b Bool,
field3 :: Encode c Char
} deriving Generic
instance (FromJSON (Encode a Int),
FromJSON (Encode b Bool),
FromJSON (Encode c Char)) => FromJSON (Foo a b c)
One problem: writing the full type for each combination of occurrences/absences would be tedious, especially if only a few fields are present each time. But perhaps we could define an auxiliary type synonym FooWith that let us mention only those fields that are present:
type family Mentioned (ns :: [Symbol]) (n :: Symbol) :: Presence where
Mentioned '[] _ = Absent
Mentioned (n ': _) n = Present
Mentioned (_ ': ns) n = Mentioned ns n
-- the field names are repeated as symbols, how to avoid this?
type FooWith (ns :: [Symbol]) = Foo (Mentioned ns "field1")
(Mentioned ns "field2")
(Mentioned ns "field3")
Example of use:
ghci> :kind! FooWith '["field2","field3"]
FooWith '["field2","field3"] :: * = Foo 'Absent 'Present 'Present
Another problem: for each request, we must repeat the list of required fields two times: one in the URL ("fields=a,b,c...") and another in the expected type. It would be better to have a single source of truth.
We can deduce the term-level list of fields to be added to the URL from the type-level list of fields, by using an auxiliary type class Demote:
class Demote (ns :: [Symbol]) where
demote :: Proxy ns -> [String]
instance Demote '[] where
demote _ = []
instance (KnownSymbol n, Demote ns) => Demote (n ': ns) where
demote _ = symbolVal (Proxy #n) : demote (Proxy #ns)
For example:
ghci> demote (Proxy #["field2","field3"])
["field2","field3"]

How do I print the name and value of a custom data type in Haskell

Lets say I define a data type as follows:
data OP = Plus | Minus | Num Int deriving (Show, Eq)
Then I take a list of strings, and get a list of their respective OP values like this:
getOp :: [String] -> [OP]
getOp [] = []
getOp (x:rest)
| x == "+" = Plus:(getOp rest)
| isInfixOf "Num" x == True = Num (read (drop 4 x) :: Int):(getOp rest)
| otherwise = "-" = Minus:(getOp rest)
I then want to show the [OP] list, separated by new lines. I've done it with list of Strings easily, but not sure what to do with a list of data types.
I have the following structure as a starting point:
showOp :: [OP] -> String
showOp [] = []
showOp (o:os) = (putStr o):'\n':(showOp os)
I know the last line is wrong. I'm trying to return a [Char] in the first section, then a Char, then a recursive call. I tried some other variations for the last line (see below) with no luck.
showOp o = show o (works but not what I need. It shows the whole list, not each element on a new line
showOp o = putStrLn (show o) (epic fail)
showOp o
| o == "+" = "Plus\n":(showOp os)
| more of the same. Trying to return a [Char] instead of a Char, plus other issues.
Also, i'm not sure how the output will need to be different for the Num Int type, since I'll need to show the type name and the value.
An example i/o for this would be something like:
in:
getOp ["7","+","4","-","10"]
out:
Num 7
Plus
Num 4
Minus
Num 10
You need to look at the types of the functions and objects you are using. Hoogle is a great resource for getting function signatures.
For starters, the signature of putStr is
putStr :: String -> IO ()
but your code has putStr o, where o is not a string, and the result should not be an IO (). Do you really want showOp to print the Op, or just make a multi-line string for it?
If the former, you need the signature of showOp to reflect that:
showOp :: [Op] -> IO ()
Then you can use some do-notation to finish the function.
I'll write a solution for your given type signature. Since showOp should return a String and putStr returns an IO (), we won't be using putStr anywhere. Note that String is simply a type synonym for [Char], which is why we can treat Strings as a list.
showOp :: [Op] -> String
showOp [] = [] -- the empty list is a String
showOp (o:os) = showo ++ ('\n' : showos)
where showo = (show o) -- this is a String, i.e. [Char]
showos = showOp os -- this is also a String
Both showo and showos are Strings: both show and showOp return Strings.
We can add a single character to a list of characters using the cons operation :. We can append two lists of strings using append operator ++.
Now you might want another function
printOp :: [Op] -> IO ()
printOp xs = putStr $ showOp xs
How about:
showOp = putStrLn . unlines . map show
Note that your data constructor OP is already an instance of Show. Hence, you can actually map show into your array which contains members of type OP. After that, things become very somple.
A quick couple of notes ...
You might have wanted:
getOp :: [String] -> [OP]
getOp [] = []
getOp (x:rest)
| x == "+" = Plus:(getOp rest)
| x == "-" = Minus:(getOp rest)
| isInfixOf "Num" x == True = Num (read (drop 4 x) :: Int):(getOp rest)
| otherwise = (getOp rest)
Instead of what you have. Your program has a syntax error ...
Next, the input that you wanted to provide was probably
["Num 7","+","Num 4","-","Num 10"]
?. I guess that was a typo.

Int and Num type of haskell

I have below code to take the args to set some offset time.
setOffsetTime :: (Ord a, Num b)=>[a] -> b
setOffsetTime [] = 200
setOffsetTime (x:xs) = read x::Int
But compiler says "Could not deduce (b ~ Int) from the context (Ord a, Num b) bound by the type signature for setOffsetTime :: (Ord a, Num b) => [a] -> b
Also I found I could not use 200.0 if I want float as the default value. The compilers says "Could not deduce (Fractional b) arising from the literal `200.0'"
Could any one show me some code as a function (not in the prelude) that takes an arg to store some variable so I can use in other function? I can do this in the main = do, but hope
to use an elegant function to achieve this.
Is there any global constant stuff in Hasekll? I googled it, but seems not.
I wanna use Haskell to replace some of my python script although it is not easy.
I think this type signature doesn't quite mean what you think it does:
setOffsetTime :: (Ord a, Num b)=>[a] -> b
What that says is "if you give me a value of type [a], for any type a you choose that is a member of the Ord type class, I will give you a value of type b, for any type b that you choose that is a member of the Num type class". The caller gets to pick the particular types a and b that are used each time setOffsetTime is called.
So trying to return a value of type Int (or Float, or any particular type) doesn't make sense. Int is indeed a member of the type class Num, but it's not any member of the type class Num. According to that type signature, I should be able to make a brand new instance of Num that you've never seen before, import setOffsetTime from your module, and call it to get a value of my new type.
To come up with an acceptable return value, you can only use functions that likewise return an arbitrary Num. You can't use any functions of particular concrete types.
Existential types are essentially a mechanism for allowing the callee to choose the value for a type variable (and then the caller has to be written to work regardless of what that type is), but that's not really something you want to be getting into while you're still learning.
If you are convinced that the implementation of your function is correct, i.e., that it should interpret the first element in its input list as the number to return and return 200 if there is no such argument, then you only need to make sure that the type signature matches that implementation (which it does not do, right now).
To do so, you could, for example, remove the type signature and ask ghci to infer the type:
$ ghci
GHCi, version 7.6.2: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude> :{
Prelude| let setOffsetTime [] = 200
Prelude| setOffsetTime (x : xs) = read x :: Int
Prelude| :}
Prelude> :t setOffsetTime
setOffsetTime :: [String] -> Int
Prelude> :q
Leaving GHCi.
$
And indeed,
setOffsetTime :: [String] -> Int
setOffsetTime [] = 200
setOffsetTime (x : xs) = read x :: Int
compiles fine.
If you want a slightly more general type, you can drop the ascription :: Int from the second case. The above method then tells you that you can write
setOffsetTime :: (Num a, Read a) => [String] -> a
setOffsetTime [] = 200
setOffsetTime (x : xs) = read x
From the comment that you added to your question, I understand that you want your function to return a floating-point number. In that case, you can write
setOffsetTime :: [String] -> Float
setOffsetTime [] = 200.0
setOffsetTime (x : xs) = read x
or, more general:
setOffsetTime :: (Fractional a, Read a) => [String] -> a
setOffsetTime [] = 200.0
setOffsetTime (x : xs) = read x

Why pattern matching does not throw exception in Maybe monad

My question is simple. Why wrong pattern matching does not throw exception in Maybe monad. For clarity :
data Task = HTTPTask {
getParams :: [B.ByteString],
postParams :: [B.ByteString],
rawPostData :: B.ByteString
} deriving (Show)
tryConstuctHTTPTask :: B.ByteString -> Maybe Task
tryConstuctHTTPTask str = do
case decode str of
Left _ -> fail ""
Right (Object trie) -> do
Object getP <- DT.lookup (pack "getParams") trie
Object postP <- DT.lookup (pack "postParams") trie
String rawData <- DT.lookup (pack "rawPostData") trie
return $ HTTPTask [] [] rawData
Look at tryConstuctHTTPTask function. I think that when the pattern does not match (for example "Object getP") we must get something like "Prelude.Exception", instead i get the "Nothing". I like this behavior but i am not understand why.
Thanks.
Doing pattern <- expression in a do-block, will call fail when the pattern does not match. So it is equivalent to doing
expression >>= \x ->
case x of
pattern -> ...
_ -> fail
Since fail is defined as Nothing in the Maybe monad, you get Nothing for failed pattern matches using <-.