In questo caso si può effettivamente utilizzare il pacchetto newtypes
per risolvere questo problema più genericamente:
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
si potrebbe anche avere una versione più generica che prende un Newtype n (Maybe String)
come
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
Quindi
> 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"
come spiegazione per come funziona, la funzione ala'
prende un involucro Newtype per determinare l'istanza di Newtype
da usare, una funzione che in questo caso vogliamo essere foldMap
:
foldMap :: (Monoid m, Foldable t) => (a -> m) -> t a -> m
dal foldMap f
finisce essendo un tipo generalizzato mconcat . map f
su Foldable
anziché solo liste, quindi una funzione da utilizzare come "preprocessore" per l'aggancio alla funzione di ordine superiore passata a ala'
(foldMap
), quindi in questo caso alcuni Foldable t => t Node
da elaborare. Se non si desidera eseguire la fase di pre-elaborazione, utilizzare semplicemente ala
, che utilizza id
per il relativo preprocessore. L'uso di questa funzione a volte può essere difficile a causa del suo tipo complesso, ma come gli esempi nella documentazione mostrano foldMap
è spesso una buona scelta.
La potenza di questo è che se si voleva scrivere il proprio newtype
wrapper per 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
Poi
> process' firstAsCaps input
Just "BAR"
Sembra "coercizione". – Zeta
Qual è il tipo di 'processo' che dovrebbe essere? Potrebbe essere possibile usare il pacchetto 'newtype' per nascondere la maggior parte di questo. Tutto ciò che posso fare è che 'Pick' deve appartenere a un tipo ricorsivo, poiché' Pick :: [a] -> PickType', e 'process :: PickType -> Forse a', ma' Primo. process :: PickType -> First a', quindi 'xs :: [PickType]'? – bheklilr
È solo un esempio di giocattolo, ma lo aggiungerò all'OP. – NioBium