मैं निम्नलिखित कोड में से एक कॉन्फ़िग डेटा रिकॉर्ड प्राप्त करने का प्रयास कर रहा हूँ:

data Connections = Connections { cfgProperty  :: !Object
                   , connectionName :: String
                   } deriving (Show, Generic)

data Config = Config {connections :: [Connections]} deriving (Show, Generic)

data Cfg = Cfg { config :: Config } deriving (Show, Generic)



instance FromJSON Cfg
instance FromJSON Config
instance FromJSON Connections
instance ToJSON Cfg
instance ToJSON Config
instance ToJSON Connections

jsonFile :: FilePath
jsonFile = "config/config.json"

getCfg :: IO B.ByteString
getCfg = B.readFile jsonFile


parseCfg = do
      j <- (A.eitherDecode <$> getCfg) :: IO (Either String Cfg)
      case j of
        Left err ->  liftIO $ putStrLn err
        Right j -> config j

मुझे निम्न त्रुटि मिल रही है:

/apps/workspace/hade/src/Actor/MasterActor.hs: 56, 20
• Couldn't match expected type ‘IO ()’ with actual type ‘Config’
• In the expression: config j
  In a case alternative: Right j -> config j
  In a stmt of a 'do' block:
    case j of {
      Left err -> liftIO $ putStrLn err
      Right j -> config j }

यहाँ config.json है

{
  "config":
  {
    "connections": [
    {
      "cfgProperty":
      {
        "Env": "local",
        "Host": "localhost",
        "Port": "8001",
        "Directory": "/apps/workspace/hade/maps/src01_cvs"
      },
      "connectionName": "src01_cvs"
    },
    {
        "cfgProperty":
        {
          "Env": "local",
          "Host": "localhost",
          "Port": "8001",
          "Directory": "/apps/workspace/hade/maps/trg01_cvs"
        },
        "connectionName": "trg01_cvs"
      }
    ]
  }
}{
  "config":
  {
    "connections": [
    {
      "cfgProperty":
      {
        "Env": "local",
        "Host": "localhost",
        "Port": "8001",
        "Directory": "/apps/workspace/hade/maps/src01_cvs"
      },
      "connectionName": "src01_cvs"
    },
    {
        "cfgProperty":
        {
          "Env": "local",
          "Host": "localhost",
          "Port": "8001",
          "Directory": "/apps/workspace/hade/maps/trg01_cvs"
        },
        "connectionName": "trg01_cvs"
      }
    ]
  }
}

मैंने डिकोड और डिकोड दोनों का उपयोग करके कई अलग-अलग कॉन्फ़िगरेशन की कोशिश की है, लेकिन मैं हर बार बाधाओं में चला गया हूं। अगर मैं इस मामले को बदलता हूं तो मैं कॉन्फिग रिकॉर्ड को प्रिंट करने के लिए कोड प्राप्त कर सकता हूं:

      case j of
        Left err ->  putStrLn err
        Right j -> print $ config j

(कुछ अन्य परिवर्तनों के साथ), लेकिन मैं इसे केवल कॉन्फ़िग रिकॉर्ड को वापस करने के लिए नहीं प्राप्त कर सकता। किसी भी तरह की सहायता की सराहना की जाएगी।

1
peteofce 3 अप्रैल 2017, 01:27
1
यहां एक संकेत दिया गया है: case स्टेटमेंट की प्रत्येक शाखा को एक ही प्रकार की वापसी करनी चाहिए। इसके अतिरिक्त, a do ब्लॉक का प्रकार इसके अंतिम व्यंजक के समान है। parseCfg किस प्रकार का होना चाहिए? यदि आप अपनी सभी शीर्ष-स्तरीय घोषणाओं (जो आपको वैसे भी करना चाहिए) पर टाइप एनोटेशन डालते हैं, तो आपकी समस्या का समाधान बहुत स्पष्ट हो जाना चाहिए।
 – 
Alexis King
3 अप्रैल 2017, 01:30
क्या रिटर्न $ config j काम करता है?
 – 
mac10688
3 अप्रैल 2017, 01:37
एलेक्सिस, मैं "बाएं ->" से एक खाली कॉन्फिग रिकॉर्ड वापस करने के साथ ठीक हो जाऊंगा ताकि प्रकारों को समान बनाया जा सके, लेकिन मुझे यकीन नहीं है कि मैं यह कैसे करूंगा। कोई विचार? मैंने कुछ अलग चीजों की कोशिश की है, लेकिन अब तक कोई सफलता नहीं मिली है।
 – 
peteofce
3 अप्रैल 2017, 19:07
किसी ने उत्तर में बहुत प्रयास किया और फिर किसी कारण से गायब हो गया। बस जिज्ञासु क्या हुआ?
 – 
