Более короткий способ написать этот код

В коде Haskell очень часто появляется следующий шаблон. Есть ли более короткий способ написать его?

if pred x
then Just x
else Nothing

Ответы

Ответ 1

Вы ищете mfilter в Control.Monad:

mfilter :: MonadPlus m => (a -> Bool) -> m a -> m a

-- mfilter odd (Just 1) == Just 1
-- mfilter odd (Just 2) == Nothing

Обратите внимание, что если условие не зависит от содержимого MonadPlus, вы можете написать вместо этого:

"foo" <$ guard (odd 3) -- Just "foo"
"foo" <$ guard (odd 4) -- Nothing

Ответ 2

Hm... Вы ищете комбинатор, который принимает a, функцию a -> Bool и возвращает a Maybe a. Стоп! Время Google. Полное совпадение отсутствует, но find довольно близко:

find :: (a -> Bool) -> [a] -> Maybe a

Я сомневаюсь, что вы действительно можете найти свою функцию где-нибудь. Но почему бы не определить его самостоятельно?

ifMaybe :: (a -> Bool) -> a -> Maybe a
ifMaybe f a | f a = Just a
ifMaybe _ _       = Nothing

Ответ 3

Использование:

(?:) (5>2) (Just 5,Nothing)

из Data.Bool.HT.

Ответ 4

Вы можете использовать guard для достижения такого поведения:

guard (pred x) >> return x

Это такое полезное поведение, которое я даже определил ensure в своем собственном маленьком наборе кода для одноразовых (у всех есть такая вещь, правда?):

ensure p x = guard (p x) >> return x

Ответ 5

f pred x = if pred x then Just x else Nothing

Учитывая указанное выше определение, вы можете просто написать:

f pred x

Конечно, это ничем не отличается от Даниэля Вагнера ensure или FUZxxl ifMaybe. Но это просто f, что делает его самым коротким, и это определение - именно тот код, который вы дали, что делает его наиболее легко проверенным.;)

Некоторые ghci, просто для удовольствия

ghci> let f pred x = if pred x then Just x else Nothing
ghci> f (5>) 2
Just 2
ghci> f (5>) 6
Nothing

Если вы не могли сказать, это не очень серьезный ответ. Остальные немного более проницательны, но я не мог удержаться от щекотливого ответа на "сделать этот код короче".

Ответ 6

Обычно я большой поклонник очень общего кода, но на самом деле я нахожу эту точную функцию полезной достаточно часто, специализированной для Maybe, поэтому я использую ее вместо использования guard, mfilter и как.

Имя, которое я использую для него, это justIf, и я обычно использовал его для выполнения таких действий:

∀x. x ⊢ import Data.List
∀x. x ⊢ unfoldr (justIf (not . null . snd) . splitAt 3) [1..11]
[[1,2,3],[4,5,6],[7,8,9]]

В основном, в тех случаях, когда в составном выражении должно выполняться какая-то элементарная фильтрация или проверка, поэтому Maybe используется для указания результата предиката.

Для специализированной версии, такой как это, действительно мало что можно сделать, чтобы сделать ее короче. Это уже довольно просто. Там тонкая грань между краткими и просто играя в гольф ваш код для подсчета персонажа, и для чего-то такого простого я бы не стал беспокоиться о попытке "улучшить" его...