2016-11-22 2 views
1

Я новичок в Haskell и FP, и я проработал через LYAH и другие источники, но «научиться делать». Я пытаюсь написать небольшую программу, включающую Разбор JSON. Тем не менее, я закодировал себя в углу и не могу выбраться. Мой код вымощен из разных учебных пособий, и я чувствую, что я все еще «мысленно рассуждаю» о том, как правильно сочетать его, но я не получил необходимого прорыва, чтобы заставить его работать.Функция map to newtype, определенная как список структур данных

Во-первых, это сокращенная версия многоуровневого файла JSON, это прогноз погоды от API Underground Underground, сокращенный до трех часов.

{ 
    "response": { 
     "version": "0.1", 
     "termsofService": "http://www.wunderground.com/weather/api/d/terms.html", 
     "features": { 
      "hourly": 1 
     } 
    }, 
    "hourly_forecast": [{ 
     "FCTTIME": { 
      "hour": "8", 
      "epoch": "1479736800", 
      "pretty": "8:00 AM CST on November 21, 2016" 
     }, 
     "temp": { 
      "english": "27", 
      "metric": "-3" 
     }, 
     "condition": "Partly Cloudy" 
    }, { 
     "FCTTIME": { 
      "hour": "9", 
      "epoch": "1479740400", 
      "pretty": "9:00 AM CST on November 21, 2016" 
     }, 
     "temp": { 
      "english": "32", 
      "metric": "0" 
     }, 
     "condition": "Partly Cloudy" 
    }, { 
     "FCTTIME": { 
      "hour": "10", 
      "epoch": "1479744000", 
      "pretty": "10:00 AM CST on November 21, 2016" 
     }, 
     "temp": { 
      "english": "35", 
      "metric": "2" 
     }, 
     "condition": "Clear" 
    }] 
} 

Далее, вот моя программа Haskell. Я успешно разбора JSON в newtype под названием ForecastPointCollection, который определяется как List из WeatherPoint, который является data структурой различных вещей, которые пришли из файла JSON. Но тогда я не могу понять, как получить список [WeatherPoint] (см. Комментарии к коду). Как тест на «что-то делать» со списком, я хочу преобразовать температуру Celcius в Kelvin и получить новый List, с которым я могу работать (вывод в JSON, сделайте show, независимо от того).

