我正在使用esqueleto进行SQL查询,我有一个查询返回类型为(Value a, Value b, Value c)
的数据。我想从中提取(a, b, c)
。我知道我可以像这样使用模式匹配:
let (Value a, Value b, Value c) = queryResult
但我想避免为每个元组元素重复Value
。当元组具有更多元素(如10)时,这尤其令人讨厌。有什么方法可以简化这个吗?有没有我可以使用的功能:
let (a, b, c) = someFunction queryResult
答案 0 :(得分:2)
该库似乎具有unValue功能,因此您只需选择一种映射任意长度元组的方法。然后someFunction可以成为
import Control.Lens (over, each)
someFunction = (over each) unValue
如果您想尝试其他方式来映射元组而不依赖镜头,可以查看以下问题:Haskell: how to map a tuple?
编辑:正如danidiaz所指出的,这只适用于最多8个字段长的元组。我不确定是否有更好的方法来概括它。
答案 1 :(得分:2)
如果您的元组具有所有相同的元素类型:
all3 :: (a -> b) -> (a, a, a) -> (b, b, b)
all3 f (x, y, z) = (f x, f y, f z)
这个案例可以用镜头抽象,使用@ {1}},如@ Zpalmtree的回答所述。
但是如果你的元组有不同的元素类型,你可以使用over each
扩展名使这个函数的f
参数变为多态:
RankNTypes
然后假设你有all3 :: (forall a. c a -> a) -> (c x, c y, c z) -> (x, y, z)
all3 f (x, y, z) = (f x, f y, f z)
,你可以写:
unValue :: Value a -> a
但是,如果您有大元组,则需要编写单独的函数(a, b, c) = all3 unValue queryResult
,all4
,...,all5
。在这种情况下,您可以通过使用Template Haskell生成它们来减少样板。这是Haskell中通常避免使用大元组的部分原因,因为它们很难处理并且不能轻易地抽象出来。
答案 2 :(得分:1)
base
的 Data.Coerce
提供coerce
,其作为您的someFunction
。
coerce
"交换" newtype
为他们包装的基础类型(反之亦然)。即使它们被深深地包裹在其他类型中,这也可以工作。这也是零开销,因为newtype
具有与它们包装的类型完全相同的运行时表示。
如果你感兴趣的话,你可以在the Wiki page上阅读关于类型变量角色的更多复杂性,但是这样的应用程序变得很简单,因为包使用&# 34;默认" Value
类型变量参数的作用。