2016-10-27 9 views
5

У меня есть эти три функцииЕсть ли предопределенный способ пропустить вычисления, которые приводят к Nothing?

a :: Int -> Maybe Int 
a i = if i < 100 then Just i else Nothing 

b :: Int -> Maybe Int 
b i = if i < 50 then Just i else Nothing 

c :: Int -> Maybe Int 
c i = if i > 0 then Just i else Nothing 

И я хочу, чтобы приковать их вместе, так что, когда результат одной функции приводит в Nothing вход этой функции вместо него возвращается.

я могу добиться этого с помощью этой функции:

import Data.Maybe (fromMaybe) 

e :: Int -> [Int -> Maybe Int] -> Int 
e i [] = i 
e i (f:fs) = e (fromMaybe i $ f i) fs 

-

*Main> e 75 [a,b,c] 
75 

Есть существующую функцию, экземпляр монады или другим способом в базовых библиотеках, которая демонстрирует такое поведение?

+0

Должно быть ясно, что вы не можете сделать это в целом, так как входные и выходные типы могут отличаться. Вы можете написать функцию типа '(a -> Maybe a) -> a -> a', но не' (a -> Maybe b) -> a -> b' –

+3

Я бы сначала обратил '[Int -> Возможно, Int] 'в' [Int -> Int] 'используя' fromMaybe' так же, как вы. После этого я составил список endos. – chi

+1

'foldr1 (> =>) [a, b, c]' –

ответ

4

Расширение моего комментария выше - этот подход не слишком отличается от кода, опубликованного OP.

Сначала определим, как включить функцию a -> Maybe a в a -> a, заменив входной сигнал на Nothing.

totalize :: (a -> Maybe a) -> (a -> a) 
totalize f x = fromMaybe x (f x) 

Затем мы используем выше: мы делаем каждую функцию «общий» (что означает бездекомпрессионные Nothing с), завернуть его в качестве Endo, то мы составляем список эндоморфизмов (mconcat является композицией в Endo моноиде).

e :: [a -> Maybe a] -> a -> a 
e = appEndo . mconcat . map (Endo . totalize) 

или даже (как предложено ниже)

e :: Foldable t => t (a -> Maybe a) -> a -> a 
e = appEndo . foldMap (Endo . totalize) 
+2

'e = appEndo. foldMap (Endo. totalize) '? – user3237465

+0

Вы также можете сделать это без абзаца 'Endo':' compose = foldr (.) Id; e = составить. карта суммировать'. –

0

Вы ищете монаду?

*Main> let f x = a x >>= b >>= c >> return x 
*Main> f 1 
Just 1 
*Main> f 100 
Nothing 
*Main> 

Затем, если результат не будет ничего, мы можем добраться до желаемого конечного состояния с fromMaybe (или просто maybe и id, то же самое):

*Main> let g x = maybe x id (f x) 
*Main> g 100 
100 
+1

Это делает что-то отличное от кода OP. Код OP, когда 'a x' возвращает' Nothing', подает 'x' в' b'. Монадическая цепочка не делает этого. – chi

2

Ну, вы можете создать a -> a из a -> Maybe a:

repair :: (a -> Maybe a) -> a -> a 
repair f x = fromMaybe x (f x) 

После этого, вы можете просто объединить (.) и repair:

andThen :: (a -> Maybe a) -> (a -> Maybe a) -> a -> a 
andThen f g = repair g . repair f 

Но нет никакой функции библиотеки для этого, так как не существует общий способ, чтобы получить значение из Monad.