使用泛型实现Applicative构建器样式

ron*_*ron 7 haskell ghc-generics

上下文

如果我们有

data Foo = Foo { x :: Maybe Int, y :: Maybe Text }
Run Code Online (Sandbox Code Playgroud)

我们已经可以在Applicative上下文(这里是IO)中将它构建为applicative-style

myfoo :: IO Foo
myfoo = Foo <$> getEnvInt "someX" <*> getEnvText "someY"
Run Code Online (Sandbox Code Playgroud)

问题

如果一个人喜欢明确地写出记录字段名称,那该怎么办?如:

myfoo = Foo { x = getEnvInt "someX", y = getEnvText "someY" }
Run Code Online (Sandbox Code Playgroud)

这不会发生变形.一个解决方案是

{-# LANGUAGE RecordWildCards #-}
myfoo = do
    x <- getEnvInt "someX"
    y <- getEnvText "someY"
    return $ Foo {..}
Run Code Online (Sandbox Code Playgroud)

哪个不错.但我想知道(此时只为了它自己)以下是否可行:

data FooC f = FooC { x :: f Int, y :: f Text }
type Foo = FooC Maybe

myfoo :: IO Foo
myfoo = genericsMagic $ FooC
    { x = someEnvInt "someX"
    , y = someEnvText "someY"
    }
Run Code Online (Sandbox Code Playgroud)

我相信它可以通过裸GHC.Generics模式匹配来完成,但这样就不具备类型安全性,所以我一直在寻找更强大的方法.我遇到了generics-sop,它将记录转换为异构列表,并带来了一个看似方便的hsequence操作.

我被卡住的地方

generics-sop将Applicative的类型存储在其异构列表的单独类型参数中,并且I在使用生成的转换时始终(标识).所以我需要映射hlist并I从有效移动Applicative的元素中移除所I提到的类型参数(它会Comp IO Maybe),所以我可以使用hsequence,最后添加回来I让我可以转回记录.

但我不知道如何为I删除/添加功能编写类型签名,该功能通过丢失/获取外部类型来传达各个hlist元素的类型一致地改变.这甚至可能吗?

dan*_*iaz 0

但我不知道如何为 I 删除/添加函数编写类型签名,该签名表示各个 hlist 元素的类型通过丢失/获得外部类型来一致更改。这可能吗?

我也不知道该怎么做。一种可能的解决方法(以一些样板为代价)是使用记录模式同义词直接构造乘积和表示,同时仍然能够使用命名字段:

{-# language DeriveGeneric #-}
{-# language TypeFamilies #-}
{-# language TypeOperators #-}
{-# language PatternSynonyms #-}

import Data.Text
import qualified GHC.Generics as GHC
import Generics.SOP
import Text.Read

data Foo = Foo { x :: Int, y :: Text } deriving (Show, GHC.Generic)

instance Generic Foo

pattern Foo' :: t Int -> t Text -> SOP t (Code Foo)
pattern Foo' {x', y'} = SOP (Z (x' :* y' :* Nil))

readFooMaybe :: SOP (IO :.: Maybe) (Code Foo)
readFooMaybe = Foo'
             {
                x' = Comp (fmap readMaybe getLine)
             ,  y' = Comp (fmap readMaybe getLine)
             }
Run Code Online (Sandbox Code Playgroud)

在 ghci 上测试:

ghci> hsequence' readFooMaybe >>= print
12
"foo"
SOP (Z (Just 12 :* (Just "foo" :* Nil)))
Run Code Online (Sandbox Code Playgroud)