2015-12-30 4 views
5

Я просто проработал несколько простых упражнений в haskell и задавался вопросом, существует ли простой способ преобразования инструкции if-then-else в тип Maybe: Nothing возвращается, если условие ложно, и Just вход если условие истинно.Есть ли бесплатный способ конвертировать условную проверку в тип Maybe типа ввода?

Короче говоря, учитывая некоторые из них:

maybeIf :: (a -> Bool) -> a -> Maybe a 
maybeIf cond a = if cond a then Just a else Nothing 

Есть ли реализация, которая является точкой, свободной по отношению к a? Я также смотрел более конкретную версию, a -> Maybe a, и чувствую, что может быть ответ где-то в Control.Arrow. Однако, поскольку Maybe является типом данных, а операторы if-else управляют потоком данных, я не уверен, есть ли чистый способ сделать это.

ответ

7

Вы можете импортировать find из Data.Foldable, а затем это довольно просто:

import Data.Foldable(find) 

maybeIf cond = find cond . Just 

Функция find не сложно, так что вы могли бы довольно легко определить он сам менее общий, с точки зрения Maybe, но это не " t на самом деле так отличается от вашей собственной реализации maybeIf, поэтому вы можете не получить много, в зависимости от того, почему вы хотели это сделать.

6

Главное, что делается для того, чтобы сделать эту точку свободной: if/then/else. Вы можете определить if' комбинатора, или вы можете использовать эту обобщенную версию, что я определяю и часто используете:

ensure p x = x <$ guard (p x) 

Стандартных инструменты дают последовательные версии точечной бесплатно в

ensure p = ap (<$) (guard . p) 
ensure = ap (<$) . (guard .) 

хотя я действительно не думаю, что они лучше, чем точечные версии.

+2

'Data.Bool' имеет' bool'. – dfeuer

+1

Очень интересно! Я думаю, что мне нравится этот ответ наилучшим образом, поскольку он имеет самое существенное, чтобы учиться простым и лаконичным образом. Я собираюсь дать это @Peter, так как его ответ является самым читаемым, и я предполагаю, что я попросил только огласить его. – stites

3

Если мы выбираем церковную-кодирование для Booleans ...

truth :: Bool -> a -> a -> a 
truth True t f = t 
truth False t f = f 

Тогда мы можем написать точечно бесплатно maybeIf в Applicative стиле.

maybeIf :: (a -> Bool) -> a -> Maybe a 
maybeIf = liftA3 truth <*> pure Just <*> pure (pure Nothing) 

Некоторых интуитивные ...

f <$> m₁ <*> … <*> mₙ = \x -> f (m₁ x) … (mₙ x) 
liftAₙ f <$> m₁ <*> … <*> mₙ = \x -> f <$> m₁ x <*> … <*> mₙ x 

Вот рендеринг в формате PNG из выше «интуиции», в случае, если ваши установленные шрифты не поддерживают необходимые символы Юникода.

enter image description here

И поэтому:

liftA3 truth <*> pure Just <*> pure (pure Nothing) 
= liftA3 truth <$> id <*> pure Just <*> pure (pure Nothing) 
= \p -> truth <$> id p <*> (pure Just) p <*> (pure (pure Nothing)) p 
= \p -> truth <$> p <*> Just <*> pure Nothing 
= \p -> \a -> truth (p a) (Just a) ((pure Nothing) a) 
= \p -> \a -> truth (p a) (Just a) Nothing 
+0

Я не вижу некоторых персонажей! После «Некоторые интуиции ...» индексы кода кода (?), Которые не являются «1», помечены вопросительными знаками в боксе. – stites

+1

@stites Я добавил рендеринг, который вы должны видеть. – erisco

2

После dfeuer's lead (и используя новое имя Даниэля Вагнера для этой функции),

import Data.Bool (bool) 
--   F T  
-- bool :: a -> a -> Bool -> a 

ensure :: (a -> Bool) -> a -> Maybe a 
ensure p x = bool (const Nothing) Just (p x) x 

ensure p = join (bool (const Nothing) Just . p) 
      = bool (const Nothing) Just =<< p 

ensure  = (bool (const Nothing) Just =<<) 

join является монадической функцией, join :: Monad m => m (m a) -> m a, но для функций это просто

join k x = k x x 
(k =<< f) x = k (f x) x 

join принимаются как замену на W combinator в бесплатном коде.

Вы только хотели его точечными бесплатно по аргументу значение, но это легко преобразовать уравнение join дальнейшего (читаемость результата еще одна проблема в целом), а

  = join ((bool (const Nothing) Just .) p) 
      = (join . (bool (const Nothing) Just .)) p 

Действительно,

#> (join . (bool (const Nothing) Just .)) even 3 
Nothing 

#> (bool (const Nothing) Just =<<) even 4 
Just 4 
+1

@dfeuer Я отредактировал, спасибо. Получающееся выражение является незаметным, но по крайней мере теперь оно не имеет смысла. –

+1

Хм .... Интересно, есть ли смысл в обобщении 'обеспечить p = join (bool (const empty) pure. P)'. – dfeuer

+0

@dfeuer приятно! это намного яснее ... и с новой базой это то же самое, что и отличный Дэниел Вагнер (guard. p', так как теперь ['guard :: Alternative f => Bool -> f()'] (http : //hackage.haskell.org/package/base-4.8.1.0/docs/Control-Monad.html#v: guard)! (то есть не 'MonadPlus') –