peteofce
4 अप्रैल 2017, 20:55
मैंने प्रश्न का उत्तर दिया और फिर अपना उत्तर हटा दिया क्योंकि, कुछ सोचने के बाद, मुझे यह पसंद नहीं आया। मैं एक सुधार पोस्ट करने जा रहा हूं, लेकिन मैं इस बीच पिछले एक को स्वीकार किए जाने का कोई मौका नहीं लेना चाहता था।
 – 
Dave Compton
4 अप्रैल 2017, 21:40

1 उत्तर

सबसे बढ़िया उत्तर

जिस समस्या का आप सामना कर रहे हैं उसकी जड़ getCfg को parseCfg में कॉल करना है। getCfg का प्रकार IO ByteString है जो एक IO सन्यासी है और केवल एक चीज जो आप एक IO सन्यासी के साथ कर सकते हैं, वह है उन कार्यों को कॉल करना जिन्हें सभी के लिए परिभाषित किया जाना चाहिए। मोनैड (bind, fmap, ap, ...) ये सभी एक और IO मोनाड लौटाते हैं। इसका अर्थ यह है कि यदि parseCfg getCfg को कॉल करने जा रहा है तो उसे एक IO सन्यासी को जरूरी लौटाना होगा।

haskell wiki को उद्धृत करने के लिए: "क्योंकि आप IO मोनाड से बच नहीं सकते, ऐसा फ़ंक्शन लिखना असंभव है जो आईओ मोनैड में गणना करता है लेकिन जिसके परिणाम प्रकार में IO टाइप कन्स्ट्रक्टर शामिल नहीं है।"

इसका एक तरीका यह है कि parseCfg के बाहर getCfg को कॉल करें और परिणाम को parseCfg पर भेज दें। फिर parseCfg एक Config लौटा सकता है, लेकिन Either String Config को वापस करना अधिक समझदारी है ताकि eitherDecode से कोई भी पार्सिंग त्रुटि संरक्षित रहे। यह आपको parseCfg को eitherDecode रिटर्न वैल्यू पर config के fmap से ज्यादा कुछ नहीं के रूप में परिभाषित करने देता है।

परिणामी फ़ंक्शन इस तरह दिखता है:

parseCfg :: ByteString -> Either String Config
parseCfg json = config <$> eitherDecode json 

यह आपका प्रोग्राम है, जिसे उपरोक्त parseCfg के साथ चलाने के लिए संशोधित किया गया है।

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE TemplateHaskell #-}

import Data.ByteString.Lazy as B
import Data.Aeson 
import Data.Aeson.TH
import GHC.Generics
import Control.Monad.IO.Class

data CfgProperty = CfgProperty { 
                       env             :: String,
                       host            :: String,
                       port            :: String,
                       directory       :: String
                   } deriving (Show, Generic)

-- Instead of FromJSON, use deriveJSON for CfgProperty 
-- which allows changing of field labels from lower 
-- case to upper case.

$(deriveJSON 

      defaultOptions { fieldLabelModifier = let f "env" = "Env"
                                                f "host" = "Host"
                                                f "port" = "Port"
                                                f "directory" = "Directory"
                                                f other = other
                                             in f
                     } 

      ''CfgProperty
 )

data Connections = Connections { cfgProperty  :: CfgProperty
                   , connectionName :: String
                   } deriving (Show, Generic)

data Config = Config {connections :: [Connections]} deriving (Show, Generic)

data Cfg = Cfg { config :: Config } deriving (Show, Generic)

instance FromJSON Cfg
instance FromJSON Config
instance FromJSON Connections

jsonFile :: FilePath
jsonFile = "config/config.json"

getCfg :: IO ByteString
getCfg = B.readFile jsonFile

parseCfg :: ByteString -> Either String Config
parseCfg json = config <$> eitherDecode json 

main :: IO()
main = do
    json <- getCfg
    case parseCfg json of
        Left err ->  Prelude.putStrLn err
        Right cfg -> print cfg  -- do whatever you want with cfg here.

यहाँ एक संशोधित config.json है। मूल में दो config JSON रिकॉर्ड थे, जिनके बीच कुछ भी नहीं था जो वैध नहीं था JSON, और जो प्रश्न के लिए प्रासंगिक नहीं था।

{
  "config":
  {
    "connections": [
    {
      "cfgProperty":
      {
        "Env": "local",
        "Host": "localhost",
        "Port": "8001",
        "Directory": "/apps/workspace/hade/maps/src01_cvs"
      },
      "connectionName": "src01_cvs"
    },
    {
        "cfgProperty":
        {
          "Env": "local",
          "Host": "localhost",
          "Port": "8001",
          "Directory": "/apps/workspace/hade/maps/trg01_cvs"
        },
        "connectionName": "trg01_cvs"
      }
    ]
  }
}
1
Dave Compton 4 अप्रैल 2017, 21:48