安全记录字段查询

时间:2017-02-27 17:15:10

标签: haskell

是否有一种干净的方法可以避免以下样板:

给定记录数据类型定义....

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扩展或我忽略的其他东西。

2 个答案:

答案 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}},用于选择StringA构造函数中的B,并且不会在{C中执行任何操作1}} case。

ghci> :i name
name :: Traversal' T String  -- Defined at test.hs:11:1

因此,我们可以使用Traversal'中的the ^? operatorpreview的翻译同义词)来提取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

请注意,AB生成Value String值,而C生成Value ()。定义函数时

name :: Value String -> String

它明确指出您只能传递一个包含字符串的值。因此,您只能在AB值上进行模式匹配。这对于避免在代码中需要Maybe非常有用。