Json in Idris

import Data.String
import Data.List
import Data.Maybe
import System.File
import Language.JSON

-- We'll use these two records to demonstrate encoding and
-- decoding of custom types below.
record Response1 where
  constructor MkResponse1
  page : Int
  fruits : List String

record Response2 where
  constructor MkResponse2
  page : Int
  fruits : List String

main : IO ()
main = do
  -- First we'll look at encoding basic data types to
  -- JSON strings. Here are some examples for atomic
  -- values.
  putStrLn $ show $ encode True
  putStrLn $ show $ encode 1
  putStrLn $ show $ encode 2.34
  putStrLn $ show $ encode "gopher"

  -- And here are some for lists and dictionaries, which encode
  -- to JSON arrays and objects as you'd expect.
  let slcD = ["apple", "peach", "pear"]
  putStrLn $ show $ encode slcD

  let mapD = [("apple", 5), ("lettuce", 7)]
  putStrLn $ show $ encode mapD

  -- The JSON package can automatically encode your
  -- custom data types. It will only include exported
  -- fields in the encoded output and will by default
  -- use those names as the JSON keys.
  let res1D = MkResponse1 1 ["apple", "peach", "pear"]
  putStrLn $ show $ encode res1D

  -- Now let's look at decoding JSON data into Idris
  -- values. Here's an example for a generic data
  -- structure.
  let byt = "{\"num\":6.13,\"strs\":[\"a\",\"b\"]}"

  -- We need to provide a type where the JSON
  -- package can put the decoded data. This
  -- `Maybe (List (String, JSON))` will hold a dictionary of strings
  -- to arbitrary data types.
  case decode byt of
    Just (JObject dat) => do
      putStrLn $ show dat
      -- In order to use the values in the decoded map,
      -- we'll need to convert them to their appropriate type.
      -- For example here we convert the value in `num` to
      -- the expected `Double` type.
      case lookup "num" dat of
        Just (JNumber num) => putStrLn $ show num
        _ => putStrLn "Failed to find 'num'"

      -- Accessing nested data requires a series of
      -- pattern matches.
      case lookup "strs" dat of
        Just (JArray strs) => 
          case head' strs of
            Just (JString str1) => putStrLn str1
            _ -> putStrLn "Failed to get first string"
        _ -> putStrLn "Failed to find 'strs'"
    _ -> putStrLn "Failed to parse JSON"

  -- We can also decode JSON into custom data types.
  -- This has the advantages of adding additional
  -- type-safety to our programs and eliminating the
  -- need for type assertions when accessing the decoded
  -- data.
  let str = "{\"page\": 1, \"fruits\": [\"apple\", \"peach\"]}"
  case decode str of
    Just (JObject obj) => 
      case fromJSON obj of
        Just res => do
          putStrLn $ show res
          putStrLn $ show $ head' (fruits res)
        Nothing -> putStrLn "Failed to parse into Response2"
    _ -> putStrLn "Failed to parse JSON"

  -- In the examples above we always used strings as
  -- intermediates between the data and JSON representation.
  -- We can also stream JSON encodings directly to files or
  -- even network connections.
  let d = [("apple", 5), ("lettuce", 7)]
  Right () <- writeFile "output.json" (show $ encode d)
    | Left err => putStrLn $ "Error writing file: " ++ show err
  pure ()

This Idris code demonstrates JSON encoding and decoding, similar to the Go example. However, there are some differences due to Idris’s features and limitations:

  1. Idris uses record instead of struct for defining custom types.
  2. JSON handling in Idris is typically done using the Language.JSON module, which provides encode and decode functions.
  3. Idris uses pattern matching extensively for handling Maybe types and JSON structures.
  4. Error handling is done through the use of Maybe types and pattern matching, rather than explicit error checks.
  5. Idris doesn’t have a direct equivalent to Go’s json.NewEncoder, so file writing is done more directly.

Note that Idris’s standard library might not have all the JSON functionality that Go’s encoding/json package provides. In a real-world scenario, you might need to use or create additional libraries for more advanced JSON handling.