gpt4 book ai didi

haskell - FromJSON 实例的单例、类型族和存在类型

转载 作者:行者123 更新时间:2023-12-04 19:53:22 26 4
gpt4 key购买 nike

首先简要概述我的一般问题然后显示我卡在哪里可能更容易。

我想接收一些单例索引类型的 JSON 列表,其中索引类型也具有关联的类型族。在代码中:

data MyType = MyValue1 | MyValue2
type family MyFamily (mt :: MyType) where
MyFamily MyValue1 = Int
MyFamily MyValue2 = Double
data InputType (mt :: MyType) = InputNoFamily | InputWithFamily (MyFamily mt)
data OutputType (mt :: MyType) = OutputNoFamily | OutputWithFamily (MyFamily mt)

通过存在量化,我应该能够隐藏变化的索引并且仍然能够获得值(使用一些类似延续的更高级别的类型函数 - 可能有一个更好的名称)。我最终会按照以下方式运行我的程序
JSON -> [Some InputType] -> [Some OutputType] -> JSON

在哪里 Some来自 exinst包,但也在下面重新定义。如果我不解析 MyFamily mt,我可以解析 JSON ,但我也无法找到从 JSON 解析它的最佳方法。

到目前为止我所拥有的如下:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE RankNTypes #-}

module SO where

import Data.Aeson
import Data.Singletons.TH
import GHC.Generics

$(singletons [d|
data MyType
= MyValue1
| MyValue2
| MyValue3
deriving (Show, Eq, Generic)
|])
instance FromJSON MyType

type family MyFamily (mt :: MyType) :: * where
MyFamily 'MyValue1 = Double
MyFamily 'MyValue2 = Double
MyFamily 'MyValue3 = Int

-- stolen from exinst package
data Some (f :: k -> *) =
forall a. Some (Sing a) (f a)

some :: forall (f :: k -> *) a. SingI a => f a -> Some f
some = Some (sing :: Sing a)

withSome :: forall (f :: k -> *) (r :: *). Some f -> (forall a. SingI a => f a -> r) -> r
withSome (Some s x) g = withSingI s (g x)

data MyCompoundType (mt :: MyType)
= CompoundNoIndex
| CompoundWithIndex (MyFamily mt)

deriving instance (Show (SMyType mt), Show (MyFamily mt)) => Show (MyCompoundType mt)

-- instance with no parsing of `MyFamily`
instance
forall (mt :: MyType).
( SingKind (KindOf mt)
, FromJSON (DemoteRep (KindOf mt))
) => FromJSON (Some MyCompoundType) where
parseJSON = withObject "MyCompoundType" $ \o -> do
mt :: MyType <- o .: "myType"
case toSing mt of
SomeSing (smt :: SMyType mt') -> case smt of
SMyValue1 -> return $ some (CompoundNoIndex :: MyCompoundType mt')
SMyValue2 -> return $ some (CompoundNoIndex :: MyCompoundType mt')
SMyValue3 -> return $ some (CompoundNoIndex :: MyCompoundType mt')

我显然需要添加一个 FromJSON (MarketIndex mt)约束,但我还需要能够将其绑定(bind)到 Some CompoundType我正在为其生成实例。
FromJSON (MyFamily mt) 的简单添加约束
instance
forall (mt :: MyType).
( SingKind (KindOf mt)
, FromJSON (DemoteRep (KindOf mt))
, FromJSON (MyFamily mt)
) => FromJSON (Some MyCompoundType) where
parseJSON = undefined

给出模棱两可的类型错误
Could not deduce (FromJSON (MyFamily mt0))
arising from the ambiguity check for an instance declaration
from the context (SingKind (KindOf mt),
FromJSON (DemoteRep (KindOf mt)),
FromJSON (MyFamily mt))
bound by an instance declaration:
(SingKind (KindOf mt), FromJSON (DemoteRep (KindOf mt)),
FromJSON (MyFamily mt)) =>
FromJSON (Some MyCompoundType)
at SO.hs:(57,3)-(61,39)
The type variable ‘mt0’ is ambiguous
In the ambiguity check for:
forall (mt :: MyType).
(SingKind (KindOf mt), FromJSON (DemoteRep (KindOf mt)),
FromJSON (MyFamily mt)) =>
FromJSON (Some MyCompoundType)
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
In the instance declaration for ‘FromJSON (Some (MyCompoundType))’

我可以看到类型检查器在谈论 mt0而不是 mt是个大问题,但我不知道如何哄它期待 mt在约束的右侧键入。

(我也意识到我没有包含 FromJSON (MyFamily mt) 实例,但如果类型检查器无法找出 mt ~ mt0 我认为目前不重要)。

希望有解决办法?

我花了相当多的时间尝试不同的事情,但有很多不同的事情发生(单例,存在主义等)。我正在慢慢地让自己达到某种程度的熟练程度,但我只是没有足够的知识或经验来确定他们是如何(或没有)促成这个问题的。

最佳答案

(我之前对你的 prior question 的回答在很大程度上适用于这里)。

你可以自由解析任何你想要的类型,你只需要证明一个特定的类型有 FromJSON实例。在这种情况下,您应该解析 MyFamily 的具体结果类型。 ,因为它们都有适当的实例。

instance FromJSON (Some MyCompoundType) where
parseJSON = withObject "MyCompoundType" $ \o -> do
cons :: String <- o .: "constructor"
mt :: MyType <- o .: "myType"
case toSing mt of
SomeSing smt ->
case cons of
"CompoundNoIndex" -> pure $ Some smt CompoundNoIndex
"CompoundWithIndex" -> case smt of
SMyValue1 -> Some SMyValue1 . CompoundWithIndex <$> o .: "field"
SMyValue2 -> Some SMyValue2 . CompoundWithIndex <$> o .: "field"
SMyValue3 -> Some SMyValue3 . CompoundWithIndex <$> o .: "field"

在这里,我假设有一些东西表明了编码的构造函数。当然,有许多用于编码和解码的替代格式。

或者,我们可以将量化约束的近似值放在一起,并更多地使用从 "myType" 解析的单例标签。 field :
import Data.Constraint -- from "constraints"
import Data.Proxy

data MyFamilySym :: TyFun MyType * -> *
type instance Apply MyFamilySym a = MyFamily a

class ForallInst (f :: TyFun k * -> *) (c :: * -> Constraint) where
allInst :: Proxy '(f, c) -> Sing x -> Dict (c (f @@ x))

instance ForallInst MyFamilySym FromJSON where
allInst _ SMyValue1 = Dict
allInst _ SMyValue2 = Dict
allInst _ SMyValue3 = Dict

instance FromJSON (Some MyCompoundType) where
parseJSON = withObject "MyCompoundType" $ \o -> do
cons :: String <- o .: "constructor"
SomeSing smt <- toSing <$> o .: "myType"
case cons of
"CompoundNoIndex" -> pure (Some smt CompoundNoIndex)
"CompoundWithIndex" ->
case allInst (Proxy :: Proxy '(MyFamilySym, FromJSON)) smt of
Dict -> Some smt . CompoundWithIndex <$> o .: "field"

这里的关键点是 MyFamilySym 的去功能化。和 Apply .它使我们能够有效地把 MyFamily进入实例头,否则会被 GHC 取缔。看到这个 blog postsingletons 中了解更多关于去功能化的信息.

对于类型族的量化实例,我们永远无法避免一件事:写出类型族的所有案例并为每个案例演示一个实例。 ForallInst解决方案也这样做,但至少它要求我们只写一次案例。

关于haskell - FromJSON 实例的单例、类型族和存在类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33021521/

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