是否有一种干净的方法可以避免以下样板:
给定记录数据类型定义....
data Value = A{ name::String } | B{ name::String } | C{}
编写一个安全返回name
getName :: Value -> Maybe String
getName A{ name=x } = Just x
getName B{ name=x } = Just x
getName C{} = Nothing
我知道你可以用模板Haskell做到这一点,我正在寻找一个更清洁的解决方案,也许是GHC扩展或我忽略的其他东西。
答案 0 :(得分:5)
lens
Template Haskell helpers在遇到部分记录字段时做正确的事。
{-# LANGUAGE TemplateHaskell #-}
import Control.Applicative
import Control.Lens
data T = A { _name :: String }
| B { _name :: String }
| C
makeLenses ''T
这将生成一个名为name
的{{3}},用于选择String
和A
构造函数中的B
,并且不会在{C
中执行任何操作1}} case。
ghci> :i name
name :: Traversal' T String -- Defined at test.hs:11:1
因此,我们可以使用Traversal'
中的the ^?
operator(preview
的翻译同义词)来提取Maybe
名称。
getName :: T -> Maybe String
getName = (^? name)
您还可以为数据类型的构造函数设置Control.Lens.Fold
,然后使用Prism'
选择匹配的第一个。当构造函数的字段具有不同的名称时,此版本很有用,但您必须记住在添加构造函数时更新提取器函数。
makePrisms ''T
getName' :: T -> Maybe String
getName' t = t^?_A <|> t^?_B
lens
非常有用!
答案 1 :(得分:0)
你为什么不使用GADT?我不知道你是否对仅使用记录感兴趣。但是,我认为GADT为您的问题提供了一个干净的解决方案,因为您可以通过改进类型来限制构造函数的有效性。
{-# LANGUAGE GADTs #-}
module Teste where
data Value a where
A :: String -> Value String
B :: String -> Value String
C :: Value ()
name :: Value String -> String
name (A s) = s
name (B s) = s
请注意,A
和B
生成Value String
值,而C
生成Value ()
。定义函数时
name :: Value String -> String
它明确指出您只能传递一个包含字符串的值。因此,您只能在A
或B
值上进行模式匹配。这对于避免在代码中需要Maybe
非常有用。