You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Haskell Servant中如何为含Vector字段的类型同时派生FromRow与FromForm?

解决方案

针对你遇到的Vector String无法同时满足FromRow(postgresql-simple)和FromForm(Servant)的问题,有以下几种可行方案:

方案一:自定义Vector String的FromHttpApiData实例

既然[String]已经有现成的FromHttpApiData实例,我们可以基于它实现Vector String的实例,直接复用List的解析逻辑再转成Vector:

import Data.Vector (Vector)
import qualified Data.Vector as V
import Web.HttpApiData (FromHttpApiData(..))

instance FromHttpApiData (Vector String) where
  parseQueryParam = fmap V.fromList . parseQueryParam
  parseUrlPiece = fmap V.fromList . parseUrlPiece
  parseHeader = fmap V.fromList . parseHeader

实现这个实例后,你就可以为Reading派生Generic的FromForm实例了,所有字段的类型都满足FromHttpApiData要求。

方案二:参数化Reading类型的authors字段

通过将authors的类型抽象为参数,你可以分别为数据库和API场景定义专用的类型别名,并派生对应的实例:

import Data.Vector (Vector)
import qualified Data.Vector as V
import Web.FormUrlEncoded (FromForm)
import Database.PostgreSQL.Simple.FromRow (FromRow)
import Data.Time.Calendar (Day)

data Reading a = Reading
  { title :: String
  , authors :: a
  , startDate :: Day
  , endDate :: Maybe Day
  } deriving (Eq, Show, Generic)

-- 数据库操作专用类型,派生FromRow
type DbReading = Reading (Vector String)
deriving instance FromRow DbReading

-- API表单专用类型,派生FromForm
type ApiReading = Reading [String]
deriving instance FromForm ApiReading

在API端点使用ApiReading接收请求,之后通过V.fromList将[String]转换为Vector String,再构造DbReading存入数据库;反之从数据库读取时,也可以用V.toList转换为List用于API响应。

方案三:直接使用[String]同时满足两个需求

实际上postgresql-simple的FromRow完全支持[String]类型——它会自动将PostgreSQL的数组类型映射为Haskell的List。如果你没有必须使用Vector的特殊需求(比如性能优化、特定容器操作),直接把authors的类型改为[String]是最简洁的方案:

data Reading = Reading
  { title :: String
  , authors :: [String]
  , startDate :: Day
  , endDate :: Maybe Day
  } deriving (Eq, Show, Generic, FromRow, FromForm)

这样无需额外代码,就能同时派生FromRow和FromForm实例,完美适配数据库和API场景。


内容的提问来源于stack exchange,提问作者Razumov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 12:32:47