2013-11-11 6 views
1

Я пытаюсь использовать Data.Map для привязки строк к функциям. Проблема, с которой я сталкиваюсь, заключается в том, что основная обработка ошибок в моей программе использует любую монаду, а Map.lookup вернет Maybe ([SomeVal] -> Either ValError SomeVal). Как я могу сделать Map.lookup играть красиво с Либо монадой в этом случае?Как использовать Data.Map.lookup в контексте любой монады?

apply :: String -> [SomeVal] -> Either ValError SomeVal 
apply s args = (Map.lookup s prims) >>= \fn -> fn args 

prims :: Map String ([SomeVal] -> Either ValError SomeVal) 
prims = Map.fromList 
    [("key", function) 
    ,("key2", function2) 
    ] 

::> apply "key" [val1, val2, val3] 

ответ

6

Другие ответы объяснили, как превратить Maybe в Either в целом. Однако для Data.Map конкретно, вместо Map.lookup вы можете использовать Map.findWithDefault, который не обертывается в Maybe и позволяет вам выбирать, что делать с несуществующими ключами.

1

Это зависит от того, как вы хотите обрабатывать корпус Nothing. Если вы хотите, чтобы утверждать, что ключ всегда будет там (плохая идея)

import Data.Maybe 
apply s args = (prims ! s) args 

Но умнее метод должен иметь здравое значение в том случае, если ничего не найдено. Скорее всего, вы хотите, чтобы некоторые ValError конструктору

ValError = ... 
     | NoKey String 
     ... 
apply s args = maybe (Left $ NoKey s) ($args) $ M.lookup s prims 

Или просто хотите сохранить Maybe

apply :: String -> [SomeVal] -> Maybe (Either ValError SomeVal) 

Любой из этих методов обеспечивают гораздо разумнее семантику в том случае, когда ключ не был найден. Вы выбираете главным образом предпочтение, если вы намерены сделать apply довольно фундаментальной частью вашего API настолько, чтобы ValError s не знал об этом, тогда 1 отлично работает.

В противном случае 2 более болезнен в использовании, но не требует изменения какого-либо существующего кода, который приятен.

+0

Мне нравится второй случай: возможно, это сложная небольшая функция ... рад, что я знаю об этом сейчас. – LeeG

8

Control.Error package имеет хорошую функцию

note :: e -> Maybe a -> Either e a 
note e Nothing = Left e 
note _ (Just a) = Right a 

, которая полезна для «модернизации» Maybe -как Неудачи Either -как из них.

lookupNote :: k -> Map.Map k v -> Either ValError v 
lookupNote k = note (Missing k) . lookup k 

Она также имеет много других, как это для отображения между трансформаторами Either и Maybe и общими MonadPlus экземплярами. Я очень рекомендую этот пакет.

+1

Я думаю, что это будет очень полезно запомнить в будущем, но Map.findWithDefault. Думаю, это самый чистый способ справиться с этим. – LeeG