我正在编写一个使用blaze-html来构造HTML文档的Haskell程序。当一个title :: Maybe Text
不Nothing
时,我想从中产生一个h1
。我可以这样做:
case title of
Just t -> H.h1 $ toHtml t
_ -> return ()
或者,在Maybe
的特定情况下,我可以这样做:
when (title /= Nothing) $ H.h1 $ toHtml $ fromJust title
我可以拥有(也许带有 GHC 扩展(看起来更像第二个示例的东西,但对于任意ADT?类似于部分模式匹配的东西,只是默认为return ()
.我想象这样的语法:
-- imaginary syntax
when title of Just t -> H.h1 $ toHtml t
你想要的是以下类型的函数:
Applicative f => Maybe a -> (a -> f ()) -> f ()
使用Hoogle,您可以找到几个选项。所以这个函数最常见的名称似乎是whenJust
.例如,您可以在relude
库中找到它。
如果您对此问题的通用解决方案感兴趣,最接近的方法就是使用lens
库中的棱镜。下面我提供了完整的代码和后续说明:
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE TemplateHaskell #-}
module Prism where
import Control.Lens (Prism', makePrisms, preview)
data Foo
= Bar String
| Baz Int
| Quux Bool
makePrisms ''Foo
whenMatched :: Applicative f => Prism' s a -> s -> (a -> f ()) -> f ()
whenMatched prism val f = case preview prism val of
Nothing -> pure ()
Just a -> f a
函数makePrisms
是一个Haskell宏,需要启用TemplateHaskell
扩展。此函数将生成名称为_Bar
、_Baz
和_Quux
的棱镜。您可以在 GHCi 中检查它们的类型:
λ: :t _Bar
_Bar
:: (profunctors-5.3:Data.Profunctor.Choice.Choice p,
Applicative f) =>
p String (f String) -> p Foo (f Foo)
λ: :t _Baz
_Baz
:: (profunctors-5.3:Data.Profunctor.Choice.Choice p,
Applicative f) =>
p Int (f Int) -> p Foo (f Foo)
类型可能看起来很可怕,但这是因为lens
库的内部结构复杂。我已经实现了whenMatched
函数,它应该有助于以更简单的方式使用棱镜。以下是使用它的方法:
λ: whenMatched _Bar (Bar "foo") putStrLn
foo
λ: whenMatched _Bar (Baz 42) putStrLn