注意:我在下面定义的类型只是为了这个问题的目的而提供的一个方便的例子;我确信我没有必要在 Haskell 中推出我自己的复数定义。
我不知道我在这里使用的术语是否正确,但下面的选择器 r
是我所说的“部分”记录选择器的示例:
data Complex = Polar { r :: Float, y :: Float }
| Rectangular { x :: Float, y :: Float }
deriving Show
r
是“部分”的,因为它不能应用于所有 Complex
值;例如
r $ Polar 3 0
-- 3.0
...但是
r $ Rectangular 3 0
-- *** Exception: No match in record selector r
然而,在这种情况下,r $ Rectangular x y
有一个合理的定义,即:
-- assuming {-# LANGUAGE RecordWildCards #-}
r :: Complex -> Float
r Rectangular { .. } = sqrt $ (x * x) + (y * y)
GHCi 拒绝 r
的定义,并出现 multiple declarations of ‘r’
错误。
有没有办法扩展 r
以便它可以应用于任何 Complex
值?
当然,我意识到我可以定义类似的东西
-- assuming {-# LANGUAGE RecordWildCards #-}
modulus :: Complex -> Float
modulus Polar { .. } = r
modulus Rectangular { .. } = sqrt $ (x * x) + (y * y)
...但我想知道是否可以扩展已经存在的选择器r
。
最佳答案
不,IMO 这样的记录选择器一开始就不应该被引入。我会这样写
type ℝ = Float -- Note that Double is usually more sensible
newtype S¹ = S¹ {ϑ :: ℝ} -- in [-π, π[
newtype ℝPlus = ℝPlus {posℝ :: ℝ} -- in [0, ∞[
data Complex = Polar ℝPlus S¹
| Rectangular ℝ ℝ
deriving Show
这样,部分记录选择器的形式就没有潜在的错误,也不会混淆要解包的内容等。即使对于这种“非记录类型”,您也可以编写自己的访问器,最好是镜头形式:
import Control.Lens
r :: Lens' Complex ℝPlus
r = lens get set
where get (Polar r _) = r
get (Rectangular x y) = ℝPlus . sqrt $ x^2 + y^2
set (Polar _ θ) r = Polar r θ
set (Rectangular x y) (ℝPlus r) = Rectangular (x * η) (y * η)
where η = r / sqrt (x^2 + y^2)
关于haskell - 如何将 "extend"设为一个(部分)记录选择器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41337704/