2015-12-25 6 views
3

У меня есть две функции:Объединяя (а -> Может быть) и (а -> а) функции

f :: a -> Maybe a 
g :: a -> a 

Я хочу создать такую ​​функцию:

h :: a -> Maybe a 

h x 
| isJust(f x) = Just (g $ fromJust(f x)) 
| otherwise = Nothing 

Как я могу это сделать более элегантным способом?

+7

Элегантный способ был опубликован ниже. Тем не менее, я хочу напомнить, что использование 'isJust/fromJust' является, пожалуй, наименее элегантным. В самом деле, если вы забудете проверку 'isJust',' fromJust' может привести к сбою вашей программы. Лучше всего было бы использовать сопоставление шаблонов, например, 'case f x of Nothing -> Nothing; Просто y -> Просто $ g y'. Также см. [Boolean blindness] (https://existentialtype.wordpress.com/2011/03/15/boolean-blindness/) для получения дополнительной информации. – chi

+1

Это не правильный тип. 'g (fromJust (f x)): a'. Я думаю, что вы написали 'Just (g (fromJust (fx))'. –

ответ

13

Поскольку вы добавили этот вопрос с :

h :: a -> Maybe a 
h = fmap g . f 

Для объяснения:

f   ::       a -> Maybe a 
g   ::  a ->  a 
fmap g  :: Maybe a -> Maybe a 
(.)   :: (Maybe a -> Maybe a) -> (a -> Maybe a) -> (a -> Maybe a) 
(.) (fmap g) ::       (a -> Maybe a) -> (a -> Maybe a) 
fmap g . f ::           (a -> Maybe a) 
h   ::           a -> Maybe a 

Обратите внимание, что (.) «ы и fmap g» ы типа на самом деле более общий:

(.) :: (b -> c) -> (a -> b) -> (a -> c) 
-- b in this case is Maybe a 
-- c in this case is Maybe a 

fmap g :: Functor f => f a -> f a 
-- f in this case is Maybe 

Однако, вы можете также шаблон матч на результат f:

h x = 
    case f x of 
    Just k -> Just (g k) 
    _  -> Nothing 

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

+0

Я думаю, что это самый идиоматический способ и выражает идею наиболее четко из всех опций. – dfeuer

4

Почему бы не просто так:

h :: a -> Maybe a 
h x = fmap g (f x) 

Или версия оператора:

h :: a -> Maybe a 
h x = g <$> f x 
5

Имея

fmap2 :: (Functor g, Functor f) => (a -> b) -> g (f a) -> g (f b) 
fmap2 = fmap . fmap 

здесь смешной способ:

h :: a -> Maybe a 
h = fmap2 g f 

fmap2 g f ~> fmap (fmap g) f ~> fmap g . f ~> \x -> fmap g (f x)

Экземпляр Functor ((->) r) используется здесь: fmap может быть использован вместо (.).

+0

Учитывая, что пользователь (вероятно) новый для Haskell, вы добавили бы какое-то объяснение? Не все знакомы с «Functor ((->) r)'. Использование 'b ~ a',' fa ~ Maybe a' и 'gc ~ a -> c' сделает это намного понятнее для них – Zeta

+0

@Zeta, я хотел сделать его похожим на головоломку – user3237465

+0

, поэтому в 'fmap. fmap' левый' fmap' находится в '(a ->)', потому что 'fmap g' всегда является функцией , что бы ни был функтором. Правый 'fmap' здесь находится в' Maybe'. (и в комбинаторе совы он находится в (r ->) тоже. nice.) –