2

为了FromRow在简单产品上一般派生 -kind-of-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可能会变得非常乏味且容易出错,所以我想我应该为类型添加一个default实现。Generic但是,它似乎获得了我需要提供给定类型的术语(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 会涵盖这个用例吗?

4

1 回答 1

2

您无需提供术语。您不需要 的值Rep a,只需将其作为类型进行检查,而无需使用from.

就此而言,您也不需要Proxy,在出现之前,这始终只是弥补 Haskell 不足的丑陋技巧TypeApplications

{-# 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]

现在,为了编写泛型实例,我们首先需要一个辅助类来执行类型级别的检查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)
于 2021-10-24T14:16:41.447 回答