Составление двух функций, вызывающих ошибку, в Haskell

Задача, которую мне дали, говорит об этом:

Аналогично mapMaybe, определите функцию: composeMaybe :: (a->Maybe b) -> (b -> Maybe c) -> (a-> Maybe c), которая объединяет две функции, вызывающие ошибку.

Тип Maybe a и функция mapMaybe закодированы следующим образом:

data Maybe a = Nothing | Just a

mapMaybe g Nothing = Nothing
mapMaybe g (Just x) = Just (g x)

Я пробовал использовать такую ​​композицию:

composeMaybe f g = f.g

Но не компилируется.

Может ли кто-нибудь указать мне в правильном направлении?


person ChrisMacDee    schedule 14.01.2010    source источник
comment
Как вы научитесь, если другие сделают вашу домашнюю работу за вас?   -  person yairchu    schedule 14.01.2010
comment
Я прошу указатели, а не ответы   -  person ChrisMacDee    schedule 14.01.2010


Ответы (6)


Инструмент, который вы ищете, уже существует. В Control.Monad есть два оператора композиции Клейсли.

(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
(<=<) :: Monad m => (b -> m c) -> (a -> m b) -> a -> m c

Когда m = Maybe, реализация composeMaybe становится очевидной:

composeMaybe = (>=>)

Глядя на определение (>=>),

f >=> g     = \x -> f x >>= g

который вы можете встроить, если хотите думать об этом в своих собственных терминах как

composeMaybe f g x = f x >>= g

или что можно было бы записать в do-сахаре как:

composeMaybe f g x = do 
    y <- f x
    g y

В общем, я бы просто придерживался использования (>=>), у которого есть хорошие теоретические основания для существования, потому что он обеспечивает самый чистый способ сформулировать законы монад.

person Edward KMETT    schedule 14.01.2010
comment
Я предполагаю, что это домашнее задание, и в этом случае использование ›=›, вероятно, не считается допустимым решением. :-) - person Martijn; 15.01.2010
comment
да, это была проверка вчерашнего экзамена, мы не рассматривали монады, но, вероятно, будем делать их в расширенном функциональном программировании в следующем году! Так что еще пригодится! :) - person ChrisMacDee; 16.01.2010
comment
Martijn, правда, поэтому я открыл его, чтобы посмотреть на вывод. - person Edward KMETT; 16.01.2010

Прежде всего: во всяком случае, это должно быть g.f, а не f.g, потому что вам нужна функция, которая принимает тот же аргумент, что и f, и возвращает то же значение, что и g. Однако это не работает, потому что тип возвращаемого значения f не равен типу аргумента g (тип возвращаемого значения f содержит Maybe, а тип аргумента g — нет).

Итак, что вам нужно сделать, это: Определить функцию, которая принимает Maybe b в качестве аргумента. Если этот аргумент равен Nothing, он должен вернуть Nothing. Если аргумент равен Just b, он должен вернуть g b. composeMaybe должен возвращать состав функции с f.

person sepp2k    schedule 14.01.2010
comment
Эй, спасибо за помощь парню! :P Я сделал это из вашего ответа: g :: Может быть b -> Может быть b g Ничего = Ничего g (Просто x) = g(x) Но он говорит = не может построить бесконечный тип: b = Может быть b composeMaybe :: (a-› Возможно б) -> (б -> Возможно в) -> (а-> Возможно в) composeMaybe f g = g.f но не компилируется - person ChrisMacDee; 14.01.2010
comment
а) Сигнатура вспомогательной функции должна быть Maybe b -> Maybe c, потому что тип g — b -> Maybe c. б) Вы не должны вызывать вспомогательную функцию g, потому что вторая функция, предоставленная для composeMaybe, уже вызывается g. c) Вы должны определить вспомогательную функцию в определении composeMaybe, чтобы она имела доступ к функции g (в качестве альтернативы она могла бы принимать функцию g в качестве аргумента). - person sepp2k; 14.01.2010

Вот отличное руководство по монадам Haskell (и особенно монаде Maybe, который используется в первых примерах).

person 3lectrologos    schedule 14.01.2010
comment
sepp2k, вы можете ознакомиться с этим руководством. То, что вы описываете, является функцией композиции Клейсли для монады Maybe. Взгляните на модуль Control.Monad. - person Thiago Arrais; 14.01.2010
comment
ссылка больше недействительна, теперь ее можно найти здесь: wiki.haskell.org/All_About_Monads - person Nils Blum-Oeste; 12.07.2016

composeMaybe :: (a -> Maybe b)
             -> (b -> Maybe c)
             -> (a -> Maybe c)
composeMaybe f g = \x ->

Так как g принимает аргумент типа b, а f возвращает значение типа Maybe b, вам необходимо выполнить сопоставление с образцом для результата f x, если вы хотите передать этот результат в g.

                         case f x of
                              Nothing -> ...
                              Just y  -> ...
person Josh Lee    schedule 14.01.2010

Очень похожая функция уже существует — монадический оператор связывания, >>=. Его тип (для монады Maybe) — Maybe a -> (a -> Maybe b) -> Maybe b, и он используется следующим образом:

Just 100 >>= \n -> Just (show n) -- gives Just "100"

Это не совсем то же самое, что и ваша функция composeMaybe, которая принимает функцию, возвращающую Maybe вместо прямого значения Maybe в качестве первого аргумента. Но вы можете очень просто написать свою функцию composeMaybe с помощью этого оператора — это почти так же просто, как определение обычной функции компоновки, (.) f g x = f (g x).

person Chuck    schedule 14.01.2010

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

ghci> :t (>>=)
(>>=) :: (Monad m) => m a -> (a -> m b) -> m b

Порядок f и g является обратным для композиции, так как насчет лучшего имени?

thenMaybe :: (a -> Maybe b) -> (b -> Maybe c) -> (a -> Maybe c) 
thenMaybe f g = (>>= g) . (>>= f) . return

Учитывая следующие определения

times3 x = Just $ x * 3

saferecip x
  | x == 0 = Nothing
  | otherwise = Just $ 1 / x

можно, например,

ghci> saferecip `thenMaybe` times3 $ 4
Just 0.75
ghci> saferecip `thenMaybe` times3 $ 8
Just 0.375
ghci> saferecip `thenMaybe` times3 $ 0
Nothing
ghci> times3 `thenMaybe` saferecip $ 0
Nothing
ghci> times3 `thenMaybe` saferecip $ 1
Just 0.3333333333333333
person Greg Bacon    schedule 19.01.2010