Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / BasicTypes.hs @ f59cefcb

History | View | Annotate | Download (12.2 kB)

1 95e683c6 Petr Pudlak
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
2 93be1ced Iustin Pop
3 0c37d1e4 Iustin Pop
{-
4 0c37d1e4 Iustin Pop
5 1091021c Iustin Pop
Copyright (C) 2009, 2010, 2011, 2012 Google Inc.
6 0c37d1e4 Iustin Pop
7 0c37d1e4 Iustin Pop
This program is free software; you can redistribute it and/or modify
8 0c37d1e4 Iustin Pop
it under the terms of the GNU General Public License as published by
9 0c37d1e4 Iustin Pop
the Free Software Foundation; either version 2 of the License, or
10 0c37d1e4 Iustin Pop
(at your option) any later version.
11 0c37d1e4 Iustin Pop
12 0c37d1e4 Iustin Pop
This program is distributed in the hope that it will be useful, but
13 0c37d1e4 Iustin Pop
WITHOUT ANY WARRANTY; without even the implied warranty of
14 0c37d1e4 Iustin Pop
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 0c37d1e4 Iustin Pop
General Public License for more details.
16 0c37d1e4 Iustin Pop
17 0c37d1e4 Iustin Pop
You should have received a copy of the GNU General Public License
18 0c37d1e4 Iustin Pop
along with this program; if not, write to the Free Software
19 0c37d1e4 Iustin Pop
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 0c37d1e4 Iustin Pop
02110-1301, USA.
21 0c37d1e4 Iustin Pop
22 0c37d1e4 Iustin Pop
-}
23 0c37d1e4 Iustin Pop
24 0c37d1e4 Iustin Pop
module Ganeti.BasicTypes
25 93be1ced Iustin Pop
  ( GenericResult(..)
26 b74ebe44 Klaus Aehlig
  , genericResult
27 93be1ced Iustin Pop
  , Result
28 d71fbcc5 Agata Murawska
  , ResultT(..)
29 565821d1 Petr Pudlak
  , mkResultT
30 565821d1 Petr Pudlak
  , withError
31 565821d1 Petr Pudlak
  , withErrorT
32 f59cefcb Petr Pudlak
  , toError
33 565821d1 Petr Pudlak
  , toErrorStr
34 a87a017b Petr Pudlak
  , Error(..) -- re-export from Control.Monad.Error
35 0c37d1e4 Iustin Pop
  , isOk
36 0c37d1e4 Iustin Pop
  , isBad
37 9491766c Hrvoje Ribicic
  , justOk
38 ea128e20 Klaus Aehlig
  , justBad
39 0c37d1e4 Iustin Pop
  , eitherToResult
40 f3f76ccc Iustin Pop
  , annotateResult
41 565821d1 Petr Pudlak
  , annotateError
42 565821d1 Petr Pudlak
  , failError
43 565821d1 Petr Pudlak
  , catchErrorT
44 565821d1 Petr Pudlak
  , handleErrorT
45 b1c772fd Klaus Aehlig
  , iterateOk
46 2fc5653f Iustin Pop
  , select
47 2fc5653f Iustin Pop
  , LookupResult(..)
48 2fc5653f Iustin Pop
  , MatchPriority(..)
49 2fc5653f Iustin Pop
  , lookupName
50 2fc5653f Iustin Pop
  , goodLookupResult
51 2fc5653f Iustin Pop
  , goodMatchPriority
52 2fc5653f Iustin Pop
  , prefixMatch
53 2fc5653f Iustin Pop
  , compareNameComponent
54 4651c69f Jose A. Lopes
  , ListSet(..)
55 4651c69f Jose A. Lopes
  , emptyListSet
56 0c37d1e4 Iustin Pop
  ) where
