這似乎是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」的定義,期望的輸入/輸出以及任何錯誤消息。 –