我可以在数据构造函数中的数据上转换视图模式吗?

时间:2018-02-08 04:48:43

标签: haskell

我的代码中有一堆复杂的数据构造函数,我希望借助新的Pattern Synonyms语言扩展稍微理顺一下。但是,在我看来,如果没有专门的视图函数来删除手头的特定数据构造函数,我就无法实现它,只是将一些常用的库函数应用于内部数据。我希望我可以对构造函数进行模式匹配,然后将通常的库函数作为视图应用,从而摆脱了显式定义专用视图函数的需要。唉,我无法弄清楚如何做到这一点。

编写专用视图函数的不太明显的缺点是,如果模式匹配失败,则会出现提及视图函数名称的错误,从而挫败了模式同义词的抽象。所以我不能在我的视图函数中只有一个模式匹配失败,但我宁愿显式返回一个Maybe值,这样我就无法匹配模式本身的Just。这正是样板。

对于在这种情况下如何最好地安排代码,我将不胜感激,包括将模式同义词用于这种方式是否真的是一种好的做法。

这是我专门针对这个问题编写的示例代码,与我实际编写的代码非常相似。我尽力简明扼要。我还到处都提出了评论和例子 - 我希望它能让读者感到更加恼火。您也可以立即在ghci中运行代码。

{-# LANGUAGE
    PatternSynonyms
  , ViewPatterns
  #-}

module PatternSynonym where


-- ### Type definitions.

-- | This wrapper may have been introduced to harden type safety, or define a
--   typeclass instance. Its actual purpose does not matter for us here.
newtype Wrapped a = Wrap { unWrap :: a } deriving (Eq, Show)

-- | This data type exemplifies a non-trivial collection of things.
data MaybeThings a = Some [a] | None deriving (Eq, Show)

-- | This type synonym exemplifies a non-trivial collection of things that are
--   additionally wrapped.
type MaybeWrappedThings a = MaybeThings (Wrapped a)


-- ### An example of what we may want to do with our types:

-- | This is a function that does useful work on plain (not Wrapped!) Ints.
doSomething :: [Int] -> [Int]
doSomething = filter even
-- ^
--   λ doSomething [2, 3, 5]
--   [2]

-- | This is the example data we may have.
example :: MaybeWrappedThings Int
example = Some [Wrap 2, Wrap 3, Wrap 5]

-- | This is a function that must only accept a collection of wrapped things,
--   but it has to unwrap them in order to do something useful.
getThings :: MaybeWrappedThings Int -> [Int]
getThings (Some wxs) = doSomething xs
  where xs = unWrap <$> wxs
getThings None       = [ ]
-- ^
--   λ getThings example
--   [2]


-- ### An example of how we may simplify the same logic with the help of
--   pattern synonyms.

-- | This helper function is necessary (?) in order for it to be possible to
--   define the pattern synonym.
unWrapAll :: MaybeWrappedThings a -> Maybe [a]
unWrapAll (Some wxs) = Just (unWrap <$> wxs)
unWrapAll None = Nothing

-- | This is the pattern synonym that allows us to somewhat simplify getThings.
pattern Some' :: [a] -> MaybeWrappedThings a
pattern Some' xs <- (unWrapAll -> Just xs)
  where Some' = Some . fmap Wrap

-- | This is how we may define our data with the pattern synonym.
--   It's got linearly less lexemes!
example' :: MaybeWrappedThings Int
example' = Some' [2, 3, 5]
-- ^
--   λ example == example'
--   True

-- | This is how our function may look with the pattern synonym.
getThings' :: MaybeWrappedThings Int -> [Int]
getThings' (Some' xs) = doSomething xs
getThings' None       = [ ]
-- ^
--   λ getThings' example'
--   [2]
--
--   λ getThings' example' == getThings example
--   True

1 个答案:

答案 0 :(得分:2)

Haskell 有什么不能吗?

我想这个:

pattern Some' xs <- Some (coerce -> xs)

- 完美。

感谢@chi代表coerce和@ li-yao-xia,感谢代码在与Some数据构造函数匹配后查看变量。

这非常直观,我甚至不怀疑Haskell可以做这么多。