在這種情況下,你可以實際使用的newtypes
包更一般地解決這個問題:
process :: Node -> Maybe String
process (Pick xs) = ala' First foldMap process xs
process (Join xs) = liftM os_path_join (mapM process xs)
process (Name x) = Just x
process (Given x) = x
你甚至可以有一個更寬泛的版本,需要一個Newtype n (Maybe String)
像
process'
:: (Newtype n (Maybe String), Monoid n)
=> (Maybe String -> n) -> Node -> Maybe String
process' wrapper (Pick xs) = ala' wrapper foldMap (process' wrapper) xs
process' wrapper (Join xs) = liftM os_path_join (mapM (process' wrapper) xs)
process' wrapper (Name x) = Just x
process' wrapper (Given x) = x
Then
> let processFirst = process' First
> let processLast = process' Last
> let input = Pick [Given Nothing, Name "bar", Given (Just "foo"), Given Nothing]
> processFirst input
Just "bar"
> ProcessLast input
Just "foo"
至於它是如何工作的解釋,ala'
功能需要一個NEWTYPE包裝,以確定Newtype
實例來使用,在這種情況下,我們想成爲foldMap
功能:
foldMap :: (Monoid m, Foldable t) => (a -> m) -> t a -> m
因爲foldMap f
結束是一個普遍的mconcat . map f
而不是僅僅是列表的Foldable
類型,然後一個函數用作掛鉤到高階函數的「預處理器」被傳遞到ala'
(foldMap
),然後在這種情況下一些Foldable t => t Node
來處理。如果您不想要預處理步驟,則只需使用ala
,其中id
用於其預處理器。使用此功能有時會因爲其複雜的類型而變得困難,但作爲文檔顯示foldMap
中的示例通常是一個不錯的選擇。
這樣做的動力是,如果你想寫自己newtype
包裝爲Maybe String
:
newtype FirstAsCaps = FirstAsCaps { getFirstAsCaps :: Maybe String }
firstAsCaps :: Maybe String -> FirstAsCaps
firstAsCaps = FirstAsCaps . fmap (fmap toUpper)
instance Monoid FirstAsCaps where
mempty = firstAsCaps Nothing
mappend (FirstAsCaps f) (FirstAsCaps g)
= FirstAsCaps $ ala First (uncurry . on (<>)) (f, g)
instance Newtype FirstAsCaps (Maybe String) where
pack = firstAsCaps
unpack = getFirstAsCaps
然後
> process' firstAsCaps input
Just "BAR"
聽起來像是「脅迫」。 – Zeta
'process'應該是什麼類型?有可能使用'newtype'包來隱藏大部分內容。我所能做的就是'Pick'必須屬於一個遞歸類型,因爲'Pick :: [a] - > PickType'和'process :: PickType - >也許a',而是'First。 process :: PickType - > First a',所以'xs :: [PickType]'? – bheklilr
這只是一個玩具的例子,但我將它添加到OP。 – NioBium