В этом случае вы можете использовать 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
Затем
> 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 -> Maybe a', но' First. process :: PickType -> Сначала a', поэтому 'xs :: [PickType]'? – bheklilr
Это просто игрушечный пример, но я добавлю его в OP. – NioBium