这似乎是Alternative
的工作。 Maybe
's Alternative
instance实施左偏选择 - 其<|>
选择第一个非Nothing
值。
import Control.Applicative
import Data.Semigroup
data Foo = Foo {
bar :: Maybe Int,
baz :: Maybe String
}
我要去实现一个Semigroup
实例Foo
又带动<|>
逐点在记录字段。因此,操作x <> y
覆盖y
的字段,其中匹配的非Nothing
字段的值为x
。 (您也可以使用the First
monoid,它做同样的事情。)
instance Semigroup Foo where
f1 <> f2 = Foo {
bar = bar f1 <|> bar f2,
baz = baz f1 <|> baz f2
}
ghci> let defaultFoo = Foo { bar = Just 2, baz = Just "default" }
ghci> let overrides = Foo { bar = Just 8, baz = Nothing }
ghci> overrides <> defaultFoo
Foo {bar = Just 8, baz = Just "default"}
请注意,您不需要为这个镜头,虽然他们也许能帮助你做出的(<>)
一点更简洁的实现。
当用户给你一个部分填充的Foo
时,你可以通过追加缺省的Foo
来填写剩下的字段。
fillInDefaults :: Foo -> Foo
fillInDefaults = (<> defaultFoo)
一个有趣的事情,你可以用这个做的是因素Maybe
出Foo
的定义。
{-# LANGUAGE RankNTypes #-}
import Control.Applicative
import Data.Semigroup
import Data.Functor.Identity
data Foo f = Foo {
bar :: f Int,
baz :: f String
}
的Foo
我上面本来写现在等效Foo Maybe
。但是现在,您可以表达像“此Foo
已将其所有字段填入”的不变量,而不会复制Foo
本身。
type PartialFoo = Foo Maybe -- the old Foo
type TotalFoo = Foo Identity -- a Foo with no missing values
的Semigroup
情况下,只依靠的Alternative
上Maybe
的情况下,保持不变,
instance Alternative f => Semigroup (Foo f) where
f1 <> f2 = Foo {
bar = bar f1 <|> bar f2,
baz = baz f1 <|> baz f2
}
,但你现在可以概括defaultFoo
到任意Applicative
。
defaultFoo :: Applicative f => Foo f
defaultFoo = Foo { bar = pure 2, baz = pure "default" }
现在,随着Traversable
一点点灵感的分类废话,
-- "higher order functors": functors from the category of endofunctors to the category of types
class HFunctor t where
hmap :: (forall x. f x -> g x) -> t f -> t g
-- "higher order traversables",
-- about which I have written a follow up question: https://stackoverflow.com/q/44187945/7951906
class HFunctor t => HTraversable t where
htraverse :: Applicative g => (forall x. f x -> g x) -> t f -> g (t Identity)
htraverse eta = hsequence . hmap eta
hsequence :: Applicative f => t f -> f (t Identity)
hsequence = htraverse id
instance HFunctor Foo where
hmap eta (Foo bar baz) = Foo (eta bar) (eta baz)
instance HTraversable Foo where
htraverse eta (Foo bar baz) = liftA2 Foo (Identity <$> eta bar) (Identity <$> eta baz)
fillInDefaults
可以调整,以表达恒定所产生的Foo
不缺少任何值。
fillInDefaults :: Alternative f => Foo f -> f TotalFoo
fillInDefaults = hsequence . (<> defaultFoo)
-- fromJust (unsafely) asserts that there aren't
-- any `Nothing`s in the output of `fillInDefaults`
fillInDefaults' :: PartialFoo -> TotalFoo
fillInDefaults' = fromJust . fillInDefaults
可能对您所需要的东西过度矫枉过正,但它仍然非常整齐。
我无法理解你想要做什么。请包括“名词”和“HStoreList”的定义,期望的输入/输出以及任何错误消息。 –