gpt4 book ai didi

haskell - 使用 `generics-sop` 导出投影函数

转载 作者:行者123 更新时间:2023-12-04 11:32:42 24 4
gpt4 key购买 nike

我将如何推导函数

getField :: (Generic a, HasDatatypeInfo a) => Proxy (name :: Symbol) -> a -> b
使用类型级字符串( Symbol )从任意记录投影字段,使用 generics-sop图书馆?
这类似于 Retrieving record function in generic SOP ,但我有以下问题:
  • OP 没有解释如何走最后一英里来获得我想要的签名。
  • OP 定义了复杂的专用助手类型,我很想避免
  • 给定的解决方案仅在运行时出错,但编译时匹配应该是可能的,因为类型级别 DataTypeInfo通过 DatatypeInfoOf 提供类型家庭(很高兴拥有,但不是必需的)。
  • lens-sop还包 seems to do something similar ,但我不知道如何让它为我工作。
    我也更喜欢使用 IsProductType 的解决方案类型类。

    最佳答案

    我知道这是一个困惑的答案,并不是您真正想要的,但这是我现在能做的最好的。请注意,这适用于所有构造函数都具有指定字段名称的产品类型和总和类型。
    我认为通过将名称查找与产品处理的其余部分分开,这可能会有所简化。即:使用数据类型信息来计算字段编号(作为一元自然数),然后使用该数字来挖掘代码。不幸的是,generics-sop似乎没有非常好的工具来处理列表压缩,所以我最终“手工”做了很多。

    {-# language EmptyCase, GADTs, TypeFamilies, DataKinds, TypeOperators, RankNTypes #-}
    {-# language UndecidableInstances, UndecidableSuperClasses #-}
    {-# language AllowAmbiguousTypes, TypeApplications, MultiParamTypeClasses,
    FlexibleContexts, FlexibleInstances, MagicHash, UnboxedTuples, ScopedTypeVariables #-}
    {-# language ConstraintKinds #-}
    {-# OPTIONS_GHC -Wall #-}

    module Data.Proj where
    import Data.Kind (Type, Constraint)
    import Generics.SOP
    import Generics.SOP.Type.Metadata as GST
    import GHC.TypeLits
    import Data.Type.Equality (type (==))

    -- This is what you were looking for, but slightly more flexible.
    genericPrj :: forall s b a.
    ( Generic a
    , HasFieldNS s b (GetConstructorInfos (DatatypeInfoOf a)) (Code a))
    => a -> b
    genericPrj a = case genericPrj# @s a of (# b #) -> b

    -- This version lets you force the *extraction* of a field without
    -- forcing the field itself.
    genericPrj# :: forall s b a.
    ( Generic a
    , HasFieldNS s b (GetConstructorInfos (DatatypeInfoOf a)) (Code a))
    => a -> (# b #)
    genericPrj# a = case from a of
    SOP xs -> extraction @s @b @(GetConstructorInfos (DatatypeInfoOf a)) @(Code a) xs

    -- | Extract info about the constructor(s) from 'GST.DatatypeInfo'.
    type family GetConstructorInfos (inf :: GST.DatatypeInfo) :: [GST.ConstructorInfo] where
    GetConstructorInfos ('GST.ADT _ _ infos _) = infos
    GetConstructorInfos ('GST.Newtype _ _ info) = '[info]

    class HasFieldNS (s :: Symbol) b (cis :: [GST.ConstructorInfo]) (code :: [[Type]]) where
    extraction :: NS (NP I) code -> (# b #)
    instance HasFieldNS s b cis '[] where
    extraction x = case x of
    instance (HasFieldNP' s b r c, HasFieldNS s b cis cs, rec ~ 'GST.Record q r, VerifyRecord rec)
    => HasFieldNS s b (rec ': cis) (c ': cs) where
    extraction (Z x) = extractIt @s @b @rec @c x
    extraction (S x) = extraction @s @b @cis @cs x

    type family VerifyRecord rec :: Constraint where
    VerifyRecord ('GST.Record _ _) = ()
    VerifyRecord _ = TypeError ('Text "Constructor is not in record form.")

    -- | Given info about a constructor, a list of its field types, and the name and
    -- type of a field, produce an extraction function.
    class HasFieldNP (s :: Symbol) b (ci :: GST.ConstructorInfo) (fields :: [Type]) where
    extractIt :: NP I fields -> (# b #)
    instance (HasFieldNP' s b fi fields, ci ~ 'GST.Record _cn fi)
    => HasFieldNP s b ci fields where
    extractIt = extractIt' @s @_ @fi

    class HasFieldNP' (s :: Symbol) b (fi :: [GST.FieldInfo]) (fields :: [Type]) where
    extractIt' :: NP I fields -> (# b #)

    class TypeError ('Text "Can't find field " ':<>: 'ShowType s)
    => MissingField (s :: Symbol) where
    impossible :: a

    instance MissingField s => HasFieldNP' s b fi '[] where
    extractIt' = impossible @s ()

    instance HasFieldNP'' s b (fi == s) field fis fields =>
    HasFieldNP' s b ('GST.FieldInfo fi ': fis) (field ': fields) where
    extractIt' = extractIt'' @s @b @(fi == s) @field @fis @fields

    class HasFieldNP'' (s :: Symbol) b (match :: Bool) (field :: Type) (fis :: [GST.FieldInfo]) (fields :: [Type]) where
    extractIt'' :: NP I (field ': fields) -> (# b #)
    instance b ~ field => HasFieldNP'' _s b 'True field fis fields where
    extractIt'' (I x :* _) = (# x #)
    instance (HasFieldNP' s b fis fields) => HasFieldNP'' s b 'False _field fis fields where
    extractIt'' (_ :* fields) = extractIt' @s @b @fis fields
    例子
    data Foo
    = Foo {blob :: Int, greg :: String}
    | Bar {hello :: Char, blob :: Int}
    deriveGeneric ''Foo

    genericPrj @"blob" (Foo 12 "yo") ===> 12
    genericPrj @"blob" (Bar 'x' 5) ===> 5
    genericPrj# @"blob" (Bar 'x' 5) ===> (# 5 #)

    myAbsurd :: Void -> a
    myAbsurd = genericPrj @"whatever"

    data Booby a
    = Booby {foo :: a}
    | Bobby {bar :: a}
    deriveGeneric ''Booby

    genericPrj @"foo" (Booby 'a')
    -- Type error because Bobby has no foo field

    关于haskell - 使用 `generics-sop` 导出投影函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66992276/

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