Sal*_*Sal 3 haskell ghc-generics
在Sum of Products方法中,如何检索记录功能?下面带有record datatype(ghc 7.10.3)的示例代码:
{-# LANGUAGE DeriveGeneric #-}
import qualified GHC.Generics as GHC
import Generics.SOP
data Rec = Rec { frec :: Int, srec :: Maybe String}
deriving (Show, GHC.Generic)
instance Generic Rec -- empty
instance HasDatatypeInfo Rec
Run Code Online (Sandbox Code Playgroud)
让我们看看DataTypeInfo ghci提示:
*Main> datatypeInfo (Proxy :: Proxy Rec)
ADT "Main" "Rec" (Record "Rec" (FieldInfo "frec" :* (FieldInfo "srec" :* Nil)) :* Nil)
Run Code Online (Sandbox Code Playgroud)
我们看到frec并且srec都是FieldInfo具有FieldInfo带fieldName字符串的构造函数的类型.所以,我没有看到任何方法来获得实际的功能frec :: Rec -> Int和srec :: Rec -> Maybe String.我也查看了show示例,但它没有使用记录功能.
将会欣赏有关如何获取记录函数的指针(可能是类型的HList HList '[(Rec -> Int), (Rec -> Maybe String)])).
该问题的补遗
关于如何使用user2407038设计的方法从投影中获取函数,我陷入类型结.所以,我想进一步补充这个问题:我们如何使用构造函数SOP方法构建一个类似下面的Rec函数 - 我们同时使用记录字段名称和函数:
[ ("frec" ++) . show . frec, ("srec" ++) . show . srec]
Run Code Online (Sandbox Code Playgroud)
该generics-sop库实现了用于处理产品总和的通用组合器,因此您应该使用这些组合器编写这样的函数.
有一个问题 - generics-sop在类型级别上没有关于记录与构造函数的任何信息,因此您的函数仍然是部分的(除非您深入研究GHC泛型Rep).
对于这个例子,我将使用部分功能路由.
首先,您需要以下数据类型:
data (:*:) f g x = f x :*: g x deriving (Show, Eq, Ord, Functor)
Run Code Online (Sandbox Code Playgroud)
它似乎应该包含在库中,但它不是(或者我找不到它).
函数的类型将是
recordSelectors :: forall t r . (Code t ~ '[ r ], Generic t, HasDatatypeInfo t)
=> Proxy t -> Maybe (NP (FieldInfo :*: (->) t) r)
Run Code Online (Sandbox Code Playgroud)
约束Code t ~ '[ r ]简单地说生产表示的总和t是单个列表(一个构造函数).返回类型是(也许)一个在列表上的产品r(记录字段类型的列表),其中有一个FieldInfo x和t -> x每种类型x在r.
一个实现是
case datatypeInfo (Proxy :: Proxy t) of
ADT _ _ (Record _ fields :* Nil) -> Just $
hzipWith (\nm (Fn prj) -> nm :*: (unI . prj . K . (\(Z x) -> x) . unSOP . from))
fields
projections
_ -> Nothing
Run Code Online (Sandbox Code Playgroud)
这里函数确定给定的数据类型实际上是一个记录,否则返回Nothing.如果它是一个记录,则压缩到记录字段和projections(由库定义),它定义了任意通用产品的投影,这基本上只NP '[ Code Rec -> Int, Code Rec -> Maybe String ]适用于您的类型.剩下的就是from用每个投影组成函数以获得"真实"投影.其余的(Fn,unSOP等等)只是身份.
事实证明,你只需要记录投影功能,没有功能名称,这甚至更简单.现在函数不是局部的 - 任何一个构造函数类型都有"记录投影".
recordSelectors' :: forall t r . (Code t ~ '[ r ], Generic t)
=> Proxy t -> NP ((->) t) r
recordSelectors' _ = hmap (\(Fn prj) -> unI . prj . K . (\(Z x) -> x) . unSOP . from)
projections
Run Code Online (Sandbox Code Playgroud)