gpt4 book ai didi

haskell - 如何根据 Reader 环境中的设置有条件地解析 JSON?

转载 作者:行者123 更新时间:2023-12-02 14:11:55 25 4
gpt4 key购买 nike

有没有办法将读取器环境传递给 Aeson 的 JSON(反)序列化函数?这是一个现实生活中的例子,说明为什么需要这样做?

-- JSON instances for decimal -- ORPHAN instances

defaultPrecision :: Word8
defaultPrecision = fromInteger 2

instance ToJSON Data.Decimal.Decimal where
toJSON d = toJSON $ show d

instance FromJSON Data.Decimal.Decimal where
-- TODO: New problem! How do we get the precision dynamically, based on
-- the currency settings of the logged-in user
parseJSON (Number n) = return $ Data.Decimal.realFracToDecimal defaultPrecision n
parseJSON x = fail $ "Expectig a number in the JSON to parse to a Decimal. Received " ++ (show x)

最佳答案

如果实例依赖于某些运行时值,那么您真正想要的是在运行时创建实例的能力。您可以为 Reader 实现 FromJSON,就像在 gist 中所做的那样。 。但正如您正确注意到的那样,您无法对 ToJSON 执行相同的操作,因为您不知道此精度。最简单的解决方案是将精度存储为数据类型中的单独字段。像这样:

data DecimalWithPrecision = MkDWP
{ value :: Decimal
, precision :: Word8
}

如果您将此数据类型存储在数据库中并在用户登录后查询它,那么这是最简单的解决方案,不需要您使用类型级别的技巧。

如果您事先不知道精度,例如用户通过控制台输入精度(我不知道为什么,但让我们假设这一点),那么这对您不起作用。众所周知,“类型类只是数据类型的语法糖”,您可以将 Money_ToJSON/FromJSON 约束替换为 JsonDict方式如下:

newtype Money_ = Money_ (Reader Word8 Decimal)

data JsonDict a = JsonDict
{ jdToJSON :: a -> Value
, jdParseJSON :: Value -> Parser a
}

mkJsonDict :: Word8 -- precision
-> JsonDict Money_

您可以使用上下文中的 Word8 即时创建此类字典(或类似的字典),然后将其传递给需要它的函数。请参阅this blog post通过 Gabriel Gonzalez了解详情。

如果您确实想在实例内实现 toJSON 实现,您可以使用 reflection图书馆。精度是一个自然数,它使您能够使用该库。使用它,您基本上可以像以前的方法一样在运行时创建实例,但您仍然拥有类型类。请参阅this blog post其中应用了类似的技术来使任意实例依赖于运行时值。在您的情况下,这将如下所示:

{-# LANGUAGE ScopedTypeVariables  #-}
{-# LANGUAGE UndecidableInstances #-}

import Control.Monad.Reader (Reader, ask)

import Data.Aeson (FromJSON (..), Result (..), ToJSON (..),
Value, fromJSON, withNumber)
import Data.Aeson.Types (Parser)
import Data.Decimal (Decimal, realFracToDecimal)
import Data.Proxy (Proxy (..))
import Data.Reflection (Reifies (reflect), reify)
import Data.Word8 (Word8)

newtype PreciseDecimal s = PD Decimal

instance Reifies s Int => FromJSON (PreciseDecimal s) where
parseJSON = withNumber "a number" $ \n -> do
let precision = fromIntegral $ reflect (Proxy :: Proxy s)
pure $ PD $ realFracToDecimal precision n

instance Reifies s Int => ToJSON (PreciseDecimal s) where
toJSON (PD decimal) =
let precision = reflect (Proxy :: Proxy s)
ratDec = realToFrac decimal :: Double
in toJSON ratDec -- use precision if needed

makeMoney :: Decimal -> Reader Word8 (Value, Decimal)
makeMoney value = do
precision <- fromIntegral <$> ask
let jsoned = reify precision $ \(Proxy :: Proxy s) ->
toJSON (PD value :: PreciseDecimal s)
let parsed = reify precision $ \(Proxy :: Proxy s) ->
let Success (PD res :: PreciseDecimal s)
= fromJSON jsoned in res
pure (jsoned, parsed)

然后你可以像这样运行它来测试:

ghci> runReader (makeMoney 3.12345) 2
(Number 3.12345,3.12)

关于haskell - 如何根据 Reader 环境中的设置有条件地解析 JSON?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44516946/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com