使用泛型静态分析类型
Statically analyze type with Generics
为了在简单产品上为 FromRow
-种类class 通用派生实例,我想静态分析一个类型而不实际提供任何具体术语。
示例:
class FromRow a where
rowrep :: Proxy a -> [Maybe NativeType]
fromRow :: Statement -> IO a
data User = User
{ name :: String
, uid :: Int
, active :: Bool
} deriving (Show, Generic)
“技巧”是我在获取任何数据之前需要 rowrep - 可能会覆盖某些甚至所有列的默认值。在我想使用 rowrep 的时间点,我还没有术语,因此 Proxy
。编写 FromRow
的实例可能会变得非常乏味且容易出错,所以我想我会为 Generic
类型添加一个 default
实现。然而,它似乎得到了我需要提供给定类型的术语的通用表示(from :: a -> Rep a
),类型本身的知识是不够的。
事实上,我们可以看到这不仅仅是 API 的噱头,而且通用表示确实具有值:
> from (User "foo" 1 True)
M1 {unM1 = M1 {unM1 = M1 {unM1 = K1 {unK1 = "foo"}} :*: (M1 {unM1 = K1 {unK1 = 1}} :*: M1 {unM1 = K1 {unK1 = True}})}}
有没有办法使用 Generic
来分析事物的结构和类型,即我们不关心实际值的地方?如果做不到这一点,TH 会涵盖这个用例吗?
您无需提供术语。您不需要 Rep a
的值,您只需要将其作为 类型 进行检查,而无需使用 from
.
就此而言,您也不需要 Proxy
,在 TypeApplications
出现之前,这始终只是弥补 Haskell 不足的一个丑陋的 hack。
{-# LANGUAGE TypeFamilies, TypeApplications, AllowAmbiguousTypes
, ScopedTypeVariables, UnicodeSyntax, DefaultSignatures #-}
import Data.Kind (Type)
data NativeType = Intish | Floatish
class FromRow a where
rowrep :: [Maybe NativeType]
instance FromRow Int where
rowrep = [Just Intish]
现在,为了编写泛型实例,我们首先需要一个助手 class 来对 Rep
:
进行类型级检查
class GFromRow (g :: k -> Type) where
gRowrep :: [Maybe NativeType]
instance ∀ i c f . GFromRow f => GFromRow (M1 i c f) where
gRowRep = gRowRep @f
instance ∀ i c . FromRow c => GFromRow (K1 i c) where
gRowRep = rowRep @c
... -- instances for U1 and f:*:g
那么默认实现将是
class FromRow a where
rowrep :: [Maybe NativeType]
default rowrep :: GFromRow (Rep a) => [Maybe NativeType]
rowrep = gRowrep @(Rep a)
为了在简单产品上为 FromRow
-种类class 通用派生实例,我想静态分析一个类型而不实际提供任何具体术语。
示例:
class FromRow a where
rowrep :: Proxy a -> [Maybe NativeType]
fromRow :: Statement -> IO a
data User = User
{ name :: String
, uid :: Int
, active :: Bool
} deriving (Show, Generic)
“技巧”是我在获取任何数据之前需要 rowrep - 可能会覆盖某些甚至所有列的默认值。在我想使用 rowrep 的时间点,我还没有术语,因此 Proxy
。编写 FromRow
的实例可能会变得非常乏味且容易出错,所以我想我会为 Generic
类型添加一个 default
实现。然而,它似乎得到了我需要提供给定类型的术语的通用表示(from :: a -> Rep a
),类型本身的知识是不够的。
事实上,我们可以看到这不仅仅是 API 的噱头,而且通用表示确实具有值:
> from (User "foo" 1 True)
M1 {unM1 = M1 {unM1 = M1 {unM1 = K1 {unK1 = "foo"}} :*: (M1 {unM1 = K1 {unK1 = 1}} :*: M1 {unM1 = K1 {unK1 = True}})}}
有没有办法使用 Generic
来分析事物的结构和类型,即我们不关心实际值的地方?如果做不到这一点,TH 会涵盖这个用例吗?
您无需提供术语。您不需要 Rep a
的值,您只需要将其作为 类型 进行检查,而无需使用 from
.
就此而言,您也不需要 Proxy
,在 TypeApplications
出现之前,这始终只是弥补 Haskell 不足的一个丑陋的 hack。
{-# LANGUAGE TypeFamilies, TypeApplications, AllowAmbiguousTypes
, ScopedTypeVariables, UnicodeSyntax, DefaultSignatures #-}
import Data.Kind (Type)
data NativeType = Intish | Floatish
class FromRow a where
rowrep :: [Maybe NativeType]
instance FromRow Int where
rowrep = [Just Intish]
现在,为了编写泛型实例,我们首先需要一个助手 class 来对 Rep
:
class GFromRow (g :: k -> Type) where
gRowrep :: [Maybe NativeType]
instance ∀ i c f . GFromRow f => GFromRow (M1 i c f) where
gRowRep = gRowRep @f
instance ∀ i c . FromRow c => GFromRow (K1 i c) where
gRowRep = rowRep @c
... -- instances for U1 and f:*:g
那么默认实现将是
class FromRow a where
rowrep :: [Maybe NativeType]
default rowrep :: GFromRow (Rep a) => [Maybe NativeType]
rowrep = gRowrep @(Rep a)