{-# LANGUAGE OverloadedStrings #-} 
-- {-# LANGUAGE RecordWildCards #-} 
{-# LANGUAGE ScopedTypeVariables #-} 

module Main where 

import   Data.Aeson 
import   Data.Aeson.Types 

import   Control.Applicative ((<$>), (<*>)) 
import   Control.Monad  (mzero) 

import qualified Data.ByteString.Lazy as BSL 
import qualified Data.Text   as T 
import qualified Data.Vector   as V 

type MetricTemperature = Int 
type KelvinTemperature = Int 

newtype ForecastPointCollection = ForecastPointCollection 
    {forecastpointcollection :: [WeatherPoint]} deriving Show 

data WeatherPoint = WeatherPoint 
    { epoch  :: T.Text 
    , prettyTime :: T.Text 
    , tempMetric :: MetricTemperature 
    , condition :: T.Text 
    } deriving Show 

instance FromJSON ForecastPointCollection where 
    parseJSON (Object o) = 
    ForecastPointCollection <$> o .: "hourly_forecast" 
    parseJSON _ = mzero 

data ProcessedWeatherPoint = ProcessedWeatherPoint 
    { newEpoch  :: T.Text 
    , newPrettyTime :: T.Text 
    , newTempKelvin :: KelvinTemperature 
    , newCondition :: T.Text 
    } deriving Show 

instance FromJSON WeatherPoint where 
    parseJSON = 
    withObject "Root Object Arbitrary Name" $ \o -> do 
    fctO <- o .: "FCTTIME" 
    epoch <- fctO .: "epoch" -- contained within FCTTIME 
    pretty <- fctO .: "pretty" -- contained within FCTTIME 
    tempO <- o .: "temp" 
    metric <- tempO .: "metric" -- contained within temp 
    condition <- o .: "condition" -- at top level under hourly_forecast 
    return $ WeatherPoint epoch pretty (read metric) condition 
    -- parseJSON _ = mzero 

kelvinizeTemp :: MetricTemperature -> KelvinTemperature 
kelvinizeTemp x = x + 273 -- hey, close enough 

adjustTemp :: Maybe ForecastPointCollection -> [ProcessedWeatherPoint] 
adjustTemp Nothing = [] 
adjustTemp x = [] -- HERE IS WHERE I AM LOSING MY WAY! 
        -- HOW CAN I WALK THROUGH THE LIST INSIDE ForecastPointCollection 
        -- TO map kelvinizeTemp ACROSS THAT LIST AND 
        -- GET A [ProcessedWeatherPoint] LIST BACK TO PLAY WITH? 

getSampleForecast = BSL.readFile "/home/mypath/test/forecastsubmit.json" 

main = do 
    textOfJson <- getSampleForecast 
    let (forecasts2 :: Maybe ForecastPointCollection) = decode textOfJson 
    case forecasts2 of 
    Just (ForecastPointCollection forecasts2) -> do 
     putStrLn ("Success!") 
     putStrLn . show $ forecasts2 
    _ -> putStrLn "Could not parse ForecastPointCollection JSON correctly." 
    -- So far so good, we've extracted data from the JSON and stored it in memory. 
    -- But now, how can we manipulate that data and start doing stuff with it? 
    -- Currently, the "adjustTemp" function returns an empty list no matter what. 
    let (processed2 :: [ProcessedWeatherPoint]) = adjustTemp forecasts2 
    putStrLn ("More success (OK, not really, yet)!") 
    putStrLn . show $ processed2 

Любые советы, оцененные. Должен ли я не делать ForecastPointCollection a newtype? Где я идиоматик и где я просто идиот? :-p

Обновление, основанное на ответе: для потомков здесь возможно (рабочая) реализация вновь созданной функции processWeatherPoint. Части структуры data следует рассматривать как функцию!

processWeatherPoint :: WeatherPoint -> ProcessedWeatherPoint 
processWeatherPoint x = ProcessedWeatherPoint 
    (epoch x) 
    (prettyTime x) 
    (kelvinizeTemp (tempMetric x)) 
    (condition x) 

kelvinizeTemp :: MetricTemperature -> KelvinTemperature 
kelvinizeTemp x = x + 273 -- this works OK because both types are type of Int 

ответ

3

Это должно быть достаточно, чтобы определить функцию ...

processWeatherPoint :: WeatherPoint -> ProcessedWeatherPoint 

... извлечь поле с [WeatherPoint] из Newtype и карта функции над списком:

adjustTemp :: Maybe ForecastPointCollection -> [ProcessedWeatherPoint] 
adjustTemp Nothing = [] 
adjustTemp (Just (ForecastPointCollection points)) = processWeatherPoint <$> points 

Альтернатива сопоставлению с образцом на ForecastPointCollection использует средство доступа к записи для поля. Это было бы особенно полезно, если вы не намерены экспортировать конструктор:

adjustTemp :: Maybe ForecastPointCollection -> [ProcessedWeatherPoint] 
adjustTemp Nothing = [] 
adjustTemp (Just forecast) = processWeatherPoint <$> forecastpointcollection forecast 

возможно более удобный способ записи определение только выше включает в себя использование функции maybe вместо того, чтобы делать явного анализа случая на Maybe:

adjustTemp :: Maybe ForecastPointCollection -> [ProcessedWeatherPoint] 
adjustTemp = maybe [] (fmap processWeatherPoint . forecastpointcollection) 
+0

Этот ответ был чрезвычайно полезен (как было позднее «Just» edit, чтобы помочь ему скомпилировать), и я отметил его как принятый ответ. Спасибо, добрые Хаскеллеры! –

Смежные вопросы