57 0c37d1e4 Iustin Pop
58 25779212 Iustin Pop
import Control.Applicative
59 0c37d1e4 Iustin Pop
import Control.Monad
60 95e683c6 Petr Pudlak
import Control.Monad.Error.Class
61 d71fbcc5 Agata Murawska
import Control.Monad.Trans
62 2fc5653f Iustin Pop
import Data.Function
63 2fc5653f Iustin Pop
import Data.List
64 ea128e20 Klaus Aehlig
import Data.Maybe
65 95e683c6 Petr Pudlak
import Data.Monoid
66 4651c69f Jose A. Lopes
import Data.Set (Set)
67 4651c69f Jose A. Lopes
import qualified Data.Set as Set (empty)
68 4651c69f Jose A. Lopes
import Text.JSON (JSON)
69 4651c69f Jose A. Lopes
import qualified Text.JSON as JSON (readJSON, showJSON)
70 0c37d1e4 Iustin Pop
71 93be1ced Iustin Pop
-- | Generic monad for our error handling mechanisms.
72 93be1ced Iustin Pop
data GenericResult a b
73 93be1ced Iustin Pop
  = Bad a
74 93be1ced Iustin Pop
  | Ok b
75 139c0683 Iustin Pop
    deriving (Show, Eq)
76 0c37d1e4 Iustin Pop
77 b74ebe44 Klaus Aehlig
-- | Sum type structure of GenericResult.
78 b74ebe44 Klaus Aehlig
genericResult :: (a -> c) -> (b -> c) -> GenericResult a b -> c
79 b74ebe44 Klaus Aehlig
genericResult f _ (Bad a) = f a
80 b74ebe44 Klaus Aehlig
genericResult _ g (Ok b) = g b
81 565821d1 Petr Pudlak
{-# INLINE genericResult #-}
82 b74ebe44 Klaus Aehlig
83 93be1ced Iustin Pop
-- | Type alias for a string Result.
84 93be1ced Iustin Pop
type Result = GenericResult String
85 93be1ced Iustin Pop
86 93be1ced Iustin Pop
-- | 'Monad' instance for 'GenericResult'.
87 a87a017b Petr Pudlak
instance (Error a) => Monad (GenericResult a) where
88 0c37d1e4 Iustin Pop
  (>>=) (Bad x) _ = Bad x
89 0c37d1e4 Iustin Pop
  (>>=) (Ok x) fn = fn x
90 0c37d1e4 Iustin Pop
  return = Ok
91 a87a017b Petr Pudlak
  fail   = Bad . strMsg
92 0c37d1e4 Iustin Pop
93 93be1ced Iustin Pop
instance Functor (GenericResult a) where
94 a9ccc950 Iustin Pop
  fmap _ (Bad msg) = Bad msg
95 a9ccc950 Iustin Pop
  fmap fn (Ok val) = Ok (fn val)
96 a9ccc950 Iustin Pop
97 a87a017b Petr Pudlak
instance (Error a, Monoid a) => MonadPlus (GenericResult a) where
98 a87a017b Petr Pudlak
  mzero = Bad $ strMsg "zero Result when used as MonadPlus"
99 0c37d1e4 Iustin Pop
  -- for mplus, when we 'add' two Bad values, we concatenate their
100 0c37d1e4 Iustin Pop
  -- error descriptions
101 a87a017b Petr Pudlak
  (Bad x) `mplus` (Bad y) = Bad (x `mappend` strMsg "; " `mappend` y)
102 0c37d1e4 Iustin Pop
  (Bad _) `mplus` x = x
103 0c37d1e4 Iustin Pop
  x@(Ok _) `mplus` _ = x
104 0c37d1e4 Iustin Pop
105 a87a017b Petr Pudlak
instance (Error a) => MonadError a (GenericResult a) where
106 95e683c6 Petr Pudlak
  throwError = Bad
107 565821d1 Petr Pudlak
  {-# INLINE throwError #-}
108 95e683c6 Petr Pudlak
  catchError x h = genericResult h (const x) x
109 565821d1 Petr Pudlak
  {-# INLINE catchError #-}
110 95e683c6 Petr Pudlak
111 93be1ced Iustin Pop
instance Applicative (GenericResult a) where
112 25779212 Iustin Pop
  pure = Ok
113 25779212 Iustin Pop
  (Bad f) <*> _       = Bad f
114 25779212 Iustin Pop
  _       <*> (Bad x) = Bad x
115 25779212 Iustin Pop
  (Ok f)  <*> (Ok x)  = Ok $ f x
116 25779212 Iustin Pop
117 a87a017b Petr Pudlak
instance (Error a, Monoid a) => Alternative (GenericResult a) where
118 78209a84 Petr Pudlak
  empty = mzero
119 78209a84 Petr Pudlak
  (<|>) = mplus
120 78209a84 Petr Pudlak
121 d71fbcc5 Agata Murawska
-- | This is a monad transformation for Result. It's implementation is
122 d71fbcc5 Agata Murawska
-- based on the implementations of MaybeT and ErrorT.
123 565821d1 Petr Pudlak
--
124 565821d1 Petr Pudlak
-- 'ResultT' is very similar to @ErrorT@, but with one subtle difference:
125 565821d1 Petr Pudlak
-- If 'mplus' combines two failing operations, errors of both of them
126 565821d1 Petr Pudlak
-- are combined.
127 93be1ced Iustin Pop
newtype ResultT a m b = ResultT {runResultT :: m (GenericResult a b)}
128 d71fbcc5 Agata Murawska
129 95e683c6 Petr Pudlak
-- | Eliminates a 'ResultT' value given appropriate continuations
130 95e683c6 Petr Pudlak
elimResultT :: (Monad m)
131 95e683c6 Petr Pudlak
            => (a -> ResultT a' m b')
132 95e683c6 Petr Pudlak
            -> (b -> ResultT a' m b')
133 95e683c6 Petr Pudlak
            -> ResultT a m b
134 95e683c6 Petr Pudlak
            -> ResultT a' m b'
135 95e683c6 Petr Pudlak
elimResultT l r = ResultT . (runResultT . result <=< runResultT)
136 95e683c6 Petr Pudlak
  where
137 95e683c6 Petr Pudlak
    result (Ok x)   = r x
138 95e683c6 Petr Pudlak
    result (Bad e)  = l e
139 95e683c6 Petr Pudlak
{-# INLINE elimResultT #-}
140 95e683c6 Petr Pudlak
141 95e683c6 Petr Pudlak
instance (Monad f) => Functor (ResultT a f) where
142 95e683c6 Petr Pudlak
  fmap f = ResultT . liftM (fmap f) . runResultT
143 95e683c6 Petr Pudlak
144 a87a017b Petr Pudlak
instance (Monad m, Error a) => Applicative (ResultT a m) where
145 95e683c6 Petr Pudlak
  pure = return
146 95e683c6 Petr Pudlak
  (<*>) = ap
147 95e683c6 Petr Pudlak
148 a87a017b Petr Pudlak
instance (Monad m, Error a) => Monad (ResultT a m) where
149 a87a017b Petr Pudlak
  fail err = ResultT (return . Bad $ strMsg err)
150 274366e5 Agata Murawska
  return   = lift . return
151 95e683c6 Petr Pudlak
  (>>=)    = flip (elimResultT throwError)
152 95e683c6 Petr Pudlak
153 a87a017b Petr Pudlak
instance (Monad m, Error a) => MonadError a (ResultT a m) where
154 87f15934 Petr Pudlak
  throwError = ResultT . return . Bad
155 565821d1 Petr Pudlak
  catchError = catchErrorT
156 d71fbcc5 Agata Murawska
157 93be1ced Iustin Pop
instance MonadTrans (ResultT a) where
158 95e683c6 Petr Pudlak
  lift = ResultT . liftM Ok
159 d71fbcc5 Agata Murawska
160 a87a017b Petr Pudlak
instance (MonadIO m, Error a) => MonadIO (ResultT a m) where
161 d71fbcc5 Agata Murawska
  liftIO = lift . liftIO
162 d71fbcc5 Agata Murawska
163 a87a017b Petr Pudlak
instance (Monad m, Error a, Monoid a) => MonadPlus (ResultT a m) where
164 95e683c6 Petr Pudlak
  mzero = ResultT $ return mzero
165 95e683c6 Petr Pudlak
  -- Ensure that 'y' isn't run if 'x' contains a value. This makes it a bit
166 95e683c6 Petr Pudlak
  -- more complicated than 'mplus' of 'GenericResult'.
167 95e683c6 Petr Pudlak
  mplus x y = elimResultT combine return x
168 95e683c6 Petr Pudlak
    where combine x' = ResultT $ liftM (mplus (Bad x')) (runResultT y)
169 95e683c6 Petr Pudlak
170 a87a017b Petr Pudlak
instance (Monad m, Error a, Monoid a) => Alternative (ResultT a m) where
171 78209a84 Petr Pudlak
  empty = mzero
172 78209a84 Petr Pudlak
  (<|>) = mplus
173 78209a84 Petr Pudlak
174 565821d1 Petr Pudlak
-- | Changes the error message of a result value, if present.
175 565821d1 Petr Pudlak
-- Note that since 'GenericResult' is also a 'MonadError', this function
176 565821d1 Petr Pudlak
-- is a generalization of
177 565821d1 Petr Pudlak
-- @(Error e') => (e' -> e) -> GenericResult e' a -> GenericResult e a@
178 565821d1 Petr Pudlak
withError :: (MonadError e m) => (e' -> e) -> GenericResult e' a -> m a
179 565821d1 Petr Pudlak
withError f = genericResult (throwError . f) return
180 565821d1 Petr Pudlak
181 565821d1 Petr Pudlak
-- | Changes the error message of a @ResultT@ value, if present.
182 565821d1 Petr Pudlak
withErrorT :: (Monad m, Error e)
183 565821d1 Petr Pudlak
           => (e' -> e) -> ResultT e' m a -> ResultT e m a
184 565821d1 Petr Pudlak
withErrorT f = ResultT . liftM (withError f) . runResultT
185 565821d1 Petr Pudlak
186 87f15934 Petr Pudlak
-- | Lift a 'Result' value to any 'MonadError'. Since 'ResultT' is itself its
187 87f15934 Petr Pudlak
-- instance, it's a generalization of
188 87f15934 Petr Pudlak
-- @Monad m => GenericResult a b -> ResultT a m b@.
189 f59cefcb Petr Pudlak
toError :: (MonadError e m) => GenericResult e a -> m a
190 f59cefcb Petr Pudlak
toError = genericResult throwError return
191 f59cefcb Petr Pudlak
{-# INLINE toError #-}
192 d71fbcc5 Agata Murawska
193 565821d1 Petr Pudlak
-- | An alias for @withError strMsg@, which is often used to lift a pure error
194 565821d1 Petr Pudlak
-- to a monad stack. See also 'annotateResult'.
195 565821d1 Petr Pudlak
toErrorStr :: (MonadError e m, Error e) => Result a -> m a
196 565821d1 Petr Pudlak
toErrorStr = withError strMsg
197 565821d1 Petr Pudlak
198 565821d1 Petr Pudlak
-- | Converts a monadic result with a 'String' message into
199 565821d1 Petr Pudlak
-- a 'ResultT' with an arbitrary 'Error'.
200 565821d1 Petr Pudlak
--
201 565821d1 Petr Pudlak
-- Expects that the given action has already taken care of any possible
202 565821d1 Petr Pudlak
-- errors. In particular, if applied on @IO (Result a)@, any exceptions
203 565821d1 Petr Pudlak
-- should be handled by the given action.
204 565821d1 Petr Pudlak
--
205 565821d1 Petr Pudlak
-- See also 'toErrorStr'.
206 565821d1 Petr Pudlak
mkResultT :: (Monad m, Error e) => m (Result a) -> ResultT e m a
207 565821d1 Petr Pudlak
mkResultT = ResultT . liftM toErrorStr
208 565821d1 Petr Pudlak
209 93be1ced Iustin Pop
-- | Simple checker for whether a 'GenericResult' is OK.
210 93be1ced Iustin Pop
isOk :: GenericResult a b -> Bool
211 0c37d1e4 Iustin Pop
isOk (Ok _) = True
212 93be1ced Iustin Pop
isOk _      = False
213 0c37d1e4 Iustin Pop
214 93be1ced Iustin Pop
-- | Simple checker for whether a 'GenericResult' is a failure.
215 93be1ced Iustin Pop
isBad :: GenericResult a b -> Bool
216 0c37d1e4 Iustin Pop
isBad = not . isOk
217 0c37d1e4 Iustin Pop
218 9491766c Hrvoje Ribicic
-- | Simple filter returning only OK values of GenericResult
219 9491766c Hrvoje Ribicic
justOk :: [GenericResult a b] -> [b]
220 ea128e20 Klaus Aehlig
justOk = mapMaybe (genericResult (const Nothing) Just)
221 ea128e20 Klaus Aehlig
222 ea128e20 Klaus Aehlig
-- | Simple filter returning only Bad values of GenericResult
223 ea128e20 Klaus Aehlig
justBad :: [GenericResult a b] -> [a]
224 ea128e20 Klaus Aehlig
justBad = mapMaybe (genericResult Just (const Nothing))
225 9491766c Hrvoje Ribicic
226 98508e7f Dato Simó
-- | Converter from Either to 'GenericResult'.
227 93be1ced Iustin Pop
eitherToResult :: Either a b -> GenericResult a b
228 93be1ced Iustin Pop
eitherToResult (Left  s) = Bad s
229 93be1ced Iustin Pop
eitherToResult (Right v) = Ok  v
230 f3f76ccc Iustin Pop
231 87f15934 Petr Pudlak
-- | Annotate an error with an ownership information, lifting it to a
232 87f15934 Petr Pudlak
-- 'MonadError'. Since 'Result' is an instance of 'MonadError' itself,
233 87f15934 Petr Pudlak
-- it's a generalization of type @String -> Result a -> Result a@.
234 87f15934 Petr Pudlak
-- See also 'toErrorStr'.
235 87f15934 Petr Pudlak
annotateResult :: (MonadError e m, Error e) => String -> Result a -> m a
236 87f15934 Petr Pudlak
annotateResult owner = toErrorStr . annotateError owner
237 1091021c Iustin Pop
238 565821d1 Petr Pudlak
-- | Annotate an error with an ownership information inside a 'MonadError'.
239 565821d1 Petr Pudlak
-- See also 'annotateResult'.
240 565821d1 Petr Pudlak
annotateError :: (MonadError e m, Error e, Monoid e) => String -> m a -> m a
241 565821d1 Petr Pudlak
annotateError owner =
242 565821d1 Petr Pudlak
  flip catchError (throwError . mappend (strMsg $ owner ++ ": "))
243 565821d1 Petr Pudlak
{-# INLINE annotateError #-}
244 565821d1 Petr Pudlak
245 565821d1 Petr Pudlak
-- | Throws a 'String' message as an error in a 'MonadError'.
246 565821d1 Petr Pudlak
-- This is a generalization of 'Bad'.
247 565821d1 Petr Pudlak
-- It's similar to 'fail', but works within a 'MonadError', avoiding the
248 565821d1 Petr Pudlak
-- unsafe nature of 'fail'.
249 565821d1 Petr Pudlak
failError :: (MonadError e m, Error e) => String -> m a
250 565821d1 Petr Pudlak
failError = throwError . strMsg
251 565821d1 Petr Pudlak
252 565821d1 Petr Pudlak
-- | A synonym for @flip@ 'catchErrorT'.
253 565821d1 Petr Pudlak
handleErrorT :: (Monad m, Error e)
254 565821d1 Petr Pudlak
             => (e' -> ResultT e m a) -> ResultT e' m a -> ResultT e m a
255 565821d1 Petr Pudlak
handleErrorT handler = elimResultT handler return
256 565821d1 Petr Pudlak
{-# INLINE handleErrorT #-}
257 565821d1 Petr Pudlak
258 565821d1 Petr Pudlak
-- | Catches an error in a @ResultT@ value. This is similar to 'catchError',
259 565821d1 Petr Pudlak
-- but in addition allows to change the error type.
260 565821d1 Petr Pudlak
catchErrorT :: (Monad m, Error e)
261 565821d1 Petr Pudlak
            => ResultT e' m a -> (e' -> ResultT e m a) -> ResultT e m a
262 565821d1 Petr Pudlak
catchErrorT = flip handleErrorT
263 565821d1 Petr Pudlak
{-# INLINE catchErrorT #-}
264 565821d1 Petr Pudlak
265 b1c772fd Klaus Aehlig
-- | Iterate while Ok.
266 b1c772fd Klaus Aehlig
iterateOk :: (a -> GenericResult b a) -> a -> [a]
267 b1c772fd Klaus Aehlig
iterateOk f a = genericResult (const []) ((:) a . iterateOk f) (f a)
268 b1c772fd Klaus Aehlig
269 2fc5653f Iustin Pop
-- * Misc functionality
270 2fc5653f Iustin Pop
271 2fc5653f Iustin Pop
-- | Return the first result with a True condition, or the default otherwise.
272 2fc5653f Iustin Pop
select :: a            -- ^ default result
273 2fc5653f Iustin Pop
       -> [(Bool, a)]  -- ^ list of \"condition, result\"
274 2fc5653f Iustin Pop
       -> a            -- ^ first result which has a True condition, or default
275 2fc5653f Iustin Pop
select def = maybe def snd . find fst
276 2fc5653f Iustin Pop
277 2fc5653f Iustin Pop
-- * Lookup of partial names functionality
278 2fc5653f Iustin Pop
279 2fc5653f Iustin Pop
-- | The priority of a match in a lookup result.
280 2fc5653f Iustin Pop
data MatchPriority = ExactMatch
281 2fc5653f Iustin Pop
                   | MultipleMatch
282 2fc5653f Iustin Pop
                   | PartialMatch
283 2fc5653f Iustin Pop
                   | FailMatch
284 139c0683 Iustin Pop
                   deriving (Show, Enum, Eq, Ord)
285 2fc5653f Iustin Pop
286 2fc5653f Iustin Pop
-- | The result of a name lookup in a list.
287 2fc5653f Iustin Pop
data LookupResult = LookupResult
288 2fc5653f Iustin Pop
  { lrMatchPriority :: MatchPriority -- ^ The result type
289 2fc5653f Iustin Pop
  -- | Matching value (for ExactMatch, PartialMatch), Lookup string otherwise
290 2fc5653f Iustin Pop
  , lrContent :: String
291 139c0683 Iustin Pop
  } deriving (Show)
292 2fc5653f Iustin Pop
293 2fc5653f Iustin Pop
-- | Lookup results have an absolute preference ordering.
294 2fc5653f Iustin Pop
instance Eq LookupResult where
295 2fc5653f Iustin Pop
  (==) = (==) `on` lrMatchPriority
296 2fc5653f Iustin Pop
297 2fc5653f Iustin Pop
instance Ord LookupResult where
298 2fc5653f Iustin Pop
  compare = compare `on` lrMatchPriority
299 2fc5653f Iustin Pop
300 2fc5653f Iustin Pop
-- | Check for prefix matches in names.
301 2fc5653f Iustin Pop
-- Implemented in Ganeti core utils.text.MatchNameComponent
302 2fc5653f Iustin Pop
-- as the regexp r"^%s(\..*)?$" % re.escape(key)
303 2fc5653f Iustin Pop
prefixMatch :: String  -- ^ Lookup
304 2fc5653f Iustin Pop
            -> String  -- ^ Full name
305 2fc5653f Iustin Pop
            -> Bool    -- ^ Whether there is a prefix match
306 2fc5653f Iustin Pop
prefixMatch = isPrefixOf . (++ ".")
307 2fc5653f Iustin Pop
308 2fc5653f Iustin Pop
-- | Is the lookup priority a "good" one?
309 2fc5653f Iustin Pop
goodMatchPriority :: MatchPriority -> Bool
310 2fc5653f Iustin Pop
goodMatchPriority ExactMatch = True
311 2fc5653f Iustin Pop
goodMatchPriority PartialMatch = True
312 2fc5653f Iustin Pop
goodMatchPriority _ = False
313 2fc5653f Iustin Pop
314 2fc5653f Iustin Pop
-- | Is the lookup result an actual match?
315 2fc5653f Iustin Pop
goodLookupResult :: LookupResult -> Bool
316 2fc5653f Iustin Pop
goodLookupResult = goodMatchPriority . lrMatchPriority
317 2fc5653f Iustin Pop
318 2fc5653f Iustin Pop
-- | Compares a canonical name and a lookup string.
319 2fc5653f Iustin Pop
compareNameComponent :: String        -- ^ Canonical (target) name
320 2fc5653f Iustin Pop
                     -> String        -- ^ Partial (lookup) name
321 2fc5653f Iustin Pop
                     -> LookupResult  -- ^ Result of the lookup
322 2fc5653f Iustin Pop
compareNameComponent cnl lkp =
323 2fc5653f Iustin Pop
  select (LookupResult FailMatch lkp)
324 2fc5653f Iustin Pop
  [ (cnl == lkp          , LookupResult ExactMatch cnl)
325 2fc5653f Iustin Pop
  , (prefixMatch lkp cnl , LookupResult PartialMatch cnl)
326 2fc5653f Iustin Pop
  ]
327 2fc5653f Iustin Pop
328 2fc5653f Iustin Pop
-- | Lookup a string and choose the best result.
329 2fc5653f Iustin Pop
chooseLookupResult :: String       -- ^ Lookup key
330 2fc5653f Iustin Pop
                   -> String       -- ^ String to compare to the lookup key
331 2fc5653f Iustin Pop
                   -> LookupResult -- ^ Previous result
332 2fc5653f Iustin Pop
                   -> LookupResult -- ^ New result
333 2fc5653f Iustin Pop
chooseLookupResult lkp cstr old =
334 2fc5653f Iustin Pop
  -- default: use class order to pick the minimum result
335 2fc5653f Iustin Pop
  select (min new old)
336 2fc5653f Iustin Pop
  -- special cases:
337 2fc5653f Iustin Pop
  -- short circuit if the new result is an exact match
338 2fc5653f Iustin Pop
  [ (lrMatchPriority new == ExactMatch, new)
339 2fc5653f Iustin Pop
  -- if both are partial matches generate a multiple match
340 2fc5653f Iustin Pop
  , (partial2, LookupResult MultipleMatch lkp)
341 2fc5653f Iustin Pop
  ] where new = compareNameComponent cstr lkp
342 2fc5653f Iustin Pop
          partial2 = all ((PartialMatch==) . lrMatchPriority) [old, new]
343 2fc5653f Iustin Pop
344 2fc5653f Iustin Pop
-- | Find the canonical name for a lookup string in a list of names.
345 2fc5653f Iustin Pop
lookupName :: [String]      -- ^ List of keys
346 2fc5653f Iustin Pop
           -> String        -- ^ Lookup string
347 2fc5653f Iustin Pop
           -> LookupResult  -- ^ Result of the lookup
348 2fc5653f Iustin Pop
lookupName l s = foldr (chooseLookupResult s)
349 2fc5653f Iustin Pop
                       (LookupResult FailMatch s) l
350 4651c69f Jose A. Lopes
351 4651c69f Jose A. Lopes
-- | Wrapper for a Haskell 'Set'
352 4651c69f Jose A. Lopes
--
353 4651c69f Jose A. Lopes
-- This type wraps a 'Set' and it is used in the Haskell to Python
354 4651c69f Jose A. Lopes
-- opcode generation to transform a Haskell 'Set' into a Python 'list'
355 4651c69f Jose A. Lopes
-- without duplicate elements.
356 4651c69f Jose A. Lopes
newtype ListSet a = ListSet { unListSet :: Set a }
357 4651c69f Jose A. Lopes
  deriving (Eq, Show)
358 4651c69f Jose A. Lopes
359 4651c69f Jose A. Lopes
instance (Ord a, JSON a) => JSON (ListSet a) where
360 4651c69f Jose A. Lopes
  showJSON = JSON.showJSON . unListSet
361 4651c69f Jose A. Lopes
  readJSON = liftM ListSet . JSON.readJSON
362 4651c69f Jose A. Lopes
363 4651c69f Jose A. Lopes
emptyListSet :: ListSet a
364 4651c69f Jose A. Lopes
emptyListSet = ListSet Set.empty